1. /*
  2. * @(#)JMXConnectorServerFactory.java 1.17 04/05/05
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.management.remote;
  8. import java.io.IOException;
  9. import java.net.MalformedURLException;
  10. import java.util.Collections;
  11. import java.util.HashMap;
  12. import java.util.Iterator;
  13. import java.util.Map;
  14. import javax.management.MBeanServer;
  15. import javax.management.ObjectName;
  16. import com.sun.jmx.remote.util.ClassLogger;
  17. import com.sun.jmx.remote.util.EnvHelp;
  18. /**
  19. * <p>Factory to create JMX API connector servers. There
  20. * are no instances of this class.</p>
  21. *
  22. * <p>Each connector server is created by an instance of {@link
  23. * JMXConnectorServerProvider}. This instance is found as follows. Suppose
  24. * the given {@link JMXServiceURL} looks like
  25. * <code>"service:jmx:<em>protocol</em>:<em>remainder</em>"</code>.
  26. * Then the factory will attempt to find the appropriate {@link
  27. * JMXConnectorServerProvider} for <code><em>protocol</em></code>. Each
  28. * occurrence of the character <code>+</code> or <code>-</code> in
  29. * <code><em>protocol</em></code> is replaced by <code>.</code> or
  30. * <code>_</code>, respectively.</p>
  31. *
  32. * <p>A <em>provider package list</em> is searched for as follows:</p>
  33. *
  34. * <ol>
  35. *
  36. * <li>If the <code>environment</code> parameter to {@link
  37. * #newJMXConnectorServer(JMXServiceURL,Map,MBeanServer)
  38. * newJMXConnectorServer} contains the key
  39. * <code>jmx.remote.protocol.provider.pkgs</code> then the associated
  40. * value is the provider package list.
  41. *
  42. * <li>Otherwise, if the system property
  43. * <code>jmx.remote.protocol.provider.pkgs</code> exists, then its value
  44. * is the provider package list.
  45. *
  46. * <li>Otherwise, there is no provider package list.
  47. *
  48. * </ol>
  49. *
  50. * <p>The provider package list is a string that is interpreted as a
  51. * list of non-empty Java package names separated by vertical bars
  52. * (<code>|</code>). If the string is empty, then so is the provider
  53. * package list. If the provider package list is not a String, or if
  54. * it contains an element that is an empty string, a {@link
  55. * JMXProviderException} is thrown.</p>
  56. *
  57. * <p>If the provider package list exists and is not empty, then for
  58. * each element <code><em>pkg</em></code> of the list, the factory
  59. * will attempt to load the class
  60. *
  61. * <blockquote>
  62. * <code><em>pkg</em>.<em>protocol</em>.ServerProvider</code>
  63. * </blockquote>
  64. * <p>If the <code>environment</code> parameter to {@link
  65. * #newJMXConnectorServer(JMXServiceURL, Map, MBeanServer)
  66. * newJMXConnectorServer} contains the key
  67. * <code>jmx.remote.protocol.provider.class.loader</code> then the
  68. * associated value is the class loader to use to load the provider.
  69. * If the associated value is not an instance of {@link
  70. * java.lang.ClassLoader}, an {@link
  71. * java.lang.IllegalArgumentException} is thrown.</p>
  72. *
  73. * <p>If the <code>jmx.remote.protocol.provider.class.loader</code>
  74. * key is not present in the <code>environment</code> parameter, the
  75. * calling thread's context class loader is used.</p>
  76. *
  77. * <p>If the attempt to load this class produces a {@link
  78. * ClassNotFoundException}, the search for a handler continues with
  79. * the next element of the list.</p>
  80. *
  81. * <p>Otherwise, a problem with the provider found is signalled by a
  82. * {@link JMXProviderException} whose {@link
  83. * JMXProviderException#getCause() <em>cause</em>} indicates the
  84. * underlying exception, as follows:</p>
  85. *
  86. * <ul>
  87. *
  88. * <li>if the attempt to load the class produces an exception other
  89. * than <code>ClassNotFoundException</code>, that is the
  90. * <em>cause</em>
  91. *
  92. * <li>if {@link Class#newInstance()} for the class produces an
  93. * exception, that is the <em>cause</em>.
  94. *
  95. * </ul>
  96. *
  97. * <p>If no provider is found by the above steps, including the
  98. * default case where there is no provider package list, then the
  99. * implementation will use its own provider for
  100. * <code><em>protocol</em></code>, or it will throw a
  101. * <code>MalformedURLException</code> if there is none. An
  102. * implementation may choose to find providers by other means. For
  103. * example, it may support the <a
  104. * href="http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html#Service Provider">
  105. * JAR conventions for service providers</a>, where the service
  106. * interface is <code>JMXConnectorServerProvider</code>.</p>
  107. *
  108. * <p>Every implementation must support the RMI connector protocols,
  109. * specified with the string <code>rmi</code> or
  110. * <code>iiop</code>.</p>
  111. *
  112. * <p>Once a provider is found, the result of the
  113. * <code>newJMXConnectorServer</code> method is the result of calling
  114. * {@link
  115. * JMXConnectorServerProvider#newJMXConnectorServer(JMXServiceURL,
  116. * Map, MBeanServer) newJMXConnectorServer} on the provider.</p>
  117. *
  118. * <p>The <code>Map</code> parameter passed to the
  119. * <code>JMXConnectorServerProvider</code> is a new read-only
  120. * <code>Map</code> that contains all the entries that were in the
  121. * <code>environment</code> parameter to {@link
  122. * #newJMXConnectorServer(JMXServiceURL,Map,MBeanServer)
  123. * JMXConnectorServerFactory.newJMXConnectorServer}, if there was one.
  124. * Additionally, if the
  125. * <code>jmx.remote.protocol.provider.class.loader</code> key is not
  126. * present in the <code>environment</code> parameter, it is added to
  127. * the new read-only <code>Map</code>. The associated value is the
  128. * calling thread's context class loader.</p>
  129. *
  130. * @since 1.5
  131. * @since.unbundled 1.0
  132. */
  133. public class JMXConnectorServerFactory {
  134. /**
  135. * <p>Name of the attribute that specifies the default class
  136. * loader. This class loader is used to deserialize objects in
  137. * requests received from the client, possibly after consulting an
  138. * MBean-specific class loader. The value associated with this
  139. * attribute is an instance of {@link ClassLoader}.</p>
  140. */
  141. public static final String DEFAULT_CLASS_LOADER =
  142. JMXConnectorFactory.DEFAULT_CLASS_LOADER;
  143. /**
  144. * <p>Name of the attribute that specifies the default class
  145. * loader MBean name. This class loader is used to deserialize objects in
  146. * requests received from the client, possibly after consulting an
  147. * MBean-specific class loader. The value associated with this
  148. * attribute is an instance of {@link ObjectName}.</p>
  149. */
  150. public static final String DEFAULT_CLASS_LOADER_NAME =
  151. "jmx.remote.default.class.loader.name";
  152. /**
  153. * <p>Name of the attribute that specifies the provider packages
  154. * that are consulted when looking for the handler for a protocol.
  155. * The value associated with this attribute is a string with
  156. * package names separated by vertical bars (<code>|</code>).</p>
  157. */
  158. public static final String PROTOCOL_PROVIDER_PACKAGES =
  159. "jmx.remote.protocol.provider.pkgs";
  160. /**
  161. * <p>Name of the attribute that specifies the class
  162. * loader for loading protocol providers.
  163. * The value associated with this attribute is an instance
  164. * of {@link ClassLoader}.</p>
  165. */
  166. public static final String PROTOCOL_PROVIDER_CLASS_LOADER =
  167. "jmx.remote.protocol.provider.class.loader";
  168. private static final String PROTOCOL_PROVIDER_DEFAULT_PACKAGE =
  169. "com.sun.jmx.remote.protocol";
  170. private static final ClassLogger logger =
  171. new ClassLogger("javax.management.remote.misc","JMXConnectorServerFactory");
  172. /** There are no instances of this class. */
  173. private JMXConnectorServerFactory() {
  174. }
  175. private static JMXConnectorServer
  176. getConnectorServerAsService(ClassLoader loader,
  177. JMXServiceURL url,
  178. Map map,
  179. MBeanServer mbs)
  180. throws IllegalArgumentException, JMXProviderException {
  181. Iterator providers =
  182. JMXConnectorFactory.getProviderIterator(JMXConnectorServerProvider.class,
  183. loader);
  184. JMXConnectorServerProvider provider = null;
  185. JMXConnectorServer connection = null;
  186. while(providers.hasNext()) {
  187. provider =
  188. (JMXConnectorServerProvider) providers.next();
  189. try {
  190. connection = provider.newJMXConnectorServer(url, map, mbs);
  191. return connection;
  192. } catch (JMXProviderException e) {
  193. throw e;
  194. }
  195. catch (Exception e) {
  196. if (logger.traceOn())
  197. logger.trace("getConnectorAsService",
  198. "URL[" + url +
  199. "] Service provider exception " + e);
  200. continue;
  201. }
  202. }
  203. return null;
  204. }
  205. /**
  206. * <p>Creates a connector server at the given address. The
  207. * resultant server is not started until its {@link
  208. * JMXConnectorServer#start() start} method is called.</p>
  209. *
  210. * @param serviceURL the address of the new connector server. The
  211. * actual address of the new connector server, as returned by its
  212. * {@link JMXConnectorServer#getAddress() getAddress} method, will
  213. * not necessarily be exactly the same. For example, it might
  214. * include a port number if the original address did not.
  215. *
  216. * @param environment a set of attributes to control the new
  217. * connector server's behaviour. This parameter can be null.
  218. * Keys in this map must be Strings. The appropriate type of each
  219. * associated value depends on the attribute. The contents of
  220. * <code>environment</code> are not changed by this call.
  221. *
  222. * @param mbeanServer the MBean server that this connector server
  223. * is attached to. Null if this connector server will be attached
  224. * to an MBean server by being registered in it.
  225. *
  226. * @return a <code>JMXConnectorServer</code> representing the new
  227. * connector server. Each successful call to this method produces
  228. * a different object.
  229. *
  230. * @exception NullPointerException if <code>serviceURL</code> is null.
  231. *
  232. * @exception IOException if the connector server cannot be made
  233. * because of a communication problem.
  234. *
  235. * @exception MalformedURLException if there is no provider for the
  236. * protocol in <code>serviceURL</code>.
  237. *
  238. * @exception JMXProviderException if there is a provider for the
  239. * protocol in <code>serviceURL</code> but it cannot be used for
  240. * some reason.
  241. */
  242. public static JMXConnectorServer
  243. newJMXConnectorServer(JMXServiceURL serviceURL,
  244. Map<String,?> environment,
  245. MBeanServer mbeanServer)
  246. throws IOException {
  247. if (environment == null)
  248. environment = new HashMap();
  249. else {
  250. EnvHelp.checkAttributes(environment);
  251. environment = new HashMap(environment);
  252. }
  253. final Class targetInterface = JMXConnectorServerProvider.class;
  254. final ClassLoader loader =
  255. JMXConnectorFactory.resolveClassLoader(environment);
  256. final String protocol = serviceURL.getProtocol();
  257. final String providerClassName = "ServerProvider";
  258. JMXConnectorServerProvider provider =
  259. (JMXConnectorServerProvider)
  260. JMXConnectorFactory.getProvider(serviceURL, environment,
  261. providerClassName,
  262. targetInterface, loader);
  263. if(provider == null) {
  264. // Loader is null when context class loader is set to null
  265. // and no loader has been provided in map.
  266. // com.sun.jmx.remote.util.Service class extracted from j2se
  267. // provider search algorithm doesn't handle well null classloader.
  268. if(loader != null) {
  269. JMXConnectorServer connection =
  270. getConnectorServerAsService(loader,
  271. serviceURL,
  272. environment,
  273. mbeanServer);
  274. if(connection != null)
  275. return connection;
  276. }
  277. provider = (JMXConnectorServerProvider)
  278. JMXConnectorFactory.getProvider(protocol,
  279. PROTOCOL_PROVIDER_DEFAULT_PACKAGE,
  280. JMXConnectorFactory.class.getClassLoader(),
  281. providerClassName,
  282. targetInterface);
  283. }
  284. if(provider == null)
  285. throw new MalformedURLException("Unsupported protocol: " +
  286. protocol);
  287. environment = Collections.unmodifiableMap(environment);
  288. return provider.newJMXConnectorServer(serviceURL, environment,
  289. mbeanServer);
  290. }
  291. }