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