KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > management > remote > JMXConnectorFactory


1 /*
2  * @(#)JMXConnectorFactory.java 1.42 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
8 package javax.management.remote;
9
10 import java.io.IOException JavaDoc;
11 import java.net.MalformedURLException JavaDoc;
12 import java.util.Collections JavaDoc;
13 import java.util.HashMap JavaDoc;
14 import java.util.Map JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.StringTokenizer JavaDoc;
17 import java.security.AccessController JavaDoc;
18 import java.security.PrivilegedAction JavaDoc;
19
20 import com.sun.jmx.remote.util.ClassLogger;
21 import com.sun.jmx.remote.util.EnvHelp;
22 import com.sun.jmx.remote.util.Service;
23
24 /**
25  * <p>Factory to create JMX API connector clients. There
26  * are no instances of this class.</p>
27  *
28  * <p>Connections are usually made using the {@link
29  * #connect(JMXServiceURL) connect} method of this class. More
30  * advanced applications can separate the creation of the connector
31  * client, using {@link #newJMXConnector(JMXServiceURL, Map)
32  * newJMXConnector} and the establishment of the connection itself, using
33  * {@link JMXConnector#connect(Map)}.</p>
34  *
35  * <p>Each client is created by an instance of {@link
36  * JMXConnectorProvider}. This instance is found as follows. Suppose
37  * the given {@link JMXServiceURL} looks like
38  * <code>"service:jmx:<em>protocol</em>:<em>remainder</em>"</code>.
39  * Then the factory will attempt to find the appropriate {@link
40  * JMXConnectorProvider} for <code><em>protocol</em></code>. Each
41  * occurrence of the character <code>+</code> or <code>-</code> in
42  * <code><em>protocol</em></code> is replaced by <code>.</code> or
43  * <code>_</code>, respectively.</p>
44  *
45  * <p>A <em>provider package list</em> is searched for as follows:</p>
46  *
47  * <ol>
48  *
49  * <li>If the <code>environment</code> parameter to {@link
50  * #newJMXConnector(JMXServiceURL, Map) newJMXConnector} contains the
51  * key <code>jmx.remote.protocol.provider.pkgs</code> then the
52  * associated value is the provider package list.
53  *
54  * <li>Otherwise, if the system property
55  * <code>jmx.remote.protocol.provider.pkgs</code> exists, then its value
56  * is the provider package list.
57  *
58  * <li>Otherwise, there is no provider package list.
59  *
60  * </ol>
61  *
62  * <p>The provider package list is a string that is interpreted as a
63  * list of non-empty Java package names separated by vertical bars
64  * (<code>|</code>). If the string is empty, then so is the provider
65  * package list. If the provider package list is not a String, or if
66  * it contains an element that is an empty string, a {@link
67  * JMXProviderException} is thrown.</p>
68  *
69  * <p>If the provider package list exists and is not empty, then for
70  * each element <code><em>pkg</em></code> of the list, the factory
71  * will attempt to load the class
72  *
73  * <blockquote>
74  * <code><em>pkg</em>.<em>protocol</em>.ClientProvider</code>
75  * </blockquote>
76
77  * <p>If the <code>environment</code> parameter to {@link
78  * #newJMXConnector(JMXServiceURL, Map) newJMXConnector} contains the
79  * key <code>jmx.remote.protocol.provider.class.loader</code> then the
80  * associated value is the class loader to use to load the provider.
81  * If the associated value is not an instance of {@link
82  * java.lang.ClassLoader}, an {@link
83  * java.lang.IllegalArgumentException} is thrown.</p>
84  *
85  * <p>If the <code>jmx.remote.protocol.provider.class.loader</code>
86  * key is not present in the <code>environment</code> parameter, the
87  * calling thread's context class loader is used.</p>
88  *
89  * <p>If the attempt to load this class produces a {@link
90  * ClassNotFoundException}, the search for a handler continues with
91  * the next element of the list.</p>
92  *
93  * <p>Otherwise, a problem with the provider found is signalled by a
94  * {@link JMXProviderException} whose {@link
95  * JMXProviderException#getCause() <em>cause</em>} indicates the underlying
96  * exception, as follows:</p>
97  *
98  * <ul>
99  *
100  * <li>if the attempt to load the class produces an exception other
101  * than <code>ClassNotFoundException</code>, that is the
102  * <em>cause</em>;
103  *
104  * <li>if {@link Class#newInstance()} for the class produces an
105  * exception, that is the <em>cause</em>.
106  *
107  * </ul>
108  *
109  * <p>If no provider is found by the above steps, including the
110  * default case where there is no provider package list, then the
111  * implementation will use its own provider for
112  * <code><em>protocol</em></code>, or it will throw a
113  * <code>MalformedURLException</code> if there is none. An
114  * implementation may choose to find providers by other means. For
115  * example, it may support the <a
116  * HREF="http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html#Service Provider">
117  * JAR conventions for service providers</a>, where the service
118  * interface is <code>JMXConnectorProvider</code>.</p>
119  *
120  * <p>Every implementation must support the RMI connector protocols,
121  * specified with the string <code>rmi</code> or
122  * <code>iiop</code>.</p>
123  *
124  * <p>Once a provider is found, the result of the
125  * <code>newJMXConnector</code> method is the result of calling {@link
126  * JMXConnectorProvider#newJMXConnector(JMXServiceURL,Map) newJMXConnector}
127  * on the provider.</p>
128  *
129  * <p>The <code>Map</code> parameter passed to the
130  * <code>JMXConnectorProvider</code> is a new read-only
131  * <code>Map</code> that contains all the entries that were in the
132  * <code>environment</code> parameter to {@link
133  * #newJMXConnector(JMXServiceURL,Map)
134  * JMXConnectorFactory.newJMXConnector}, if there was one.
135  * Additionally, if the
136  * <code>jmx.remote.protocol.provider.class.loader</code> key is not
137  * present in the <code>environment</code> parameter, it is added to
138  * the new read-only <code>Map</code>. The associated value is the
139  * calling thread's context class loader.</p>
140  *
141  * @since 1.5
142  * @since.unbundled 1.0
143  */

144 public class JMXConnectorFactory {
145
146     /**
147      * <p>Name of the attribute that specifies the default class
148      * loader. This class loader is used to deserialize return values and
149      * exceptions from remote <code>MBeanServerConnection</code>
150      * calls. The value associated with this attribute is an instance
151      * of {@link ClassLoader}.</p>
152      */

153     public static final String JavaDoc DEFAULT_CLASS_LOADER =
154         "jmx.remote.default.class.loader";
155
156     /**
157      * <p>Name of the attribute that specifies the provider packages
158      * that are consulted when looking for the handler for a protocol.
159      * The value associated with this attribute is a string with
160      * package names separated by vertical bars (<code>|</code>).</p>
161      */

162     public static final String JavaDoc PROTOCOL_PROVIDER_PACKAGES =
163         "jmx.remote.protocol.provider.pkgs";
164
165     /**
166      * <p>Name of the attribute that specifies the class
167      * loader for loading protocol providers.
168      * The value associated with this attribute is an instance
169      * of {@link ClassLoader}.</p>
170      */

171     public static final String JavaDoc PROTOCOL_PROVIDER_CLASS_LOADER =
172         "jmx.remote.protocol.provider.class.loader";
173
174     private static final String JavaDoc PROTOCOL_PROVIDER_DEFAULT_PACKAGE =
175     "com.sun.jmx.remote.protocol";
176
177     private static final ClassLogger logger =
178     new ClassLogger("javax.management.remote.misc", "JMXConnectorFactory");
179
180     /** There are no instances of this class. */
181     private JMXConnectorFactory() {
182     }
183
184     /**
185      * <p>Creates a connection to the connector server at the given
186      * address.</p>
187      *
188      * <p>This method is equivalent to {@link
189      * #connect(JMXServiceURL,Map) connect(serviceURL, null)}.</p>
190      *
191      * @param serviceURL the address of the connector server to
192      * connect to.
193      *
194      * @return a <code>JMXConnector</code> whose {@link
195      * JMXConnector#connect connect} method has been called.
196      *
197      * @exception NullPointerException if <code>serviceURL</code> is null.
198      *
199      * @exception IOException if the connector client or the
200      * connection cannot be made because of a communication problem.
201      *
202      * @exception SecurityException if the connection cannot be made
203      * for security reasons.
204      */

205     public static JMXConnector JavaDoc connect(JMXServiceURL JavaDoc serviceURL)
206             throws IOException JavaDoc {
207         return connect(serviceURL, null);
208     }
209
210     /**
211      * <p>Creates a connection to the connector server at the given
212      * address.</p>
213      *
214      * <p>This method is equivalent to:</p>
215      *
216      * <pre>
217      * JMXConnector conn = JMXConnectorFactory.newJMXConnector(serviceURL,
218      * environment);
219      * conn.connect(environment);
220      * </pre>
221      *
222      * @param serviceURL the address of the connector server to connect to.
223      *
224      * @param environment a set of attributes to determine how the
225      * connection is made. This parameter can be null. Keys in this
226      * map must be Strings. The appropriate type of each associated
227      * value depends on the attribute. The contents of
228      * <code>environment</code> are not changed by this call.
229      *
230      * @return a <code>JMXConnector</code> representing the newly-made
231      * connection. Each successful call to this method produces a
232      * different object.
233      *
234      * @exception NullPointerException if <code>serviceURL</code> is null.
235      *
236      * @exception IOException if the connector client or the
237      * connection cannot be made because of a communication problem.
238      *
239      * @exception SecurityException if the connection cannot be made
240      * for security reasons.
241      */

242     public static JMXConnector JavaDoc connect(JMXServiceURL JavaDoc serviceURL,
243                                        Map JavaDoc<String JavaDoc,?> environment)
244             throws IOException JavaDoc {
245     if (serviceURL == null)
246         throw new NullPointerException JavaDoc("Null JMXServiceURL");
247         JMXConnector JavaDoc conn = newJMXConnector(serviceURL, environment);
248         conn.connect(environment);
249         return conn;
250     }
251
252     /**
253      * <p>Creates a connector client for the connector server at the
254      * given address. The resultant client is not connected until its
255      * {@link JMXConnector#connect(Map) connect} method is called.</p>
256      *
257      * @param serviceURL the address of the connector server to connect to.
258      *
259      * @param environment a set of attributes to determine how the
260      * connection is made. This parameter can be null. Keys in this
261      * map must be Strings. The appropriate type of each associated
262      * value depends on the attribute. The contents of
263      * <code>environment</code> are not changed by this call.
264      *
265      * @return a <code>JMXConnector</code> representing the new
266      * connector client. Each successful call to this method produces
267      * a different object.
268      *
269      * @exception NullPointerException if <code>serviceURL</code> is null.
270      *
271      * @exception IOException if the connector client cannot be made
272      * because of a communication problem.
273      *
274      * @exception MalformedURLException if there is no provider for the
275      * protocol in <code>serviceURL</code>.
276      *
277      * @exception JMXProviderException if there is a provider for the
278      * protocol in <code>serviceURL</code> but it cannot be used for
279      * some reason.
280      */

281     public static JMXConnector JavaDoc newJMXConnector(JMXServiceURL JavaDoc serviceURL,
282                            Map JavaDoc<String JavaDoc,?> environment)
283             throws IOException JavaDoc {
284         if (environment == null)
285             environment = new HashMap JavaDoc();
286         else {
287         EnvHelp.checkAttributes(environment);
288             environment = new HashMap JavaDoc(environment);
289     }
290     
291     final ClassLoader JavaDoc loader = resolveClassLoader(environment);
292     final Class JavaDoc targetInterface = JMXConnectorProvider JavaDoc.class;
293     final String JavaDoc protocol = serviceURL.getProtocol();
294     final String JavaDoc providerClassName = "ClientProvider";
295     
296     JMXConnectorProvider JavaDoc provider =
297         (JMXConnectorProvider JavaDoc) getProvider(serviceURL, environment,
298                            providerClassName,
299                            targetInterface,
300                            loader);
301     if(provider == null) {
302         // Loader is null when context class loader is set to null
303
// and no loader has been provided in map.
304
// com.sun.jmx.remote.util.Service class extracted from j2se
305
// provider search algorithm doesn't handle well null classloader.
306
if(loader != null) {
307         JMXConnector JavaDoc connection = getConnectorAsService(loader,
308                                 serviceURL,
309                                 environment);
310         if(connection != null)
311             return connection;
312         }
313         
314         provider = (JMXConnectorProvider JavaDoc)
315         getProvider(protocol, PROTOCOL_PROVIDER_DEFAULT_PACKAGE,
316                 JMXConnectorFactory JavaDoc.class.getClassLoader(),
317                 providerClassName, targetInterface);
318     }
319     
320     if(provider == null)
321         throw new MalformedURLException JavaDoc("Unsupported protocol: " +
322                         protocol);
323     
324         environment = Collections.unmodifiableMap(environment);
325     
326         return provider.newJMXConnector(serviceURL, environment);
327     }
328
329     private static String JavaDoc resolvePkgs(Map JavaDoc env) throws JMXProviderException JavaDoc {
330
331         Object JavaDoc pkgsObject = null;
332
333         if (env != null)
334             pkgsObject = env.get(PROTOCOL_PROVIDER_PACKAGES);
335
336         if (pkgsObject == null)
337             pkgsObject =
338         AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
339             public Object JavaDoc run() {
340             return System.getProperty(PROTOCOL_PROVIDER_PACKAGES);
341             }
342         });
343
344     if (pkgsObject == null)
345         return null;
346
347     if (!(pkgsObject instanceof String JavaDoc)) {
348         final String JavaDoc msg = "Value of " + PROTOCOL_PROVIDER_PACKAGES +
349         " parameter is not a String: " +
350         pkgsObject.getClass().getName();
351         throw new JMXProviderException JavaDoc(msg);
352     }
353
354     final String JavaDoc pkgs = (String JavaDoc) pkgsObject;
355     if (pkgs.trim().equals(""))
356         return null;
357
358     // pkgs may not contain an empty element
359
if (pkgs.startsWith("|") || pkgs.endsWith("|") ||
360         pkgs.indexOf("||") >= 0) {
361         final String JavaDoc msg = "Value of " + PROTOCOL_PROVIDER_PACKAGES +
362         " contains an empty element: " + pkgs;
363         throw new JMXProviderException JavaDoc(msg);
364     }
365
366     return pkgs;
367     }
368
369     static Object JavaDoc getProvider(JMXServiceURL JavaDoc serviceURL,
370                   Map JavaDoc environment, String JavaDoc providerClassName,
371                   Class JavaDoc targetInterface,
372                   ClassLoader JavaDoc loader)
373             throws IOException JavaDoc {
374
375         final String JavaDoc protocol = serviceURL.getProtocol();
376     
377         final String JavaDoc pkgs = resolvePkgs(environment);
378
379     Object JavaDoc instance = null;
380     
381     if (pkgs != null) {
382         environment.put(PROTOCOL_PROVIDER_CLASS_LOADER, loader);
383         
384         instance =
385         getProvider(protocol, pkgs, loader, providerClassName,
386                 targetInterface);
387     }
388
389     return instance;
390     }
391     
392     static Iterator JavaDoc getProviderIterator(final Class JavaDoc providerClass,
393                     final ClassLoader JavaDoc loader) {
394     PrivilegedAction JavaDoc action = new PrivilegedAction JavaDoc() {
395         public Object JavaDoc run() {
396             return Service.providers(providerClass, loader);
397         }
398         };
399     return (Iterator JavaDoc) AccessController.doPrivileged(action);
400     }
401
402     private static JMXConnector JavaDoc getConnectorAsService(ClassLoader JavaDoc loader,
403                               JMXServiceURL JavaDoc url,
404                               Map JavaDoc map)
405     throws IllegalArgumentException JavaDoc, JMXProviderException JavaDoc {
406
407     Iterator JavaDoc providers = getProviderIterator(JMXConnectorProvider JavaDoc.class,
408                          loader);
409     JMXConnectorProvider JavaDoc provider = null;
410     JMXConnector JavaDoc connection = null;
411     while(providers.hasNext()) {
412         provider =
413         (JMXConnectorProvider JavaDoc) providers.next();
414         try {
415         connection = provider.newJMXConnector(url, map);
416         return connection;
417         } catch (JMXProviderException JavaDoc e) {
418         throw e;
419         }
420         catch (Exception JavaDoc e) {
421         if (logger.traceOn())
422             logger.trace("getConnectorAsService",
423                  "URL[" + url +
424                  "] Service provider exception: " + e);
425         continue;
426         }
427     }
428     return null;
429     }
430     
431     static Object JavaDoc getProvider(String JavaDoc protocol,
432                   String JavaDoc pkgs,
433                   ClassLoader JavaDoc loader,
434                   String JavaDoc providerClassName,
435                   Class JavaDoc targetInterface)
436         throws IOException JavaDoc {
437
438         StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(pkgs, "|");
439
440         while (tokenizer.hasMoreTokens()) {
441             String JavaDoc pkg = tokenizer.nextToken();
442             String JavaDoc className = (pkg + "." + protocol2package(protocol) +
443                                 "." + providerClassName);
444         Class JavaDoc providerClass;
445             try {
446                 providerClass = Class.forName(className, true, loader);
447             } catch (ClassNotFoundException JavaDoc e) {
448                 //Add trace.
449
continue;
450             }
451
452         if (!targetInterface.isAssignableFrom(providerClass)) {
453                 final String JavaDoc msg =
454                     "Provider class does not implement " +
455                     targetInterface.getName() + ": " +
456                     providerClass.getName();
457                 throw new JMXProviderException JavaDoc(msg);
458             }
459
460             try {
461                 return providerClass.newInstance();
462             } catch (Exception JavaDoc e) {
463                 final String JavaDoc msg =
464                     "Exception when instantiating provider [" + className +
465                     "]";
466                 throw new JMXProviderException JavaDoc(msg, e);
467             }
468         }
469
470     return null;
471     }
472
473     static ClassLoader JavaDoc resolveClassLoader(Map JavaDoc environment) {
474         ClassLoader JavaDoc loader = null;
475
476         if (environment != null) {
477             try {
478                 loader = (ClassLoader JavaDoc)
479                     environment.get(PROTOCOL_PROVIDER_CLASS_LOADER);
480             } catch (ClassCastException JavaDoc e) {
481                 final String JavaDoc msg =
482                     "The ClassLoader supplied in the environment map using " +
483             "the " + PROTOCOL_PROVIDER_CLASS_LOADER +
484             " attribute is not an instance of java.lang.ClassLoader";
485                 throw new IllegalArgumentException JavaDoc(msg);
486             }
487         }
488
489         if (loader == null)
490         loader = (ClassLoader JavaDoc)
491         AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
492             public Object JavaDoc run() {
493                 return
494                 Thread.currentThread().getContextClassLoader();
495             }
496             });
497
498         return loader;
499     }
500     
501     private static String JavaDoc protocol2package(String JavaDoc protocol) {
502         return protocol.replace('+', '.').replace('-', '_');
503     }
504 }
505
Popular Tags