View Javadoc
1 /* 2 * Service.java 3 * Created on August 27, 2003 4 * 5 * The Blues Framework - A lightweight application framework 6 * Copyright (C) 2003 Lonnie Pryor 7 * http://blues.lonniepryor.com 8 * 9 * This library is free software; you can redistribute it and/or modify it under the 10 * terms of the GNU Lesser General Public License as published by the Free Software 11 * Foundation; either version 2.1 of the License, or (at your option) any later 12 * version. 13 * 14 * This library is distributed in the hope that it will be useful, but WITHOUT ANY 15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 16 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public License along 19 * with this library; if not, write to: 20 * 21 * The Free Software Foundation, Inc. 22 * 59 Temple Place, Suite 330 23 * Boston, MA 02111-1307 USA 24 * 25 */ 26 package com.lonniepryor.blues.sys; 27 28 import java.lang.reflect.Member; 29 import java.lang.reflect.Method; 30 31 import java.util.Arrays; 32 import java.util.HashMap; 33 import java.util.HashSet; 34 import java.util.Iterator; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.Set; 38 39 import com.lonniepryor.blues.aop.Imposter; 40 import com.lonniepryor.blues.aop.JoinPoint; 41 import com.lonniepryor.blues.cfg.ServiceCfg; 42 import com.lonniepryor.blues.util.Methods; 43 import com.lonniepryor.blues.util.Types; 44 45 import net.sf.cglib.Enhancer; 46 import net.sf.cglib.KeyFactory; 47 import net.sf.cglib.MethodFilter; 48 import net.sf.cglib.MethodInterceptor; 49 import net.sf.cglib.MethodProxy; 50 51 /*** 52 * Models the abstract view of a Blues advice. 53 * 54 * @author Lonnie Pryor 55 * @version $Revision: 1.1 $ 56 */ 57 public final class Service extends Component { 58 /*** A specification satisfied by types that implement Imposter. */ 59 private static final Types imposterTypes = Types.assignableTo(Imposter.class); 60 /*** A specification satisfied by public methods that can be overridden. */ 61 private static final Methods joinPointMethods = Methods.declaredPublic() 62 .and( 63 Methods.declaredStatic().not()).and(Methods.declaredFinal().not()); 64 /*** Key factory for join points (helps reduce ambiquity). */ 65 private static final JoinPointKeyFactory keyFactory = (JoinPointKeyFactory)KeyFactory 66 .create(JoinPointKeyFactory.class, null); 67 /*** The context this service is declared in. */ 68 private final Context context; 69 /*** The configuration information for this service. */ 70 private final ServiceCfg configuration; 71 /*** A mapping of interceptors to lists of advice they execute. */ 72 private final Map interceptorsToAdvice = new HashMap(); 73 /*** The service instance. */ 74 private final Object instance; 75 76 /*** 77 * Creates a new Service object. 78 * 79 * @param context The context this service is declared in. 80 * @param configuration The configuration information for this service. 81 */ 82 Service (Context context, ServiceCfg configuration) { 83 this.context = context; 84 this.configuration = configuration; 85 if (!ServiceCfg.validServiceTypes.isSatisfiedBy(configuration.getServiceType())) 86 throw new SystemException( 87 "Invalid service type: " + configuration.getServiceType().getName()); 88 final Set joinPointKeys = new HashSet(); 89 final Set interceptedMethods = new HashSet(); 90 indexInterceptors( 91 configuration.getServiceType(), joinPointKeys, interceptedMethods); 92 Class[] introductions = configuration.getIntroducedInterfaces(); 93 if (introductions.length > 0) { 94 if (!imposterTypes.isSatisfiedBy(configuration.getServiceType())) 95 throw new IllegalArgumentException( 96 "Cannot introduce interfaces to " 97 + configuration.getServiceType().getName() + ", it does not implement " 98 + Imposter.class.getName()); 99 if (!Types.interfaces().isSatisifiedByAll(introductions)) 100 throw new SystemException( 101 "Cannot introduce non-interface types: " 102 + Arrays.asList(Types.interfaces().not().selectAll(introductions))); 103 for (int i = 0; i < introductions.length; ++i) 104 indexInterceptors(introductions[i], joinPointKeys, interceptedMethods); 105 } 106 try { 107 instance = Enhancer.enhance( 108 configuration.getServiceType(), configuration.getIntroducedInterfaces(), 109 new ServiceMethodInterceptor( 110 (Interceptor[])interceptorsToAdvice.keySet().toArray( 111 new Interceptor[interceptorsToAdvice.size()])), 112 context.getClassLoader(), null, 113 new MethodFilter() { 114 public boolean accept (Member member) { 115 return interceptedMethods.contains(member); 116 } 117 }); 118 } catch (Exception e) { 119 throw new SystemException( 120 "Error creating service instance: " + e.getMessage(), e); 121 } 122 } 123 124 /*** 125 * Indexes the interceptors on the specified class. 126 * 127 * @param onClass The class to index. 128 * @param keySet The set of join point keys already intercepted. 129 * @param methodSet The set of methods intercepted. 130 */ 131 private void indexInterceptors (Class onClass, Set keySet, Set methodSet) { 132 Method[] canidates = joinPointMethods.selectAll(onClass.getMethods()); 133 for (int i = 0; i < canidates.length; ++i) { 134 if ( 135 !keySet.add( 136 keyFactory.newInstance( 137 canidates[i].getName(), canidates[i].getParameterTypes()))) 138 continue; 139 Interceptor interceptor = new Interceptor(canidates[i]); 140 List advice = context.adviceFor(newJoinPoint(canidates[i])); 141 if (interceptor.isRequired() || !advice.isEmpty()) { 142 interceptorsToAdvice.put(interceptor, advice); 143 methodSet.add(canidates[i]); 144 } 145 } 146 } 147 148 /*** 149 * Creates a new instance of JoinPoint on this service for the supplied method. 150 * 151 * @param forMethod The joinable method. 152 * 153 * @return A new instance of JoinPoint on this service for the supplied method. 154 */ 155 private JoinPoint newJoinPoint (final Method forMethod) { 156 return new JoinPoint() { 157 public String getInstanceName () { 158 return getName(); 159 } 160 161 public Method getTargetMethod () { 162 return forMethod; 163 } 164 }; 165 } 166 167 /*** 168 * Returns the name of this service. 169 * 170 * @return The name of this service. 171 */ 172 String getName () { 173 return configuration.getName(); 174 } 175 176 /* (non-Javadoc) 177 * @see com.lonniepryor.blues.sys.Component#getComponentInstance() 178 */ 179 Object getComponentInstance () { 180 return instance; 181 } 182 183 /* (non-Javadoc) 184 * @see com.lonniepryor.blues.sys.Component#configureInstance() 185 */ 186 void configureInstance () { 187 try { 188 configuration.configure(instance, context); 189 } catch (RuntimeException re) { 190 throw new SystemException( 191 "Error configuring service '" + context.getContextPath().append(getName()) 192 + "': " + re.getMessage(), re); 193 } 194 } 195 196 /*** 197 * Activates all of the registered interceptors. 198 */ 199 void activateInterceptors () { 200 for (Iterator iter = interceptorsToAdvice.entrySet().iterator(); 201 iter.hasNext();) { 202 Map.Entry entry = (Map.Entry)iter.next(); 203 ((Interceptor)entry.getKey()).bindAdvice((List)entry.getValue()); 204 } 205 } 206 207 /*** 208 * Registers this service with the specified registry. 209 * 210 * @param registry The registry to register with. 211 */ 212 void registerWith (Registry registry) { 213 registry.registerService( 214 context.getContextPath().append(configuration.getName()).toString(), instance); 215 } 216 } 217 218 219 /*** 220 * Describes a factory for producing join point keys. 221 * 222 * @author Lonnie Pryor 223 * @version $Revision: 1.1 $ 224 */ 225 interface JoinPointKeyFactory { 226 /*** 227 * Creates a key for the supplied method name and parameter list. 228 * 229 * @param methodName The method name. 230 * @param parameterTypes The parameter list. 231 * 232 * @return A key for the supplied method name and parameter list. 233 */ 234 Object newInstance (String methodName, Class[] parameterTypes); 235 } 236 237 238 /*** 239 * CGLib method interceptor for service instances. 240 * 241 * @author Lonnie Pryor 242 * @version $Revision: 1.1 $ 243 */ 244 final class ServiceMethodInterceptor implements MethodInterceptor { 245 /*** The map of intercepted methods to interceptors. */ 246 private final Map interceptors; 247 248 /*** 249 * Creates a new ServiceMethodInterceptor object. 250 * 251 * @param interceptorArray All interceptors to register. 252 */ 253 ServiceMethodInterceptor (Interceptor[] interceptorArray) { 254 interceptors = new HashMap(interceptorArray.length); 255 for (int i = 0; i < interceptorArray.length; i++) 256 interceptors.put(interceptorArray[i].getMethod(), interceptorArray[i]); 257 } 258 259 /* (non-Javadoc) 260 * @see net.sf.cglib.MethodInterceptor#intercept(java.lang.Object, 261 * java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.MethodProxy) 262 */ 263 public Object intercept ( 264 Object target, Method method, Object[] parameters, MethodProxy proxy) 265 throws Throwable { 266 Interceptor interceptor = (Interceptor)interceptors.get(method); 267 if (interceptor == null) 268 throw new SystemException("Interceptor for " + method + " not configured."); 269 return interceptor.interceptInvocation(target, parameters, proxy); 270 } 271 }

This page was automatically generated by Maven