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    }