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 =
093        new HashMap<Object,MBeanServer>();
094    
095      /**
096       * Private constructor to prevent instance creation.
097       */
098      private MBeanServerFactory() {}
099    
100      /**
101       * Returns a server implementation using the default domain name
102       * of <code>"DefaultDomain"</code>.  The default domain name is
103       * used when the domain name specified by the user is <code>null</code.
104       * A reference to the created server is retained, so that it can
105       * be retrieved at a later date using {@link #findMBeanServer}.
106       * Calling this method is equivalent to calling
107       * {@link createMBeanServer(String)} with a <code>null</code> value.
108       *
109       * @return a new {@link MBeanServer} instance.
110       * @throws SecurityException if a security manager exists and the
111       *                           caller's permissions don't imply {@link
112       *                           MBeanServerPermission(String)}("createMBeanServer")
113       * @throws JMRuntimeException if the property
114       *                     <code>javax.management.builder.initial</code>
115       *                     exists but names a class which either can not be
116       *                     instantiated or provides an implementation that returns
117       *                     <code>null</code> from either
118       *                     {@link MBeanServerBuilder#newMBeanServerDelegate()}
119       *                     or {@link MBeanServerBuilder#newMBeanServer()}
120       * @throws ClassCastException if the property
121       *                     <code>javax.management.builder.initial</code>
122       *                     exists but names a class which is not a subclass
123       *                     of {@link MBeanServerBuilder}.
124       * @see #createMBeanServer(String)
125       */
126      public static MBeanServer createMBeanServer()
127      {
128        return createMBeanServer(null);
129      }
130    
131      /**
132       * Returns a server implementation using the default domain name
133       * given, or <code>"DefaultDomain"</code> if this is <code>null</code>.
134       * The default domain name is used when the domain name specified by
135       * the user is <code>null</code.  A reference to the created server is
136       * retained, so that it can be retrieved at a later date using
137       * {@link #findMBeanServer}.
138       *
139       * @param domain the default domain name of the server.
140       * @return a new {@link MBeanServer} instance.
141       * @throws SecurityException if a security manager exists and the
142       *                           caller's permissions don't imply {@link
143       *                           MBeanServerPermission(String)}("createMBeanServer")
144       * @throws JMRuntimeException if the property
145       *                     <code>javax.management.builder.initial</code>
146       *                     exists but names a class which either can not be
147       *                     instantiated or provides an implementation that returns
148       *                     <code>null</code> from either
149       *                     {@link MBeanServerBuilder#newMBeanServerDelegate()}
150       *                     or {@link MBeanServerBuilder#newMBeanServer()}
151       * @throws ClassCastException if the property
152       *                     <code>javax.management.builder.initial</code>
153       *                     exists but names a class which is not a subclass
154       *                     of {@link MBeanServerBuilder}.
155       */
156      public static MBeanServer createMBeanServer(String domain)
157      {
158        SecurityManager sm = System.getSecurityManager();
159        if (sm != null)
160          sm.checkPermission(new MBeanServerPermission("createMBeanServer"));
161        MBeanServer server = createServer(domain);
162        try
163          {
164            ObjectName dn = new
165              ObjectName("JMImplementation:type=MBeanServerDelegate");
166            servers.put(server.getAttribute(dn, "MBeanServerId"), server);
167          }
168        catch (MalformedObjectNameException e)
169          {
170            throw (Error)
171              (new InternalError("Malformed delegate bean name.").initCause(e));
172          }
173        catch (MBeanException e)
174          {
175            throw (Error)
176              (new InternalError("Exception in getMBeanServerId().").initCause(e));
177          }
178        catch (AttributeNotFoundException e)
179          {
180            throw (Error)
181              (new InternalError("Could not find MBeanServerId attribute.").initCause(e));
182          }
183        catch (InstanceNotFoundException e)
184          {
185            throw (Error)
186              (new InternalError("Could not find the delegate bean.").initCause(e));
187          }
188        catch (ReflectionException e)
189          {
190            throw (Error)
191              (new InternalError("Could not call getMBeanServerId().").initCause(e));
192          }
193        return server;
194      }
195    
196      /**
197       * Returns the specified server, or, if <code>id</code> is <code>null</code>,
198       * a list of all registered servers.  A registered server is one that
199       * was created using {@link #createMBeanServer()} or
200       * {@link #createMBeanServer(String)} and has not yet been released
201       * using {@link releaseMBeanServer(MBeanServer)}.
202       *
203       * @param id the id of the server to retrieve, or <code>null</code>
204       *           to return all servers.
205       * @return a list of {@link MBeanServer}s.
206       * @throws SecurityException if a security manager exists and the
207       *                           caller's permissions don't imply {@link
208       *                           MBeanServerPermission(String)}("findMBeanServer")
209       */
210      public static ArrayList<MBeanServer> findMBeanServer(String id)
211      {
212        SecurityManager sm = System.getSecurityManager();
213        if (sm != null)
214          sm.checkPermission(new MBeanServerPermission("findMBeanServer"));
215        if (id == null)
216          return new ArrayList<MBeanServer>(servers.values());
217        ArrayList<MBeanServer> list = new ArrayList<MBeanServer>();
218        MBeanServer server = servers.get(id);
219        if (server != null)
220          list.add(servers.get(id));
221        return list;
222      }
223    
224      /**
225       * Returns the class loader repository used by the specified server.
226       * This is equivalent to calling {@link MBeanServer#getClassLoaderRepository()}
227       * on the given server.
228       *
229       * @param server the server whose class loader repository should be
230       *               retrieved.
231       * @throws NullPointerException if <code>server</code> is <code>null</code>.
232       * @throws SecurityException if a security manager exists and the
233       *                           caller's permissions don't imply {@link
234       *                           MBeanPermission(String,String,ObjectName,String)
235       *                           <code>MBeanPermission(null, null, null,
236       *                           "getClassLoaderRepository")</code>
237       */
238      public static ClassLoaderRepository getClassLoaderRepository(MBeanServer server)
239      {
240        return server.getClassLoaderRepository();
241      }
242    
243      /**
244       * Returns a server implementation using the default domain name
245       * of <code>"DefaultDomain"</code>.  The default domain name is
246       * used when the domain name specified by the user is <code>null</code.
247       * No reference to the created server is retained, so the server is
248       * garbage collected when it is no longer used, but it can not be
249       * retrieved at a later date using {@link #findMBeanServer}.
250       * Calling this method is equivalent to calling
251       * {@link newMBeanServer(String)} with a <code>null</code> value.
252       *
253       * @return a new {@link MBeanServer} instance.
254       * @throws SecurityException if a security manager exists and the
255       *                           caller's permissions don't imply {@link
256       *                           MBeanServerPermission(String)}("newMBeanServer")
257       * @throws JMRuntimeException if the property
258       *                     <code>javax.management.builder.initial</code>
259       *                     exists but names a class which either can not be
260       *                     instantiated or provides an implementation that returns
261       *                     <code>null</code> from either
262       *                     {@link MBeanServerBuilder#newMBeanServerDelegate()}
263       *                     or {@link MBeanServerBuilder#newMBeanServer()}
264       * @throws ClassCastException if the property
265       *                     <code>javax.management.builder.initial</code>
266       *                     exists but names a class which is not a subclass
267       *                     of {@link MBeanServerBuilder}.
268       * @see #newMBeanServer(String)
269       */
270      public static MBeanServer newMBeanServer()
271      {
272        return newMBeanServer(null);
273      }
274    
275      /**
276       * Returns a server implementation using the default domain name
277       * given, or <code>"DefaultDomain"</code> if this is <code>null</code>.
278       * The default domain name is used when the domain name specified by
279       * the user is <code>null</code.  No reference to the created server is
280       * retained, so the server is garbage collected when it is no longer
281       * used, but it can not be retrieved at a later date using
282       * {@link #findMBeanServer}.
283       *
284       * @param domain the default domain name of the server.
285       * @return a new {@link MBeanServer} instance.
286       * @throws SecurityException if a security manager exists and the
287       *                           caller's permissions don't imply {@link
288       *                           MBeanServerPermission(String)}("newMBeanServer")
289       * @throws JMRuntimeException if the property
290       *                     <code>javax.management.builder.initial</code>
291       *                     exists but names a class which either can not be
292       *                     instantiated or provides an implementation that returns
293       *                     <code>null</code> from either
294       *                     {@link MBeanServerBuilder#newMBeanServerDelegate()}
295       *                     or {@link MBeanServerBuilder#newMBeanServer()}
296       * @throws ClassCastException if the property
297       *                     <code>javax.management.builder.initial</code>
298       *                     exists but names a class which is not a subclass
299       *                     of {@link MBeanServerBuilder}.
300       */
301      public static MBeanServer newMBeanServer(String domain)
302      {
303        SecurityManager sm = System.getSecurityManager();
304        if (sm != null)
305          sm.checkPermission(new MBeanServerPermission("newMBeanServer"));
306        return createServer(domain);
307      }
308    
309      /**
310       * Common method to create a server for the {@link #createMBeanServer(String)}
311       * and {@link #newMBeanServer(String)} methods above.
312       *
313       * @param domain the default domain name of the server.
314       * @throws JMRuntimeException if the property
315       *                     <code>javax.management.builder.initial</code>
316       *                     exists but names a class which either can not be
317       *                     instantiated or provides an implementation that returns
318       *                     <code>null</code> from either
319       *                     {@link MBeanServerBuilder#newMBeanServerDelegate()}
320       *                     or {@link MBeanServerBuilder#newMBeanServer()}
321       * @throws ClassCastException if the property
322       *                     <code>javax.management.builder.initial</code>
323       *                     exists but names a class which is not a subclass
324       *                     of {@link MBeanServerBuilder}.
325       */
326      private static MBeanServer createServer(String domain)
327        {
328        if (domain == null)
329          domain = "DefaultDomain";
330        String builderClass =
331          SystemProperties.getProperty("javax.management.builder.initial");
332        if (builderClass == null)
333          {
334            if (builder == null ||
335                builder.getClass() != MBeanServerBuilder.class)
336              builder = new MBeanServerBuilder();
337          }
338        else if (!(builder != null &&
339                   builderClass.equals(builder.getClass().getName())))
340          {
341            ClassLoader cl = Thread.currentThread().getContextClassLoader();
342            if (cl == null)
343              cl = MBeanServerFactory.class.getClassLoader();
344            try
345              {
346                Class<?> bClass = Class.forName(builderClass, true, cl);
347                builder = (MBeanServerBuilder) bClass.newInstance();
348              }
349            catch (ClassNotFoundException e)
350              {
351                throw (JMRuntimeException) (new JMRuntimeException("The builder class, "
352                                                                   + builderClass +
353                                                                   ", could not be found."))
354                  .initCause(e);
355              }
356            catch (InstantiationException e)
357              {
358                throw (JMRuntimeException) (new JMRuntimeException("The builder class, "
359                                                                   + builderClass +
360                                                                   ", could not be instantiated."))
361                  .initCause(e);
362              }
363            catch (IllegalAccessException e)
364              {
365                throw (JMRuntimeException) (new JMRuntimeException("The builder class, "
366                                                                   + builderClass +
367                                                                   ", could not be accessed."))
368                  .initCause(e);
369              }
370          }
371        MBeanServerDelegate delegate = builder.newMBeanServerDelegate();
372        if (delegate == null)
373          throw new JMRuntimeException("A delegate could not be created.");
374        MBeanServer server = builder.newMBeanServer(domain, null, delegate);
375        if (server == null)
376          throw new JMRuntimeException("A server could not be created.");
377        return server;
378      }
379    
380      /**
381       * Removes the reference to the specified server, thus allowing it to
382       * be garbage collected.
383       *
384       * @param server the server to remove.
385       * @throws IllegalArgumentException if a reference to the server is not
386       *                                  held (i.e. it wasn't created by
387       *                                  {@link #createMBeanServer(String)}
388       *                                  or this method has already been called
389       *                                  on it.
390       * @throws SecurityException if a security manager exists and the
391       *                           caller's permissions don't imply {@link
392       *                           MBeanServerPermission(String)}("releaseMBeanServer")
393       */
394      public static void releaseMBeanServer(MBeanServer server)
395      {
396        SecurityManager sm = System.getSecurityManager();
397        if (sm != null)
398          sm.checkPermission(new MBeanServerPermission("releaseMBeanServer"));
399        Iterator<MBeanServer> i = servers.values().iterator();
400        while (i.hasNext())
401          {
402            MBeanServer s = i.next();
403            if (server == s)
404              {
405                i.remove();
406                return;
407              }
408          }
409        throw new IllegalArgumentException("The server given is not referenced.");
410      }
411    
412    
413    }