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  
26  import org.apache.fulcrum.yaafi.framework.util.ReadWriteLock;
27  import org.apache.fulcrum.yaafi.framework.util.Validate;
28  
29  /**
30   * The InvocationHandler invoked when a service call is routed through
31   * the dynamic proxy.
32   *
33   * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl </a>
34   */
35  
36  public class AvalonInterceptorInvocationHandler implements InvocationHandler
37  {
38      /** the name of the service */
39      private String serviceName;
40  
41      /** the shorthand of the service */
42      private String serviceShorthand;
43  
44      /** the real service implementation */
45      private Object serviceDelegate;
46  
47      /** the list of interceptors to be invoked */
48      private AvalonInterceptorService [] serviceInterceptorList;
49  
50      /** read/write lock to snychronize access to services */
51      private ReadWriteLock readWriteLock;
52  
53      /** counts the current transactions */
54      private static volatile long transactionCounter = 0L;
55  
56      /** the current transaction id */
57      private Long transactionId;
58  
59      /**
60       * Constructor.
61       *
62       * @param serviceName the name of the service
63       * @param serviceShorthand the shorthand of the service being intercepted
64       * @param serviceDelegate the real service implementation
65       * @param serviceInterceptorList the list of interceptors to be invoked
66       * @param readWriteLock the YAAFI kernel lock
67       */
68      public AvalonInterceptorInvocationHandler(
69          String serviceName,
70          String serviceShorthand,
71          Object serviceDelegate,
72          AvalonInterceptorService [] serviceInterceptorList,
73          ReadWriteLock readWriteLock )
74      {
75          Validate.notEmpty(serviceName,"serviceName");
76          Validate.notEmpty(serviceShorthand,"serviceShorthand");
77          Validate.notNull(serviceDelegate,"serviceDelegate");
78          Validate.notNull(serviceInterceptorList,"serviceInterceptorList");
79          Validate.notNull(readWriteLock,"readWriteLock");
80  
81          this.serviceName = serviceName;
82          this.serviceShorthand = serviceShorthand;
83          this.serviceDelegate = serviceDelegate;
84          this.serviceInterceptorList = serviceInterceptorList;
85          this.readWriteLock = readWriteLock;
86      }
87  
88      /**
89       * @return Returns the delegate.
90       */
91      public Object getServiceDelegate()
92      {
93          return this.serviceDelegate;
94      }
95  
96      /**
97       * @return Returns the serviceInterceptorList.
98       */
99      public AvalonInterceptorService [] getServiceInterceptorList()
100     {
101         return serviceInterceptorList;
102     }
103 
104     /**
105      * @return Returns the serviceName.
106      */
107     public String getServiceName()
108     {
109         return serviceName;
110     }
111 
112     /**
113      * @return Returns the serviceShorthand.
114      */
115     public String getServiceShorthand()
116     {
117         return serviceShorthand;
118     }
119 
120     /**
121      * @return Returns the transaction id
122      */
123     public Long getTransactionId()
124     {
125         return transactionId;
126     }
127 
128     /**
129      * @see java.lang.Object#toString()
130      */
131     public String toString()
132     {
133         return super.toString();
134     }
135 
136     /**
137      * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
138      */
139     public Object invoke(Object proxy, Method method, Object [] args)
140         throws Throwable
141     {
142         Object result = null;
143         Object lock = null;
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             lock = this.getReadWriteLock().getReadLock(this.serviceName);
163             this.onEntry(context);
164             result = method.invoke( this.getServiceDelegate(), args );
165             this.onExit(context,result);
166             return result;
167         }
168         catch (InvocationTargetException e)
169         {
170             this.onError(context,e.getTargetException());
171             throw e.getTargetException();
172         }
173         catch (Throwable t)
174         {
175             throw t;
176         }
177         finally
178         {
179             // return the read lock
180 
181             this.getReadWriteLock().releaseLock(lock,this.serviceName);
182 
183             // decrement the service invocation depth
184 
185             context.decrementInvocationDepth();
186 
187             // reset the transaction id if we have created it before
188 
189             if( hasCreatedTransaction )
190             {
191                 context.clearTransactionId();
192             }
193         }
194     }
195 
196     /**
197      * Invoke the onEntry method on all service interceptors.
198      *
199      * @param context the current interceptor context
200      */
201     private void onEntry( AvalonInterceptorContext context )
202     {
203         for( int i=0; i<this.getServiceInterceptorList().length; i++ )
204         {
205             this.getServiceInterceptorList()[i].onEntry(context);
206         }
207     }
208 
209     /**
210      * Invoke the onExit method on all service interceptors.
211      *
212      * @param context the current interceptor context
213      * @param result the result
214      */
215     private void onExit( AvalonInterceptorContext context, Object result )
216     {
217         for( int i=this.getServiceInterceptorList().length-1; i>=0; i-- )
218         {
219             this.getServiceInterceptorList()[i].onExit(context,result);
220         }
221     }
222 
223     /**
224      * Invoke the onError method on all service interceptors.
225      *
226      * @param context the current interceptor context
227      * @param t the resulting exception
228      */
229     private void onError( AvalonInterceptorContext context, Throwable t )
230     {
231         for( int i=this.getServiceInterceptorList().length-1; i>=0; i-- )
232         {
233             this.getServiceInterceptorList()[i].onError(context,t);
234         }
235     }
236 
237     /**
238      * @return Returns the readWriteLock.
239      */
240     private final ReadWriteLock getReadWriteLock()
241     {
242         return readWriteLock;
243     }
244 
245     /**
246      * Creates a transaction id using the thread local storage
247      * @param context current interceptor context
248      * @return was a new transaction started
249      */
250     private boolean createTransactionId(AvalonInterceptorContext context)
251     {
252         Long currentTransactionId = null;
253 
254         if( context.hasTransactionId() == false )
255         {
256             // create a new transaction id
257 
258             currentTransactionId = new Long(
259                 ++AvalonInterceptorInvocationHandler.transactionCounter
260                 );
261 
262             // store it in the TLS
263 
264             context.setTransactionId(currentTransactionId);
265 
266             return true;
267         }
268 
269         return false;
270     }
271 }