001 package org.apache.fulcrum.yaafi.framework.interceptor;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import java.lang.reflect.InvocationHandler;
023 import java.lang.reflect.InvocationTargetException;
024 import java.lang.reflect.Method;
025
026 import org.apache.fulcrum.yaafi.framework.util.Validate;
027 import org.apache.fulcrum.yaafi.framework.util.ToStringBuilder;
028
029 /**
030 * The InvocationHandler invoked when a service call is routed through
031 * the dynamic proxy.
032 *
033 * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl </a>
034 */
035
036 public class AvalonInterceptorInvocationHandler implements InvocationHandler
037 {
038 /** the name of the service */
039 private String serviceName;
040
041 /** the shorthand of the service */
042 private String serviceShorthand;
043
044 /** the real service implementation */
045 private Object serviceDelegate;
046
047 /** the list of interceptors to be invoked */
048 private AvalonInterceptorService [] serviceInterceptorList;
049
050 /** counts the current transactions */
051 private static volatile long transactionCounter = 0L;
052
053 /** the current transaction id */
054 private Long transactionId;
055
056 /**
057 * Constructor.
058 *
059 * @param serviceName the name of the service
060 * @param serviceShorthand the shorthand of the service being intercepted
061 * @param serviceDelegate the real service implementation
062 * @param serviceInterceptorList the list of interceptors to be invoked
063 */
064 public AvalonInterceptorInvocationHandler(
065 String serviceName,
066 String serviceShorthand,
067 Object serviceDelegate,
068 AvalonInterceptorService [] serviceInterceptorList )
069 {
070 Validate.notEmpty(serviceName,"serviceName");
071 Validate.notEmpty(serviceShorthand,"serviceShorthand");
072 Validate.notNull(serviceDelegate,"serviceDelegate");
073 Validate.notNull(serviceInterceptorList,"serviceInterceptorList");
074
075 this.serviceName = serviceName;
076 this.serviceShorthand = serviceShorthand;
077 this.serviceDelegate = serviceDelegate;
078 this.serviceInterceptorList = serviceInterceptorList;
079 }
080
081 /**
082 * @return Returns the delegate.
083 */
084 public Object getServiceDelegate()
085 {
086 return this.serviceDelegate;
087 }
088
089 /**
090 * @return Returns the serviceInterceptorList.
091 */
092 public AvalonInterceptorService [] getServiceInterceptorList()
093 {
094 return serviceInterceptorList;
095 }
096
097 /**
098 * @return Returns the serviceName.
099 */
100 public String getServiceName()
101 {
102 return serviceName;
103 }
104
105 /**
106 * @return Returns the serviceShorthand.
107 */
108 public String getServiceShorthand()
109 {
110 return serviceShorthand;
111 }
112
113 /**
114 * @return Returns the transaction id
115 */
116 public Long getTransactionId()
117 {
118 return transactionId;
119 }
120
121 /**
122 * @see java.lang.Object#toString()
123 */
124 public String toString()
125 {
126 ToStringBuilder toStringBuilder = new ToStringBuilder(this);
127
128 toStringBuilder.append("serviceShorthand",this.serviceShorthand);
129 toStringBuilder.append("serviceName",this.serviceName);
130 toStringBuilder.append("serviceDelegate",this.serviceDelegate);
131 toStringBuilder.append("transactionId",this.transactionId);
132
133 return toStringBuilder.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
144 // create the interceptor context for current method call
145
146 AvalonInterceptorContext context = new AvalonInterceptorContextImpl(
147 this.getServiceName(),
148 this.getServiceShorthand(),
149 this.getServiceDelegate(),
150 method,
151 args
152 );
153
154 // if no transaction id is currently define we create a new one
155
156 boolean hasCreatedTransaction = this.createTransactionId(context);
157
158 try
159 {
160 context.incrementInvocationDepth();
161 this.onEntry(context);
162 result = method.invoke( this.getServiceDelegate(), args );
163 this.onExit(context,result);
164 return result;
165 }
166 catch (InvocationTargetException e)
167 {
168 this.onError(context,e.getTargetException());
169 throw e.getTargetException();
170 }
171 finally
172 {
173 // decrement the service invocation depth
174
175 context.decrementInvocationDepth();
176
177 // reset the transaction id if we have created it before
178
179 if( hasCreatedTransaction )
180 {
181 context.clearTransactionId();
182 }
183 }
184 }
185
186 /**
187 * Invoke the onEntry method on all service interceptors.
188 *
189 * @param context the current interceptor context
190 */
191 private void onEntry( AvalonInterceptorContext context )
192 {
193 for( int i=0; i<this.getServiceInterceptorList().length; i++ )
194 {
195 this.getServiceInterceptorList()[i].onEntry(context);
196 }
197 }
198
199 /**
200 * Invoke the onExit method on all service interceptors.
201 *
202 * @param context the current interceptor context
203 * @param result the result
204 */
205 private void onExit( AvalonInterceptorContext context, Object result )
206 {
207 for( int i=this.getServiceInterceptorList().length-1; i>=0; i-- )
208 {
209 this.getServiceInterceptorList()[i].onExit(context,result);
210 }
211 }
212
213 /**
214 * Invoke the onError method on all service interceptors.
215 *
216 * @param context the current interceptor context
217 * @param t the resulting exception
218 */
219 private void onError( AvalonInterceptorContext context, Throwable t )
220 {
221 for( int i=this.getServiceInterceptorList().length-1; i>=0; i-- )
222 {
223 this.getServiceInterceptorList()[i].onError(context,t);
224 }
225 }
226
227 /**
228 * Creates a transaction id using the thread local storage
229 * @param context current interceptor context
230 * @return was a new transaction started
231 */
232 private boolean createTransactionId(AvalonInterceptorContext context)
233 {
234 Long currentTransactionId = null;
235
236 if( context.hasTransactionId() == false )
237 {
238 // create a new transaction id
239
240 currentTransactionId = new Long(
241 ++AvalonInterceptorInvocationHandler.transactionCounter
242 );
243
244 // store it in the TLS
245
246 context.setTransactionId(currentTransactionId);
247
248 return true;
249 }
250
251 return false;
252 }
253 }