001    /* MBeanServerFactory.java -- Manages server instances.
002       Copyright (C) 2006 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    package javax.management;
039    
040    import gnu.classpath.SystemProperties;
041    
042    import java.util.ArrayList;
043    import java.util.HashMap;
044    import java.util.Iterator;
045    import java.util.Map;
046    
047    import javax.management.loading.ClassLoaderRepository;
048    
049    /**
050     * <p>
051     * Creates and maintains a set of {@link MBeanServer} instances.
052     * Server instances, as of JMX 1.2, are created using a subclass
053     * of {@link MBeanServerBuilder}.  The exact class used is controlled
054     * by the property <code>javax.management.builder.initial</code>,
055     * and allows the instances created by {@link MBeanServerBuilder}
056     * to be wrapped, thus providing additional functionality.
057     * </p>
058     * <p>
059     * The property is used as follows:
060     * </p>
061     * <ol>
062     * <li>If the property has no value, then an instance of
063     * {@link MBeanServerBuilder} is used.</li>
064     * <li>If a value is given, then:
065     * <ol>
066     * <li>The class is loaded using
067     * <code>Thread.currentThread().getContextClassLoader()</code>, or,
068     * if this is <code>null</code>, by <code>Class.forName()</code>.</li>
069     * <li><code>Class.newInstance()</code> is used to create an instance
070     * of the class.  The class must be public and have a public empty
071     * constructor.  If an exception is thrown, it is propogated as
072     * a {@link JMRuntimeException} and no new server instances may be
073     * created until the property is set to a valid value.</li>
074     * </ol></li>
075     * <li>The value is checked on each successive request for a server.
076     * If it differs from the class of the existing instance of
077     * {@link MBeanServerBuilder}, then the value is used to create
078     * a new instance.</li>
079     * </ol>
080     */
081    public class MBeanServerFactory
082    {
083    
084      /**
085       * The last builder instance.
086       */
087      private static MBeanServerBuilder builder;
088    
089      /**
090       * The map of registered servers (identifiers to servers).
091       */
092      private static final Map<Object,MBeanServer> servers = new HashMap();
093    
094      /**
095       * Private constructor to prevent instance creation.
096       */
097      private MBeanServerFactory() {}
098    
099      /**
100       * Returns a server implementation using the default domain name
101       * of <code>"DefaultDomain"</code>.  The default domain name is
102       * used when the domain name specified by the user is <code>null</code.
103       * A reference to the created server is retained, so that it can
104       * be retrieved at a later date using {@link #findMBeanServer}.
105       * Calling this method is equivalent to calling
106       * {@link createMBeanServer(String)} with a <code>null</code> value.
107       *
108       * @return a new {@link MBeanServer} instance.
109       * @throws SecurityException if a security manager exists and the
110       *                           caller's permissions don't imply {@link
111       *                           MBeanServerPermission(String)}("createMBeanServer")
112       * @throws JMRuntimeException if the property
113       *                     <code>javax.management.builder.initial</code>
114       *                     exists but names a class which either can not be
115       *                     instantiated or provides an implementation that returns
116       *                     <code>null</code> from either
117       *                     {@link MBeanServerBuilder#newMBeanServerDelegate()}
118       *                     or {@link MBeanServerBuilder#newMBeanServer()}
119       * @throws ClassCastException if the property
120       *                     <code>javax.management.builder.initial</code>
121       *                     exists but names a class which is not a subclass
122       *                     of {@link MBeanServerBuilder}.
123       * @see #createMBeanServer(String)
124       */
125      public static MBeanServer createMBeanServer()
126      {
127        return createMBeanServer(null);
128      }
129    
130      /**
131       * Returns a server implementation using the default domain name
132       * given, or <code>"DefaultDomain"</code> if this is <code>null</code>.
133       * The default domain name is used when the domain name specified by
134       * the user is <code>null</code.  A reference to the created server is
135       * retained, so that it can be retrieved at a later date using
136       * {@link #findMBeanServer}.  
137       *
138       * @param domain the default domain name of the server.
139       * @return a new {@link MBeanServer} instance.
140       * @throws SecurityException if a security manager exists and the
141       *                           caller's permissions don't imply {@link
142       *                           MBeanServerPermission(String)}("createMBeanServer")
143       * @throws JMRuntimeException if the property
144       *                     <code>javax.management.builder.initial</code>
145       *                     exists but names a class which either can not be
146       *                     instantiated or provides an implementation that returns
147       *                     <code>null</code> from either
148       *                     {@link MBeanServerBuilder#newMBeanServerDelegate()}
149       *                     or {@link MBeanServerBuilder#newMBeanServer()}
150       * @throws ClassCastException if the property
151       *                     <code>javax.management.builder.initial</code>
152       *                     exists but names a class which is not a subclass
153       *                     of {@link MBeanServerBuilder}.
154       */
155      public static MBeanServer createMBeanServer(String domain)
156      {
157        SecurityManager sm = System.getSecurityManager();
158        if (sm != null)
159          sm.checkPermission(new MBeanServerPermission("createMBeanServer"));
160        MBeanServer server = createServer(domain);
161        try
162          {
163            ObjectName dn = new
164              ObjectName("JMImplementation:type=MBeanServerDelegate");
165            servers.put(server.getAttribute(dn, "MBeanServerId"), server);
166          }
167        catch (MalformedObjectNameException e)
168          {
169            throw (Error) 
170              (new InternalError("Malformed delegate bean name.").initCause(e));
171          }
172        catch (MBeanException e)
173          {
174            throw (Error) 
175              (new InternalError("Exception in getMBeanServerId().").initCause(e));
176          }
177        catch (AttributeNotFoundException e)
178          {
179            throw (Error) 
180              (new InternalError("Could not find MBeanServerId attribute.").initCause(e));
181          }
182        catch (InstanceNotFoundException e)
183          {
184            throw (Error) 
185              (new InternalError("Could not find the delegate bean.").initCause(e));
186          }
187        catch (ReflectionException e)
188          {
189            throw (Error) 
190              (new InternalError("Could not call getMBeanServerId().").initCause(e));
191          }
192        return server;
193      }
194    
195      /**
196       * Returns the specified server, or, if <code>id</code> is <code>null</code>,
197       * a list of all registered servers.  A registered server is one that
198       * was created using {@link #createMBeanServer()} or
199       * {@link #createMBeanServer(String)} and has not yet been released
200       * using {@link releaseMBeanServer(MBeanServer)}.
201       *
202       * @param id the id of the server to retrieve, or <code>null</code>
203       *           to return all servers.
204       * @return a list of {@link MBeanServer}s.
205       * @throws SecurityException if a security manager exists and the
206       *                           caller's permissions don't imply {@link
207       *                           MBeanServerPermission(String)}("findMBeanServer")
208       */
209      public static ArrayList<MBeanServer> findMBeanServer(String id)
210      {
211        SecurityManager sm = System.getSecurityManager();
212        if (sm != null)
213          sm.checkPermission(new MBeanServerPermission("findMBeanServer"));
214        if (id == null)
215          return new ArrayList(servers.values());
216        ArrayList<MBeanServer> list = new ArrayList<MBeanServer>();
217        MBeanServer server = servers.get(id);
218        if (server != null)
219          list.add(servers.get(id));
220        return list;
221      }
222    
223      /**
224       * Returns the class loader repository used by the specified server.
225       * This is equivalent to calling {@link MBeanServer#getClassLoaderRepository()}
226       * on the given server.
227       * 
228       * @param server the server whose class loader repository should be
229       *               retrieved.
230       * @throws NullPointerException if <code>server</code> is <code>null</code>.
231       * @throws SecurityException if a security manager exists and the
232       *                           caller's permissions don't imply {@link
233       *                           MBeanPermission(String,String,ObjectName,String)
234       *                           <code>MBeanPermission(null, null, null,
235       *                           "getClassLoaderRepository")</code>
236       */
237      public static ClassLoaderRepository getClassLoaderRepository(MBeanServer server)
238      {
239        return server.getClassLoaderRepository();
240      }
241    
242      /**
243       * Returns a server implementation using the default domain name
244       * of <code>"DefaultDomain"</code>.  The default domain name is
245       * used when the domain name specified by the user is <code>null</code.
246       * No reference to the created server is retained, so the server is
247       * garbage collected when it is no longer used, but it can not be
248       * retrieved at a later date using {@link #findMBeanServer}.   
249       * Calling this method is equivalent to calling
250       * {@link newMBeanServer(String)} with a <code>null</code> value.
251       *
252       * @return a new {@link MBeanServer} instance.
253       * @throws SecurityException if a security manager exists and the
254       *                           caller's permissions don't imply {@link
255       *                           MBeanServerPermission(String)}("newMBeanServer")
256       * @throws JMRuntimeException if the property
257       *                     <code>javax.management.builder.initial</code>
258       *                     exists but names a class which either can not be
259       *                     instantiated or provides an implementation that returns
260       *                     <code>null</code> from either
261       *                     {@link MBeanServerBuilder#newMBeanServerDelegate()}
262       *                     or {@link MBeanServerBuilder#newMBeanServer()}
263       * @throws ClassCastException if the property
264       *                     <code>javax.management.builder.initial</code>
265       *                     exists but names a class which is not a subclass
266       *                     of {@link MBeanServerBuilder}.
267       * @see #newMBeanServer(String)
268       */
269      public static MBeanServer newMBeanServer()
270      {
271        return newMBeanServer(null);
272      }
273    
274      /**
275       * Returns a server implementation using the default domain name
276       * given, or <code>"DefaultDomain"</code> if this is <code>null</code>.
277       * The default domain name is used when the domain name specified by
278       * the user is <code>null</code.  No reference to the created server is
279       * retained, so the server is garbage collected when it is no longer
280       * used, but it can not be retrieved at a later date using
281       * {@link #findMBeanServer}.
282       *
283       * @param domain the default domain name of the server.
284       * @return a new {@link MBeanServer} instance.
285       * @throws SecurityException if a security manager exists and the
286       *                           caller's permissions don't imply {@link
287       *                           MBeanServerPermission(String)}("newMBeanServer")
288       * @throws JMRuntimeException if the property
289       *                     <code>javax.management.builder.initial</code>
290       *                     exists but names a class which either can not be
291       *                     instantiated or provides an implementation that returns
292       *                     <code>null</code> from either
293       *                     {@link MBeanServerBuilder#newMBeanServerDelegate()}
294       *                     or {@link MBeanServerBuilder#newMBeanServer()}
295       * @throws ClassCastException if the property
296       *                     <code>javax.management.builder.initial</code>
297       *                     exists but names a class which is not a subclass
298       *                     of {@link MBeanServerBuilder}.
299       */
300      public static MBeanServer newMBeanServer(String domain)
301      {
302        SecurityManager sm = System.getSecurityManager();
303        if (sm != null)
304          sm.checkPermission(new MBeanServerPermission("newMBeanServer"));
305        return createServer(domain);
306      }
307    
308      /**
309       * Common method to create a server for the {@link #createMBeanServer(String)}
310       * and {@link #newMBeanServer(String)} methods above.
311       *
312       * @param domain the default domain name of the server.
313       * @throws JMRuntimeException if the property
314       *                     <code>javax.management.builder.initial</code>
315       *                     exists but names a class which either can not be
316       *                     instantiated or provides an implementation that returns
317       *                     <code>null</code> from either
318       *                     {@link MBeanServerBuilder#newMBeanServerDelegate()}
319       *                     or {@link MBeanServerBuilder#newMBeanServer()}
320       * @throws ClassCastException if the property
321       *                     <code>javax.management.builder.initial</code>
322       *                     exists but names a class which is not a subclass
323       *                     of {@link MBeanServerBuilder}.
324       */
325      private static MBeanServer createServer(String domain)
326        {
327        if (domain == null)
328          domain = "DefaultDomain";
329        String builderClass =
330          SystemProperties.getProperty("javax.management.builder.initial");
331        if (builderClass == null)
332          {
333            if (builder == null ||
334                builder.getClass() != MBeanServerBuilder.class)
335              builder = new MBeanServerBuilder();
336          }
337        else if (!(builder != null &&
338                   builderClass.equals(builder.getClass().getName())))
339          {
340            ClassLoader cl = Thread.currentThread().getContextClassLoader();
341            if (cl == null)
342              cl = MBeanServerFactory.class.getClassLoader();
343            try
344              {
345                Class bClass = Class.forName(builderClass, true, cl);
346                builder = (MBeanServerBuilder) bClass.newInstance();
347              }
348            catch (ClassNotFoundException e)
349              {
350                throw (JMRuntimeException) (new JMRuntimeException("The builder class, " 
351                                                                   + builderClass +
352                                                                   ", could not be found."))
353                  .initCause(e);
354              }
355            catch (InstantiationException e)
356              {
357                throw (JMRuntimeException) (new JMRuntimeException("The builder class, " 
358                                                                   + builderClass +
359                                                                   ", could not be instantiated."))
360                  .initCause(e);
361              }
362            catch (IllegalAccessException e)
363              {
364                throw (JMRuntimeException) (new JMRuntimeException("The builder class, " 
365                                                                   + builderClass +
366                                                                   ", could not be accessed."))
367                  .initCause(e);
368              }
369          }
370        MBeanServerDelegate delegate = builder.newMBeanServerDelegate();
371        if (delegate == null)
372          throw new JMRuntimeException("A delegate could not be created.");
373        MBeanServer server = builder.newMBeanServer(domain, null, delegate);
374        if (server == null)
375          throw new JMRuntimeException("A server could not be created.");
376        return server;
377      }
378    
379      /**
380       * Removes the reference to the specified server, thus allowing it to
381       * be garbage collected.
382       *
383       * @param server the server to remove.
384       * @throws IllegalArgumentException if a reference to the server is not
385       *                                  held (i.e. it wasn't created by
386       *                                  {@link #createMBeanServer(String)}
387       *                                  or this method has already been called
388       *                                  on it.
389       * @throws SecurityException if a security manager exists and the
390       *                           caller's permissions don't imply {@link
391       *                           MBeanServerPermission(String)}("releaseMBeanServer")
392       */
393      public static void releaseMBeanServer(MBeanServer server)
394      {
395        SecurityManager sm = System.getSecurityManager();
396        if (sm != null)
397          sm.checkPermission(new MBeanServerPermission("releaseMBeanServer"));
398        Iterator<MBeanServer> i = servers.values().iterator();
399        while (i.hasNext())
400          {
401            MBeanServer s = i.next();
402            if (server == s)
403              {
404                i.remove();
405                return;
406              }
407          }
408        throw new IllegalArgumentException("The server given is not referenced.");
409      }
410    
411    
412    }