KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > geronimo > spring > SpringGBean


1 /**
2  *
3  * Copyright 2003-2004 The Apache Software Foundation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.geronimo.spring;
19
20 import java.lang.reflect.Method JavaDoc;
21 import java.lang.reflect.Proxy JavaDoc;
22 import java.net.URI JavaDoc;
23 import java.net.URL JavaDoc;
24 import java.net.URLClassLoader JavaDoc;
25 import java.util.HashSet JavaDoc;
26 import java.util.Hashtable JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.Set JavaDoc;
29 import javax.management.MalformedObjectNameException JavaDoc;
30 import javax.management.ObjectName JavaDoc;
31
32 import net.sf.cglib.proxy.InterfaceMaker;
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.geronimo.gbean.GBeanData;
36 import org.apache.geronimo.gbean.GBeanInfo;
37 import org.apache.geronimo.gbean.GBeanInfoBuilder;
38 import org.apache.geronimo.gbean.GBeanLifecycle;
39 import org.apache.geronimo.kernel.Kernel;
40 import org.springframework.beans.BeansException;
41 import org.springframework.beans.factory.config.BeanPostProcessor;
42 import org.springframework.beans.factory.support.BeanDefinitionValidationException;
43 import org.springframework.beans.factory.support.DefaultListableBeanFactory;
44 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
45 import org.springframework.core.io.ClassPathResource;
46
47 /**
48  * A GBean for creating graphs of Spring POJOs and auto-deploying them inside Geronimo as GBeans
49  *
50  * @version $Rev: 154696 $
51  */

52 public class SpringGBean
53   implements GBeanLifecycle
54 {
55   protected static final Log _log = LogFactory.getLog(SpringGBean.class);
56
57   // injected into ctor
58
protected final Kernel _kernel;
59   protected final String JavaDoc _objectName;
60   protected final ClassLoader JavaDoc _classLoader;
61   protected final URI JavaDoc[] _classPath;
62   protected final URL JavaDoc _configurationBaseUrl;
63   protected final URI JavaDoc _configPath;
64
65   protected ClassLoader JavaDoc _appClassLoader;
66   protected ObjectName JavaDoc _jmxName;
67   protected DefaultListableBeanFactory _factory;
68
69   //----------------------------------------
70
public static final GBeanInfo GBEAN_INFO;
71
72   static
73   {
74     GBeanInfoBuilder infoBuilder = new GBeanInfoBuilder("Spring Application Context", SpringGBean.class);
75
76     infoBuilder.addAttribute("kernel" , Kernel.class , false);
77     infoBuilder.addAttribute("objectName" , String JavaDoc.class , false);
78     infoBuilder.addAttribute("classLoader" , ClassLoader JavaDoc.class , false);
79     infoBuilder.addAttribute("classPath" , URI JavaDoc[].class , true);
80     infoBuilder.addAttribute("configurationBaseUrl" , URL JavaDoc.class , true);
81     infoBuilder.addAttribute("configPath" , URI JavaDoc.class , true);
82
83     infoBuilder.setConstructor(new String JavaDoc[]{
84                  "kernel",
85                  "objectName",
86                  "classLoader",
87                  "classPath",
88                  "configurationBaseUrl",
89                  "configPath"
90                    });
91
92     GBEAN_INFO = infoBuilder.getBeanInfo();
93   }
94
95   public static GBeanInfo getGBeanInfo() {return GBEAN_INFO;}
96
97   //----------------------------------------
98

99
100   public
101     SpringGBean(Kernel kernel, String JavaDoc objectName, ClassLoader JavaDoc classLoader, URI JavaDoc[] classPath, URL JavaDoc configurationBaseUrl, URI JavaDoc configPath)
102   {
103     _kernel =kernel;
104     _objectName =objectName;
105     _configPath =configPath;
106     _classLoader =classLoader;
107     _classPath =classPath;
108     _configurationBaseUrl =configurationBaseUrl;
109   }
110
111   //----------------------------------------
112
// GBeanLifecycle
113
//----------------------------------------
114

115   class GeronimoBeanFactory
116     extends DefaultListableBeanFactory
117   {
118     GeronimoBeanFactory(){super();}
119   }
120
121   public void
122     doStart()
123     throws Exception JavaDoc
124   {
125     _jmxName=new ObjectName JavaDoc(_objectName);
126
127     // set up classloader
128
URI JavaDoc root = URI.create(_configurationBaseUrl.toString());
129
130     URL JavaDoc[] urls=new URL JavaDoc[_classPath.length];
131
132     for (int i=0; i<_classPath.length; i++)
133     {
134       URL JavaDoc url=root.resolve(_classPath[i]).toURL();
135       _log.info("_classPath["+i+"]: "+url);
136       urls[i]=url;
137     }
138
139     _appClassLoader=new URLClassLoader JavaDoc(urls, _classLoader);
140
141     // delegate work to Spring framework...
142
_factory=new GeronimoBeanFactory();
143     XmlBeanDefinitionReader xbdr=new XmlBeanDefinitionReader(_factory);
144     xbdr.setBeanClassLoader(_appClassLoader);
145     xbdr.loadBeanDefinitions(new ClassPathResource(_configPath.toString(), _appClassLoader));
146
147     // install aspects around Spring Bean initialisation...
148
_factory.addBeanPostProcessor(new BeanPostProcessor() {
149         public Object JavaDoc postProcessBeforeInitialization(Object JavaDoc bean, String JavaDoc name) throws BeansException {
150           return beforeInitialization(bean, name);
151         }
152
153     public Object JavaDoc postProcessAfterInitialization(Object JavaDoc bean, String JavaDoc name) throws BeansException {
154       return afterInitialization(bean, name);
155     }
156       });
157
158     // force lazy construction of every bean described... - is there a better way - ROB ?
159
String JavaDoc[] ids=_factory.getBeanDefinitionNames();
160     int n=ids.length;
161     for (int i=n; i>0; i--)
162       _factory.getBean(ids[i-1]);
163
164     _log.info("Deployed: "+n+" POJO"+(n==1?"":"s"));
165   }
166
167
168   public void
169     doStop()
170     throws Exception JavaDoc
171   {
172     tidyUp();
173   }
174
175   public void
176     doFail()
177   {
178     try
179     {
180       tidyUp();
181     }
182     catch (Exception JavaDoc e)
183     {
184       _log.warn("problem decommissioning Spring module: "+_jmxName, e);
185     }
186   }
187
188   protected void
189     tidyUp()
190     throws Exception JavaDoc
191   {
192     // can we put this as a spring aspect around each POJO ? would be better...
193
String JavaDoc pattern=_jmxName.getDomain()+":J2EEApplication="+_jmxName.getKeyProperty("J2EEApplication")+",J2EEServer="+_jmxName.getKeyProperty("J2EEServer")+",SpringModule="+_jmxName.getKeyProperty("name")+",j2eeType=SpringBean,*";
194     ObjectName JavaDoc on=new ObjectName JavaDoc(pattern);
195
196     // can't we do it like this - much less typo-prone...
197
// Hashtable props =new Hashtable(_jmxName.getKeyPropertyList());
198
// props.put("SpringModule" , props.get("name"));
199
// props.put("j2eeType" , "SpringBean");
200
// props.remove("name");
201
// props.put("*", "");
202
// ObjectName on=new ObjectName(_jmxName.getDomain(), props);
203

204     Set JavaDoc peers=_kernel.listGBeans(on);
205     for (Iterator JavaDoc i=peers.iterator(); i.hasNext();)
206     {
207       ObjectName JavaDoc tmp=(ObjectName JavaDoc)i.next();
208       try
209       {
210     _log.info("stopping: "+tmp);
211     _kernel.stopGBean(tmp);
212     _log.info("unloading: "+tmp);
213     _kernel.unloadGBean(tmp);
214       }
215       catch (Exception JavaDoc e)
216       {
217     _log.warn("problem decommissioning POJO peer GBean: "+tmp, e);
218       }
219     }
220
221     _factory.destroySingletons();
222   }
223
224   //----------------------------------------
225
// aspects around Spring Bean creation...
226
//----------------------------------------
227

228   /**
229    * Hook to perform action before Spring Bean initialisation.
230    */

231   protected Object JavaDoc
232     beforeInitialization(Object JavaDoc bean, String JavaDoc name)
233   {
234     return bean;
235   }
236
237   /**
238    * Hook to perform action after Spring Bean initialisation.
239    */

240   protected Object JavaDoc
241     afterInitialization(Object JavaDoc bean, String JavaDoc name)
242     throws BeansException
243   {
244     // create a GBean peer...
245
try
246     {
247       GBeanData gd=createPOJOGBeanData(bean, name);
248       if (gd==null)
249         _log.warn("No GBean available for name: " + name + " bean: " + bean);
250       else
251       {
252     _log.info("proxying: "+bean);
253     _log.info("loading: "+gd.getName());
254     // _kernel.loadGBeanProxy(bean, gd, _appClassLoader);
255
_kernel.loadGBean(gd, _appClassLoader);
256     _log.info("starting: "+gd.getName());
257     _kernel.startGBean(gd.getName());
258       }
259     }
260     catch (Exception JavaDoc e)
261     {
262       throw new BeanDefinitionValidationException("Could not load the GBean for name: " + name + " bean: " + bean + ". Reason: " + e, e);
263     }
264     return bean;
265   }
266
267   //----------------------------------------
268
// utils...
269
//----------------------------------------
270

271   /**
272    * Factory method to create an ObjectName for the Spring bean
273    *
274    * @param name the name of the bean in the Spring config file
275    * @return the ObjectName to use for the given Spring bean name
276    */

277   protected ObjectName JavaDoc
278     createObjectName(String JavaDoc name)
279     throws MalformedObjectNameException JavaDoc
280   {
281     Hashtable JavaDoc props =new Hashtable JavaDoc(_jmxName.getKeyPropertyList());
282     props.put("SpringModule" , props.get("name"));
283     props.put("j2eeType" , "SpringBean");
284     props.put("name" , name);
285     return new ObjectName JavaDoc(_jmxName.getDomain(), props);
286   }
287
288   protected int _count=0; // we should use a Spring generated unique name here - TODO
289

290   public static class InvocationHandler
291     implements java.lang.reflect.InvocationHandler JavaDoc, java.io.Serializable JavaDoc
292   {
293     protected Object JavaDoc _pojo;
294
295     public
296       InvocationHandler(Object JavaDoc pojo) {_pojo=pojo;}
297
298     public Object JavaDoc
299       invoke(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args)
300       throws Throwable JavaDoc
301     {
302       return _pojo.getClass().getMethod(method.getName(), method.getParameterTypes()).invoke(_pojo, args);
303     }
304   }
305
306   protected synchronized GBeanData
307     createPOJOGBeanData(Object JavaDoc bean, String JavaDoc name)
308       throws MalformedObjectNameException JavaDoc
309   {
310     Class JavaDoc c=createProxyClass(bean);
311     GBeanInfoBuilder gbif = new GBeanInfoBuilder(c, "POJO["+(_count++)+"]");
312
313     gbif.addAttribute("invocationHandler", java.lang.reflect.InvocationHandler JavaDoc.class, true);
314     gbif.setConstructor(new String JavaDoc[]{"invocationHandler"});
315     // describe the rest of the POJOs public API
316
Set JavaDoc pm=new HashSet JavaDoc();
317     Method JavaDoc[] methods=c.getMethods();
318     for (int i=0;i<methods.length;i++)
319     {
320       Method JavaDoc m=methods[i];
321       String JavaDoc n=m.getName();
322       Class JavaDoc[] pt=m.getParameterTypes();
323       Class JavaDoc rt=m.getReturnType();
324
325       // ugly way of collecting Bean property names - maybe we can get this from Spring ?
326
if ((n.startsWith("get") && pt.length==0) || (n.startsWith("set") && pt.length==1 && rt==Void.TYPE))
327     pm.add(n.substring(3,4).toLowerCase()+n.substring(4));
328     }
329
330     // pm.remove("class"); // do we want this available ?
331
gbif.addInterface(c, (String JavaDoc[])pm.toArray(new String JavaDoc[pm.size()]));
332     //gbif.addInterface(c);
333
GBeanData gbd=new GBeanData(createObjectName(name), gbif.getBeanInfo());
334     // ensure the injection of the InvocationHandler into the newly instantiated Proxy
335
gbd.setAttribute("invocationHandler" , new InvocationHandler(bean));
336
337     return gbd;
338   }
339
340   // We have to create a proxy here because the kernel only accepts
341
// classes from which to instantiate GBeanInstance targets, not
342
// instances, which is what we get from Spring. So we create a proxy
343
// that can be instantiated and injected with an InvocationHandler
344
// which will delegate all calls onto the corresponding method on
345
// the bean that we wanted to pass in in the first place. Complex
346
// syntactic sugar - eh...!
347
protected Class JavaDoc
348     createProxyClass(Object JavaDoc pojo)
349   {
350     InterfaceMaker im=new InterfaceMaker();
351     im.add(pojo.getClass()); // add all class' public methods...
352

353     // if POJO does not implement GBeanLifeCycle should we implement
354
// it and dump/reroute it, to be safe ?
355

356     Class JavaDoc c=im.create();
357     return Proxy.getProxyClass(c.getClassLoader(), new Class JavaDoc[] {c});
358   }
359 }
360
Popular Tags