001/* MBeanServerFactory.java -- Manages server instances. 002 Copyright (C) 2006 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038package javax.management; 039 040import gnu.classpath.SystemProperties; 041 042import java.util.ArrayList; 043import java.util.HashMap; 044import java.util.Iterator; 045import java.util.Map; 046 047import 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 */ 081public 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}