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