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 }