View Javadoc
1   package org.apache.fulcrum.yaafi.framework.interceptor;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.lang.reflect.InvocationHandler;
23  import java.lang.reflect.InvocationTargetException;
24  import java.lang.reflect.Method;
25  import java.util.concurrent.atomic.AtomicLong;
26  
27  import org.apache.fulcrum.yaafi.framework.util.ToStringBuilder;
28  import org.apache.fulcrum.yaafi.framework.util.Validate;
29  
30  /**
31   * The InvocationHandler invoked when a service call is routed through
32   * the dynamic proxy.
33   *
34   * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl </a>
35   */
36  
37  public class AvalonInterceptorInvocationHandler implements InvocationHandler
38  {
39      /** the name of the service */
40      private String serviceName;
41  
42      /** the shorthand of the service */
43      private String serviceShorthand;
44  
45      /** the real service implementation */
46      private Object serviceDelegate;
47  
48      /** the list of interceptors to be invoked */
49      private AvalonInterceptorService [] serviceInterceptorList;
50  
51      /** counts the current transactions */
52      private static AtomicLong transactionCounter = new AtomicLong(0);
53  
54      /** the current transaction id */
55      private Long transactionId;
56  
57      /**
58       * Constructor.
59       *
60       * @param serviceName the name of the service
61       * @param serviceShorthand the shorthand of the service being intercepted
62       * @param serviceDelegate the real service implementation
63       * @param serviceInterceptorList the list of interceptors to be invoked
64       */
65      public AvalonInterceptorInvocationHandler(
66          String serviceName,
67          String serviceShorthand,
68          Object serviceDelegate,
69          AvalonInterceptorService [] serviceInterceptorList )
70      {
71          Validate.notEmpty(serviceName,"serviceName");
72          Validate.notEmpty(serviceShorthand,"serviceShorthand");
73          Validate.notNull(serviceDelegate,"serviceDelegate");
74          Validate.notNull(serviceInterceptorList,"serviceInterceptorList");
75  
76          this.serviceName = serviceName;
77          this.serviceShorthand = serviceShorthand;
78          this.serviceDelegate = serviceDelegate;
79          this.serviceInterceptorList = serviceInterceptorList;
80      }
81  
82      /**
83       * @return Returns the delegate.
84       */
85      public Object getServiceDelegate()
86      {
87          return this.serviceDelegate;
88      }
89  
90      /**
91       * @return Returns the serviceInterceptorList.
92       */
93      public AvalonInterceptorService [] getServiceInterceptorList()
94      {
95          return serviceInterceptorList;
96      }
97  
98      /**
99       * @return Returns the serviceName.
100      */
101     public String getServiceName()
102     {
103         return serviceName;
104     }
105 
106     /**
107      * @return Returns the serviceShorthand.
108      */
109     public String getServiceShorthand()
110     {
111         return serviceShorthand;
112     }
113 
114     /**
115      * @return Returns the transaction id
116      */
117     public Long getTransactionId()
118     {
119         return transactionId;
120     }
121 
122     /**
123      * @see java.lang.Object#toString()
124      */
125     public String toString()
126     {
127         ToStringBuilder toStringBuilder = new ToStringBuilder(this);
128 
129         toStringBuilder.append("serviceShorthand",this.serviceShorthand);
130         toStringBuilder.append("serviceName",this.serviceName);
131         toStringBuilder.append("serviceDelegate",this.serviceDelegate);
132         toStringBuilder.append("transactionId",this.transactionId);
133 
134         return toStringBuilder.toString();
135     }
136 
137     /**
138      * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
139      */
140     public Object invoke(Object proxy, Method method, Object [] args)
141         throws Throwable
142     {
143         Object result;
144 
145         // create the interceptor context for current method call
146 
147         AvalonInterceptorContext context = new AvalonInterceptorContextImpl(
148             this.getServiceName(),
149             this.getServiceShorthand(),
150             this.getServiceDelegate(),
151             method,
152             args
153             );
154 
155         // if no transaction id is currently define we create a new one
156 
157         boolean hasCreatedTransaction = this.createTransactionId(context);
158 
159         try
160         {
161             context.incrementInvocationDepth();
162             this.onEntry(context);
163             result = method.invoke( this.getServiceDelegate(), args );
164             this.onExit(context,result);
165             return result;
166         }
167         catch (InvocationTargetException e)
168         {
169             this.onError(context,e.getTargetException());
170             throw e.getTargetException();
171         }
172         catch (Throwable t)
173         {
174             this.onError(context,t);
175             throw t;
176         }
177         finally
178         {
179             // decrement the service invocation depth
180 
181             context.decrementInvocationDepth();
182 
183             // reset the transaction id if we have created it before
184 
185             if( hasCreatedTransaction )
186             {
187                 context.clearTransactionId();
188             }
189         }
190     }
191 
192     /**
193      * Invoke the onEntry method on all service interceptors.
194      *
195      * @param context the current interceptor context
196      */
197     private void onEntry( AvalonInterceptorContext context )
198     {
199         for( int i=0; i<this.getServiceInterceptorList().length; i++ )
200         {
201             AvalonInterceptorService avalonInterceptorService = this.getServiceInterceptorList()[i];
202             avalonInterceptorService.onEntry(context);
203         }
204     }
205 
206     /**
207      * Invoke the onExit method on all service interceptors.
208      *
209      * @param context the current interceptor context
210      * @param result the result
211      */
212     private void onExit( AvalonInterceptorContext context, Object result )
213     {
214         for( int i=this.getServiceInterceptorList().length-1; i>=0; i-- )
215         {
216             AvalonInterceptorService avalonInterceptorService = this.getServiceInterceptorList()[i];
217             avalonInterceptorService.onExit(context, result);
218         }
219     }
220 
221     /**
222      * Invoke the onError method on all service interceptors.
223      *
224      * @param context the current interceptor context
225      * @param t the resulting exception
226      */
227     private void onError( AvalonInterceptorContext context, Throwable t )
228     {
229         for( int i=this.getServiceInterceptorList().length-1; i>=0; i-- )
230         {
231             AvalonInterceptorService avalonInterceptorService = this.getServiceInterceptorList()[i];
232             avalonInterceptorService.onError(context, t);
233         }
234     }
235 
236     /**
237      * Creates a transaction id using the thread local storage
238      * @param context current interceptor context
239      * @return was a new transaction started
240      */
241     private boolean createTransactionId(AvalonInterceptorContext context)
242     {
243         Long currentTransactionId;
244 
245         if( !context.hasTransactionId() )
246         {
247             // create a new transaction id
248 
249             currentTransactionId = transactionCounter.incrementAndGet();
250             this.transactionId = currentTransactionId;
251 
252             // store it in the TLS
253 
254             context.setTransactionId(currentTransactionId);
255 
256             return true;
257         }
258 
259         return false;
260     }
261 }