KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > mx > util > MBeanInstaller


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.mx.util;
23
24 import java.beans.PropertyEditor JavaDoc;
25 import java.beans.PropertyEditorManager JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.util.Date JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31
32 import javax.management.InstanceNotFoundException JavaDoc;
33 import javax.management.MBeanException JavaDoc;
34 import javax.management.MBeanServer JavaDoc;
35 import javax.management.MalformedObjectNameException JavaDoc;
36 import javax.management.ObjectInstance JavaDoc;
37 import javax.management.ObjectName JavaDoc;
38 import javax.management.ReflectionException JavaDoc;
39
40 import org.jboss.logging.Logger;
41 import org.jboss.mx.loading.MBeanElement;
42 import org.jboss.mx.server.ObjectInputStreamWithClassLoader;
43 import org.jboss.mx.server.ServerConstants;
44 import org.jboss.util.UnreachableStatementException;
45
46 /**
47  * MBean installer utility<p>
48  *
49  * This installer allows MLet to install or upgrade a mbean based on the version
50  * specified in the MLet conf file. If the mbean version is newer than the
51  * registered in the server, the installer unregisters the old mbean and then
52  * registers the new one. This management needs to store the mbean version into
53  * the MBeanRegistry in the server.
54  *
55  * When we register mbeans, however, we can't pass the metadata to MBeanServer
56  * through the standard JMX api because Both of createMBean() and registerMBean()
57  * have no extra arguments to attach the metadata. Thus we call
58  * MBeanServer.invoke() directly to set/get the internal MBean metadata.
59  *
60  * Currently version and date are stored in the mbean registry as mbean metadata.
61  * The date will be used for preparing presentaionString for this mbean info.
62  * For managment purpose, we can add any extra data to the matadata if you need.
63  *
64  * @author <a HREF="mailto:Fusayuki.Minamoto@fujixerox.co.jp">Fusayuki Minamoto</a>.
65  * @author <a HREF="mailto:jplindfo@helsinki.fi">Juha Lindfors</a>.
66  *
67  * @version $Revision: 37459 $
68  *
69  * <p><b>Revisions:</b>
70  *
71  * <p><b>20020219 Juha Lindfors:</b>
72  * <ul>
73  * <li>Clarified the use of classloaders in the code, renaming loader to a more
74  * explicit ctxClassLoader
75  * </li>
76  * <li>Fixed some irregularities with the install/update code -- original
77  * implementatio was cause IndexOutOfBoundsExceptions which prevented
78  * some replacements in valid cases. Fixing this uncovered update logic
79  * that would replace MBeans that were not associated with versioning
80  * information at all.
81  * <p>
82  * The current semantics should be:
83  * <ol>
84  * <li>If an MBean is registered without versioning information it can
85  * never be automatically replaced by another MBean (regardless of
86  * the versioning information in the new MBean).
87  * </li>
88  * <li>An MBean that has a higher version number (as determined by the
89  * MLetVersion Comparable interface) can automatically replace an
90  * MBean that was registered with a lower version number.
91  * </li>
92  * <li>An MBean without versioning info can never automatically replace
93  * an MBean that was registered with version.
94  * </li>
95  * </ol>
96  * </li>
97  * </ul>
98  */

99 public class MBeanInstaller
100 {
101    // Constants -----------------------------------------------------
102

103    public static final String JavaDoc VERSIONS = "versions";
104    public static final String JavaDoc DATE = "date";
105
106    
107    // Static --------------------------------------------------------
108

109    /**
110     * Logger instance.
111     */

112    private static final Logger log = Logger.getLogger(MBeanInstaller.class);
113
114    /** Augment the PropertyEditorManager search path to incorporate the JBoss
115        specific editors. This simply references the PropertyEditors.class to
116        invoke its static initialization block.
117    */

118    static
119    {
120       Class JavaDoc c = org.jboss.util.propertyeditor.PropertyEditors.class;
121       if (c == null)
122          throw new UnreachableStatementException();
123    }
124    
125    
126    // Attributes ----------------------------------------------------
127

128    /**
129     * Reference to the MBean server the installed MBeans will get registered to.
130     */

131    private MBeanServer JavaDoc server;
132    
133    /**
134     * Reference to the context classloader of the MLet MBean that is installing
135     * the new MBeans.
136     */

137    private ClassLoader JavaDoc ctxClassLoader;
138    
139    /**
140     * Object name of the MLet MBean installing new MBeans to the server. This
141     * object name is used as the explicit classloader object name when
142     * instantiating new MBeans. This is to ensure the MLet's classloader is the
143     * first one to be consulted when loading classes. This is implicitly
144     * guaranteed by the UnifiedLoaderRepository but is not necessarily the case
145     * with other loader repository implementations.
146     */

147    private ObjectName JavaDoc loaderName;
148    
149    /**
150     * Object name of the MBeanServer registry MBean.
151     */

152    private ObjectName JavaDoc registryName;
153
154    
155    // Constructors --------------------------------------------------
156

157    /**
158     * Create a new MBean installer instance.
159     *
160     * @param server reference to the MBean server where the new MBeans will
161     * be registered to
162     * @param ctxClassLoader Context class loader reference which will be
163     * stored in the registry for the new MBeans. This
164     * classloader will be set as the thread context
165     * classloader when the MBean is invoked.
166     * @param loaderName Object name of the classloader that should be
167     * used to instantiate the newly registered MBeans.
168     * This should normally be the object name of the
169     * MLet MBean that is installing the new MBeans.
170     */

171    public MBeanInstaller(MBeanServer JavaDoc server, ClassLoader JavaDoc ctxClassLoader, ObjectName JavaDoc loaderName)
172       throws Exception JavaDoc
173    {
174       this.server = server;
175       this.ctxClassLoader = ctxClassLoader;
176       this.loaderName = loaderName;
177       this.registryName = new ObjectName JavaDoc(ServerConstants.MBEAN_REGISTRY);
178    }
179
180    
181    // Public --------------------------------------------------------
182

183    /**
184     * Install a mbean with mbean metadata<p>
185     *
186     * @param element the data parsed from the Mlet file
187     *
188     * @return mbean instance
189     */

190    public ObjectInstance JavaDoc installMBean(MBeanElement element)
191       throws MBeanException JavaDoc,
192              ReflectionException JavaDoc,
193              InstanceNotFoundException JavaDoc,
194              MalformedObjectNameException JavaDoc
195    {
196       log.debug("Installing MBean: " + element);
197       
198       ObjectInstance JavaDoc instance = null;
199       ObjectName JavaDoc elementName = getElementName(element);
200
201       if (element.getVersions().isEmpty() || !server.isRegistered(elementName))
202       {
203          if (element.getCode() != null)
204             instance = createMBean(element);
205          else if (element.getObject() != null)
206             instance = deserialize(element);
207          else
208             throw new MBeanException JavaDoc(new IllegalArgumentException JavaDoc("No code or object tag"));
209       }
210       else
211          instance = updateMBean(element);
212
213       return instance;
214    }
215
216    public ObjectInstance JavaDoc createMBean(MBeanElement element)
217       throws MBeanException JavaDoc,
218              ReflectionException JavaDoc,
219              InstanceNotFoundException JavaDoc,
220              MalformedObjectNameException JavaDoc
221    {
222       log.debug("Creating MBean.. ");
223       
224       ObjectName JavaDoc elementName = getElementName(element);
225
226       // Set up the valueMap passing to the registry.
227
// This valueMap contains mbean meta data and update time.
228
Map JavaDoc valueMap = createValueMap(element);
229
230       // Create the mbean instance
231

232       // TODO:
233
// check the delegateToCLR attribute in the MLetElement here to determine
234
// the loading behavior in case of CNFE
235

236       String JavaDoc[] classes = element.getConstructorTypes();
237       String JavaDoc[] paramStrings = element.getConstructorValues();
238       Object JavaDoc[] params = new Object JavaDoc[paramStrings.length];
239       for (int i = 0; i < paramStrings.length; ++i)
240       {
241          try
242          {
243             Class JavaDoc typeClass = server.getClassLoaderRepository().loadClass(classes[i]);
244             PropertyEditor JavaDoc editor = PropertyEditorManager.findEditor(typeClass);
245             if (editor == null)
246                throw new IllegalArgumentException JavaDoc("No property editor for type=" + typeClass);
247
248             editor.setAsText(paramStrings[i]);
249             params[i] = editor.getValue();
250          }
251          catch (Exception JavaDoc e)
252          {
253             throw new MBeanException JavaDoc(e);
254          }
255       }
256       Object JavaDoc instance = server.instantiate(
257             element.getCode(),
258             loaderName,
259             params,
260             classes);
261
262       // Call MBeanRegistry.invoke("registerMBean") instead of server.registerMBean() to pass
263
// the valueMap that contains management values including mbean metadata and update time.
264
return registerMBean(instance, elementName, valueMap);
265    }
266
267    public ObjectInstance JavaDoc deserialize(MBeanElement element) throws MBeanException JavaDoc,
268              ReflectionException JavaDoc,
269              InstanceNotFoundException JavaDoc,
270              MalformedObjectNameException JavaDoc
271    {
272       InputStream JavaDoc is = null;
273       Object JavaDoc instance = null;
274       try
275       {
276          is = ctxClassLoader.getResourceAsStream(element.getObject());
277          if (is == null)
278             throw new IllegalArgumentException JavaDoc("Object not found " + element.getObject());
279          ObjectInputStreamWithClassLoader ois = new ObjectInputStreamWithClassLoader(is, ctxClassLoader);
280          instance = ois.readObject();
281       }
282       catch (Exception JavaDoc e)
283       {
284          throw new MBeanException JavaDoc(e);
285       }
286       finally
287       {
288          if (is != null)
289          {
290             try
291             {
292                is.close();
293             }
294             catch (Exception JavaDoc ignored)
295             {
296             }
297          }
298       }
299       ObjectName JavaDoc elementName = getElementName(element);
300
301       // Set up the valueMap passing to the registry.
302
// This valueMap contains mbean meta data and update time.
303
Map JavaDoc valueMap = createValueMap(element);
304       return registerMBean(instance, elementName, valueMap);
305    }
306
307    public ObjectInstance JavaDoc updateMBean(MBeanElement element)
308       throws MBeanException JavaDoc,
309              ReflectionException JavaDoc,
310              InstanceNotFoundException JavaDoc,
311              MalformedObjectNameException JavaDoc
312    {
313       log.debug("updating MBean... ");
314       
315       ObjectName JavaDoc elementName = getElementName(element);
316
317       // Compare versions to decide whether to skip installation of this mbean
318
MLetVersion preVersion = new MLetVersion(getVersions(elementName));
319       MLetVersion newVersion = new MLetVersion(element.getVersions());
320
321       log.debug("Installed version : " + preVersion);
322       log.debug("Loaded version : " + newVersion);
323
324       // FIXME: this comparison works well only if both versions are specified
325
// because jmx spec doesn't fully specify this behavior.
326
if (!preVersion.isNull() && !newVersion.isNull() && preVersion.compareTo(newVersion) < 0)
327       {
328          // Unregister previous mbean
329
if (server.isRegistered(elementName))
330          {
331             unregisterMBean(elementName);
332             
333             log.debug("Unregistering previous version " + preVersion);
334          }
335
336          log.debug("Installing newer version " + newVersion);
337          
338          // Create mbean with value map
339
return createMBean(element);
340       }
341
342       return server.getObjectInstance(elementName);
343    }
344
345    
346    // Private -------------------------------------------------------
347

348    private ObjectName JavaDoc getElementName(MBeanElement element)
349       throws MalformedObjectNameException JavaDoc
350    {
351       return (element.getName() != null) ? new ObjectName JavaDoc(element.getName()) : null;
352    }
353
354    private Map JavaDoc createValueMap(MBeanElement element)
355    {
356       HashMap JavaDoc valueMap = new HashMap JavaDoc();
357
358       // We need to set versions here because we can't get the mbean entry
359
// outside the server.
360
if (element.getVersions() != null && !element.getVersions().isEmpty())
361          valueMap.put(VERSIONS, element.getVersions());
362
363       // The date would be used to make a presentationString for this mbean.
364
valueMap.put(DATE, new Date JavaDoc(System.currentTimeMillis()));
365
366       // Context class loader for the MBean.
367
valueMap.put(ServerConstants.CLASSLOADER, ctxClassLoader);
368
369       return valueMap;
370    }
371
372    private List JavaDoc getVersions(ObjectName JavaDoc name)
373          throws MBeanException JavaDoc, ReflectionException JavaDoc, InstanceNotFoundException JavaDoc
374    {
375       if (!server.isRegistered(name))
376          return null;
377
378       return (List JavaDoc) getValue(name, VERSIONS);
379    }
380
381
382    private Object JavaDoc getValue(ObjectName JavaDoc name, String JavaDoc key)
383       throws MBeanException JavaDoc, ReflectionException JavaDoc, InstanceNotFoundException JavaDoc
384    {
385       Object JavaDoc value =
386             server.invoke(registryName, "getValue",
387                           new Object JavaDoc[]
388                           {
389                              name,
390                              key
391                           },
392                           new String JavaDoc[]
393                           {
394                              ObjectName JavaDoc.class.getName(),
395                              String JavaDoc.class.getName()
396                           }
397             );
398
399       return value;
400    }
401
402    private ObjectInstance JavaDoc registerMBean(Object JavaDoc object, ObjectName JavaDoc name, Map JavaDoc valueMap)
403       throws MBeanException JavaDoc, ReflectionException JavaDoc, InstanceNotFoundException JavaDoc
404    {
405       if (object == null)
406       {
407          throw new ReflectionException JavaDoc(new IllegalArgumentException JavaDoc(
408                "Attempting to register a null object"
409          ));
410       }
411       
412       return (ObjectInstance JavaDoc)
413             server.invoke(registryName, "registerMBean",
414                           new Object JavaDoc[]
415                           {
416                              object,
417                              name,
418                              valueMap
419                           },
420                           new String JavaDoc[]
421                           {
422                              Object JavaDoc.class.getName(),
423                              ObjectName JavaDoc.class.getName(),
424                              Map JavaDoc.class.getName()
425                           }
426             );
427    }
428
429    private void unregisterMBean(ObjectName JavaDoc name)
430       throws MBeanException JavaDoc, ReflectionException JavaDoc, InstanceNotFoundException JavaDoc
431    {
432       server.invoke(registryName, "unregisterMBean",
433                     new Object JavaDoc[]
434                     {
435                        name,
436                     },
437                     new String JavaDoc[]
438                     {
439                        ObjectName JavaDoc.class.getName(),
440                     }
441       );
442    }
443 }
444
445 /**
446  * MLetVersion for encapsulating the version representation<p>
447  *
448  * Because this class is comparable, you can elaborate the
449  * version comparison algorithm if you need better one.
450  */

451 class MLetVersion implements Comparable JavaDoc
452 {
453    protected List JavaDoc versions;
454
455    public MLetVersion(List JavaDoc versions)
456    {
457       this.versions = versions;
458    }
459
460    public List JavaDoc getVersions()
461    {
462       return versions;
463    }
464
465    public boolean isNull()
466    {
467       return versions == null || versions.isEmpty();
468    }
469
470    public int compareTo(Object JavaDoc o)
471    {
472       MLetVersion other = (MLetVersion) o;
473
474       if (isNull() || other.isNull())
475          throw new IllegalArgumentException JavaDoc("MLet versions is null");
476
477       // FIXME: this compares only first element of the versions.
478
// do we really need multiple versions?
479
String JavaDoc thisVersion = (String JavaDoc) versions.get(0);
480       String JavaDoc otherVersion = (String JavaDoc) other.getVersions().get(0);
481
482       return (thisVersion.compareTo(otherVersion));
483    }
484    
485    public String JavaDoc toString()
486    {
487       return "Version " + versions.get(0);
488    }
489 }
490
Popular Tags