KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > jmx > remote > util > EnvHelp


1 /*
2  * @(#)EnvHelp.java 1.36 05/08/31
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package com.sun.jmx.remote.util;
9
10 import java.io.IOException JavaDoc;
11 import java.io.ObjectOutputStream JavaDoc;
12 import java.io.OutputStream JavaDoc;
13 import java.util.Collection JavaDoc;
14 import java.util.HashMap JavaDoc;
15 import java.util.Hashtable JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.Map JavaDoc;
18 import java.util.SortedMap JavaDoc;
19 import java.util.SortedSet JavaDoc;
20 import java.util.StringTokenizer JavaDoc;
21 import java.util.TreeMap JavaDoc;
22 import java.util.TreeSet JavaDoc;
23
24 import java.security.AccessController JavaDoc;
25 import java.security.PrivilegedAction JavaDoc;
26 import java.security.PrivilegedActionException JavaDoc;
27 import java.security.PrivilegedExceptionAction JavaDoc;
28
29 import javax.management.ObjectName JavaDoc;
30 import javax.management.MBeanServer JavaDoc;
31 import javax.management.InstanceNotFoundException JavaDoc;
32 import javax.management.remote.JMXConnectorFactory JavaDoc;
33 import javax.management.remote.JMXConnectorServerFactory JavaDoc;
34 import com.sun.jmx.mbeanserver.GetPropertyAction;
35
36 public class EnvHelp {
37
38     /**
39      * <p>Name of the attribute that specifies a default class loader
40      * object.
41      * The value associated with this attribute is a ClassLoader object</p>
42      */

43     private static final String JavaDoc DEFAULT_CLASS_LOADER =
44         JMXConnectorFactory.DEFAULT_CLASS_LOADER;
45
46     /**
47      * <p>Name of the attribute that specifies a default class loader
48      * ObjectName.
49      * The value associated with this attribute is an ObjectName object</p>
50      */

51     private static final String JavaDoc DEFAULT_CLASS_LOADER_NAME =
52         JMXConnectorServerFactory.DEFAULT_CLASS_LOADER_NAME;
53
54     /**
55      * Get the Connector Server default class loader.
56      * <p>
57      * Returns:
58      * <p>
59      * <ul>
60      * <li>
61      * The ClassLoader object found in <var>env</var> for
62      * <tt>jmx.remote.default.class.loader</tt>, if any.
63      * </li>
64      * <li>
65      * The ClassLoader pointed to by the ObjectName found in
66      * <var>env</var> for <tt>jmx.remote.default.class.loader.name</tt>,
67      * and registered in <var>mbs</var> if any.
68      * </li>
69      * <li>
70      * The current thread's context classloader otherwise.
71      * </li>
72      * </ul>
73      *
74      * @param env Environment attributes.
75      * @param mbs The MBeanServer for which the connector server provides
76      * remote access.
77      *
78      * @return the connector server's default class loader.
79      *
80      * @exception IllegalArgumentException if one of the following is true:
81      * <ul>
82      * <li>both
83      * <tt>jmx.remote.default.class.loader</tt> and
84      * <tt>jmx.remote.default.class.loader.name</tt> are specified,
85      * </li>
86      * <li>or
87      * <tt>jmx.remote.default.class.loader</tt> is not
88      * an instance of {@link ClassLoader},
89      * </li>
90      * <li>or
91      * <tt>jmx.remote.default.class.loader.name</tt> is not
92      * an instance of {@link ObjectName},
93      * </li>
94      * <li>or
95      * <tt>jmx.remote.default.class.loader.name</tt> is specified
96      * but <var>mbs</var> is null.
97      * </li>
98      * @exception InstanceNotFoundException if
99      * <tt>jmx.remote.default.class.loader.name</tt> is specified
100      * and the ClassLoader MBean is not found in <var>mbs</var>.
101      */

102     public static ClassLoader JavaDoc resolveServerClassLoader(Map JavaDoc env,
103                                                        MBeanServer JavaDoc mbs)
104         throws InstanceNotFoundException JavaDoc {
105
106         if (env == null)
107             return Thread.currentThread().getContextClassLoader();
108
109         Object JavaDoc loader = env.get(DEFAULT_CLASS_LOADER);
110         Object JavaDoc name = env.get(DEFAULT_CLASS_LOADER_NAME);
111
112         if (loader != null && name != null) {
113             final String JavaDoc msg = "Only one of " +
114                 DEFAULT_CLASS_LOADER + " or " +
115                 DEFAULT_CLASS_LOADER_NAME +
116                 " should be specified.";
117             throw new IllegalArgumentException JavaDoc(msg);
118         }
119
120         if (loader == null && name == null)
121             return Thread.currentThread().getContextClassLoader();
122
123         if (loader != null) {
124             if (loader instanceof ClassLoader JavaDoc) {
125                 return (ClassLoader JavaDoc) loader;
126             } else {
127                 final String JavaDoc msg =
128                     "ClassLoader object is not an instance of " +
129                     ClassLoader JavaDoc.class.getName() + " : " +
130                     loader.getClass().getName();
131                 throw new IllegalArgumentException JavaDoc(msg);
132             }
133         }
134
135         ObjectName JavaDoc on;
136         if (name instanceof ObjectName JavaDoc) {
137             on = (ObjectName JavaDoc) name;
138         } else {
139             final String JavaDoc msg =
140                 "ClassLoader name is not an instance of " +
141                 ObjectName JavaDoc.class.getName() + " : " +
142                 name.getClass().getName();
143             throw new IllegalArgumentException JavaDoc(msg);
144         }
145
146         if (mbs == null)
147             throw new IllegalArgumentException JavaDoc("Null MBeanServer object");
148
149         return mbs.getClassLoader(on);
150     }
151
152     /**
153      * Get the Connector Client default class loader.
154      * <p>
155      * Returns:
156      * <p>
157      * <ul>
158      * <li>
159      * The ClassLoader object found in <var>env</var> for
160      * <tt>jmx.remote.default.class.loader</tt>, if any.
161      * </li>
162      * <li>The <tt>Thread.currentThread().getContextClassLoader()</tt>
163      * otherwise.
164      * </li>
165      * </ul>
166      * <p>
167      * Usually a Connector Client will call
168      * <pre>
169      * ClassLoader dcl = EnvHelp.resolveClientClassLoader(env);
170      * </pre>
171      * in its <tt>connect(Map env)</tt> method.
172      *
173      * @return The connector client default class loader.
174      *
175      * @exception IllegalArgumentException if
176      * <tt>jmx.remote.default.class.loader</tt> is specified
177      * and is not an instance of {@link ClassLoader}.
178      */

179     public static ClassLoader JavaDoc resolveClientClassLoader(Map JavaDoc env) {
180
181         if (env == null)
182             return Thread.currentThread().getContextClassLoader();
183
184         Object JavaDoc loader = env.get(DEFAULT_CLASS_LOADER);
185
186         if (loader == null)
187             return Thread.currentThread().getContextClassLoader();
188
189         if (loader instanceof ClassLoader JavaDoc) {
190             return (ClassLoader JavaDoc) loader;
191         } else {
192             final String JavaDoc msg =
193                 "ClassLoader object is not an instance of " +
194                 ClassLoader JavaDoc.class.getName() + " : " +
195                 loader.getClass().getName();
196             throw new IllegalArgumentException JavaDoc(msg);
197         }
198     }
199
200     /**
201      * Initialise the cause field of a {@code Throwable} object.
202      *
203      * @param throwable The {@code Throwable} on which the cause is set.
204      * @param cause The cause to set on the supplied {@code Throwable}.
205      * @return the {@code Throwable} with the cause field initialised.
206      */

207     public static <T extends Throwable JavaDoc> T initCause(T throwable,
208                             Throwable JavaDoc cause) {
209     throwable.initCause(cause);
210     return throwable;
211     }
212
213     /**
214      * Returns the cause field of a Throwable object.
215      * The cause field can be got only if <var>t</var> has an
216      * {@link Throwable#getCause()} method (JDK Version >= 1.4)
217      * @param t Throwable on which the cause must be set.
218      * @return the cause if getCause() succeeded and the got value is not
219      * null, otherwise return the <var>t</var>.
220      */

221     public static Throwable JavaDoc getCause(Throwable JavaDoc t) {
222         Throwable JavaDoc ret = t;
223
224         try {
225             java.lang.reflect.Method JavaDoc getCause =
226         t.getClass().getMethod("getCause", (Class JavaDoc[]) null);
227             ret = (Throwable JavaDoc)getCause.invoke(t, (Object JavaDoc[]) null);
228
229         } catch (Exception JavaDoc e) {
230         // OK.
231
// it must be older than 1.4.
232
}
233         return (ret != null) ? ret: t;
234     }
235
236
237     /**
238      * <p>Name of the attribute that specifies the size of a notification
239      * buffer for a connector server. The default value is 1000.
240      */

241     public static final String JavaDoc BUFFER_SIZE_PROPERTY =
242     "jmx.remote.x.notification.buffer.size";
243
244
245     /**
246      * Returns the size of a notification buffer for a connector server.
247      * The default value is 1000.
248      */

249     public static int getNotifBufferSize(Map JavaDoc env) {
250     int defaultQueueSize = 1000; // default value
251

252     // keep it for the compability for the fix:
253
// 6174229: Environment parameter should be notification.buffer.size
254
// instead of buffer.size
255
final String JavaDoc oldP = "jmx.remote.x.buffer.size";
256
257     // the default value re-specified in the system
258
try {
259         GetPropertyAction act = new GetPropertyAction(BUFFER_SIZE_PROPERTY);
260         String JavaDoc s = (String JavaDoc)AccessController.doPrivileged(act);
261         if (s != null) {
262         defaultQueueSize = Integer.parseInt(s);
263         } else { // try the old one
264
act = new GetPropertyAction(oldP);
265         s = (String JavaDoc)AccessController.doPrivileged(act);
266         if (s != null) {
267             defaultQueueSize = Integer.parseInt(s);
268         }
269         }
270     } catch (RuntimeException JavaDoc e) {
271         logger.warning("getNotifBufferSize",
272                "Can't use System property "+
273                BUFFER_SIZE_PROPERTY+ ": " + e);
274               logger.debug("getNotifBufferSize", e);
275     }
276
277     int queueSize = defaultQueueSize;
278
279     try {
280         if (env.containsKey(BUFFER_SIZE_PROPERTY)) {
281         queueSize = (int)EnvHelp.getIntegerAttribute(env,BUFFER_SIZE_PROPERTY,
282                         defaultQueueSize,0,
283                         Integer.MAX_VALUE);
284         } else { // try the old one
285
queueSize = (int)EnvHelp.getIntegerAttribute(env,oldP,
286                         defaultQueueSize,0,
287                         Integer.MAX_VALUE);
288         }
289     } catch (RuntimeException JavaDoc e) {
290         logger.warning("getNotifBufferSize",
291                "Can't determine queuesize (using default): "+
292                e);
293         logger.debug("getNotifBufferSize", e);
294     }
295
296     return queueSize;
297     }
298
299     /**
300      * <p>Name of the attribute that specifies the maximum number of
301      * notifications that a client will fetch from its server.. The
302      * value associated with this attribute should be an
303      * <code>Integer</code> object. The default value is 1000.</p>
304      */

305     public static final String JavaDoc MAX_FETCH_NOTIFS =
306         "jmx.remote.x.notification.fetch.max";
307
308     /**
309      * Returns the maximum notification number which a client will
310      * fetch every time.
311      */

312     public static int getMaxFetchNotifNumber(Map JavaDoc env) {
313     return (int) getIntegerAttribute(env, MAX_FETCH_NOTIFS, 1000, 1,
314                      Integer.MAX_VALUE);
315     }
316
317     /**
318      * <p>Name of the attribute that specifies the timeout for a
319      * client to fetch notifications from its server. The value
320      * associated with this attribute should be a <code>Long</code>
321      * object. The default value is 60000 milleseconds.</p>
322      */

323     public static final String JavaDoc FETCH_TIMEOUT =
324         "jmx.remote.x.notification.fetch.timeout";
325
326     /**
327      * Returns the timeout for a client to fetch notifications.
328      */

329     public static long getFetchTimeout(Map JavaDoc env) {
330     return getIntegerAttribute(env, FETCH_TIMEOUT, 60000L, 0,
331                    Long.MAX_VALUE);
332     }
333
334     /**
335      * Get an integer-valued attribute with name <code>name</code>
336      * from <code>env</code>. If <code>env</code> is null, or does
337      * not contain an entry for <code>name</code>, return
338      * <code>defaultValue</code>. The value may be a Number, or it
339      * may be a String that is parsable as a long. It must be at
340      * least <code>minValue</code> and at most<code>maxValue</code>.
341      *
342      * @throws IllegalArgumentException if <code>env</code> contains
343      * an entry for <code>name</code> but it does not meet the
344      * constraints above.
345      */

346     public static long getIntegerAttribute(Map JavaDoc env, String JavaDoc name,
347                        long defaultValue, long minValue,
348                        long maxValue) {
349     final Object JavaDoc o;
350
351     if (env == null || (o = env.get(name)) == null)
352         return defaultValue;
353
354     final long result;
355
356     if (o instanceof Number JavaDoc)
357         result = ((Number JavaDoc) o).longValue();
358     else if (o instanceof String JavaDoc) {
359         result = Long.parseLong((String JavaDoc) o);
360         /* May throw a NumberFormatException, which is an
361            IllegalArgumentException. */

362     } else {
363         final String JavaDoc msg =
364         "Attribute " + name + " value must be Integer or String: " + o;
365         throw new IllegalArgumentException JavaDoc(msg);
366     }
367
368     if (result < minValue) {
369         final String JavaDoc msg =
370         "Attribute " + name + " value must be at least " + minValue +
371         ": " + result;
372         throw new IllegalArgumentException JavaDoc(msg);
373     }
374
375     if (result > maxValue) {
376         final String JavaDoc msg =
377         "Attribute " + name + " value must be at most " + maxValue +
378         ": " + result;
379         throw new IllegalArgumentException JavaDoc(msg);
380     }
381
382         return result;
383     }
384
385     public static final String JavaDoc DEFAULT_ORB="java.naming.corba.orb";
386
387     /* Check that all attributes have a key that is a String.
388        Could make further checks, e.g. appropriate types for attributes. */

389     public static void checkAttributes(Map JavaDoc attributes) {
390         for (Iterator JavaDoc it = attributes.keySet().iterator(); it.hasNext(); ) {
391             Object JavaDoc key = it.next();
392             if (!(key instanceof String JavaDoc)) {
393                 final String JavaDoc msg =
394                     "Attributes contain key that is not a string: " + key;
395                 throw new IllegalArgumentException JavaDoc(msg);
396             }
397         }
398     }
399
400     /* Return a writable map containing only those attributes that are
401        serializable, and that are not hidden by
402        jmx.remote.x.hidden.attributes or the default list of hidden
403        attributes. */

404     public static Map JavaDoc filterAttributes(Map JavaDoc attributes) {
405         if (logger.traceOn()) {
406             logger.trace("filterAttributes", "starts");
407         }
408
409         SortedMap JavaDoc map = new TreeMap JavaDoc(attributes);
410     purgeUnserializable(map.values());
411     hideAttributes(map);
412         return map;
413     }
414
415     /**
416      * Remove from the given Collection any element that is not a
417      * serializable object.
418      */

419     private static void purgeUnserializable(Collection JavaDoc objects) {
420     logger.trace("purgeUnserializable", "starts");
421     ObjectOutputStream JavaDoc oos = null;
422     int i = 0;
423     for (Iterator JavaDoc it = objects.iterator(); it.hasNext(); i++) {
424         Object JavaDoc v = it.next();
425
426         if (v == null || v instanceof String JavaDoc) {
427         if (logger.traceOn()) {
428             logger.trace("purgeUnserializable",
429                  "Value trivially serializable: " + v);
430         }
431         continue;
432         }
433
434         try {
435         if (oos == null)
436             oos = new ObjectOutputStream JavaDoc(new SinkOutputStream());
437         oos.writeObject(v);
438         if (logger.traceOn()) {
439             logger.trace("purgeUnserializable",
440                  "Value serializable: " + v);
441         }
442         } catch (IOException JavaDoc e) {
443         if (logger.traceOn()) {
444             logger.trace("purgeUnserializable",
445                  "Value not serializable: " + v + ": " +
446                  e);
447         }
448         it.remove();
449         oos = null; // ObjectOutputStream invalid after exception
450
}
451     }
452     }
453
454     /**
455        The value of this attribute, if present, is a string specifying
456        what other attributes should not appear in
457        JMXConnectorServer.getAttributes(). It is a space-separated
458        list of attribute patterns, where each pattern is either an
459        attribute name, or an attribute prefix followed by a "*"
460        character. The "*" has no special significance anywhere except
461        at the end of a pattern. By default, this list is added to the
462        list defined by {@link #DEFAULT_HIDDEN_ATTRIBUTES} (which
463        uses the same format). If the value of this attribute begins
464        with an "=", then the remainder of the string defines the
465        complete list of attribute patterns.
466      */

467     public static final String JavaDoc HIDDEN_ATTRIBUTES =
468     "jmx.remote.x.hidden.attributes";
469
470     /**
471        Default list of attributes not to show.
472        @see #HIDDEN_ATTRIBUTES
473      */

474     /* This list is copied directly from the spec, plus
475        java.naming.security.*. Most of the attributes here would have
476        been eliminated from the map anyway because they are typically
477        not serializable. But just in case they are, we list them here
478        to conform to the spec. */

479     public static final String JavaDoc DEFAULT_HIDDEN_ATTRIBUTES =
480     "java.naming.security.* " +
481     "jmx.remote.authenticator " +
482     "jmx.remote.context " +
483     "jmx.remote.default.class.loader " +
484     "jmx.remote.message.connection.server " +
485     "jmx.remote.object.wrapping " +
486     "jmx.remote.rmi.client.socket.factory " +
487     "jmx.remote.rmi.server.socket.factory " +
488     "jmx.remote.sasl.callback.handler " +
489     "jmx.remote.tls.socket.factory " +
490     "jmx.remote.x.access.file " +
491     "jmx.remote.x.password.file ";
492
493     private static final SortedSet JavaDoc defaultHiddenStrings = new TreeSet JavaDoc();
494     private static final SortedSet JavaDoc defaultHiddenPrefixes = new TreeSet JavaDoc();
495
496     private static void hideAttributes(SortedMap JavaDoc map) {
497     if (map.isEmpty())
498         return;
499
500     final SortedSet JavaDoc hiddenStrings;
501     final SortedSet JavaDoc hiddenPrefixes;
502
503     String JavaDoc hide = (String JavaDoc) map.get(HIDDEN_ATTRIBUTES);
504     if (hide != null) {
505         if (hide.startsWith("="))
506         hide = hide.substring(1);
507         else
508         hide += " " + DEFAULT_HIDDEN_ATTRIBUTES;
509         hiddenStrings = new TreeSet JavaDoc();
510         hiddenPrefixes = new TreeSet JavaDoc();
511         parseHiddenAttributes(hide, hiddenStrings, hiddenPrefixes);
512     } else {
513         hide = DEFAULT_HIDDEN_ATTRIBUTES;
514         synchronized (defaultHiddenStrings) {
515         if (defaultHiddenStrings.isEmpty()) {
516             parseHiddenAttributes(hide,
517                       defaultHiddenStrings,
518                       defaultHiddenPrefixes);
519         }
520         hiddenStrings = defaultHiddenStrings;
521         hiddenPrefixes = defaultHiddenPrefixes;
522         }
523     }
524
525     /* Construct a string that is greater than any key in the map.
526        Setting a string-to-match or a prefix-to-match to this string
527        guarantees that we will never call next() on the corresponding
528        iterator. */

529     String JavaDoc sentinelKey = map.lastKey() + "X";
530     Iterator JavaDoc keyIterator = map.keySet().iterator();
531     Iterator JavaDoc stringIterator = hiddenStrings.iterator();
532     Iterator JavaDoc prefixIterator = hiddenPrefixes.iterator();
533
534     String JavaDoc nextString;
535     if (stringIterator.hasNext())
536         nextString = (String JavaDoc) stringIterator.next();
537     else
538         nextString = sentinelKey;
539     String JavaDoc nextPrefix;
540     if (prefixIterator.hasNext())
541         nextPrefix = (String JavaDoc) prefixIterator.next();
542     else
543         nextPrefix = sentinelKey;
544
545     /* Read each key in sorted order and, if it matches a string
546        or prefix, remove it. */

547     keys:
548     while (keyIterator.hasNext()) {
549         String JavaDoc key = (String JavaDoc) keyIterator.next();
550
551         /* Continue through string-match values until we find one
552            that is either greater than the current key, or equal
553            to it. In the latter case, remove the key. */

554         int cmp = +1;
555         while ((cmp = nextString.compareTo(key)) < 0) {
556         if (stringIterator.hasNext())
557             nextString = (String JavaDoc) stringIterator.next();
558         else
559             nextString = sentinelKey;
560         }
561         if (cmp == 0) {
562         keyIterator.remove();
563         continue keys;
564         }
565
566         /* Continue through the prefix values until we find one
567            that is either greater than the current key, or a
568            prefix of it. In the latter case, remove the key. */

569         while (nextPrefix.compareTo(key) <= 0) {
570         if (key.startsWith(nextPrefix)) {
571             keyIterator.remove();
572             continue keys;
573         }
574         if (prefixIterator.hasNext())
575             nextPrefix = (String JavaDoc) prefixIterator.next();
576         else
577             nextPrefix = sentinelKey;
578         }
579     }
580     }
581
582     private static void parseHiddenAttributes(String JavaDoc hide,
583                           SortedSet JavaDoc hiddenStrings,
584                           SortedSet JavaDoc hiddenPrefixes) {
585     final StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(hide);
586     while (tok.hasMoreTokens()) {
587         String JavaDoc s = tok.nextToken();
588         if (s.endsWith("*"))
589         hiddenPrefixes.add(s.substring(0, s.length() - 1));
590         else
591         hiddenStrings.add(s);
592     }
593     }
594
595     /**
596      * <p>Name of the attribute that specifies the timeout to keep a
597      * server side connection after answering last client request.
598      * The default value is 120000 milliseconds.</p>
599      */

600     public static final String JavaDoc SERVER_CONNECTION_TIMEOUT =
601     "jmx.remote.x.server.connection.timeout";
602
603     /**
604      * Returns the server side connection timeout.
605      */

606     public static long getServerConnectionTimeout(Map JavaDoc env) {
607     return getIntegerAttribute(env, SERVER_CONNECTION_TIMEOUT, 120000L,
608                    0, Long.MAX_VALUE);
609     }
610
611     /**
612      * <p>Name of the attribute that specifies the period in
613      * millisecond for a client to check its connection. The default
614      * value is 60000 milliseconds.</p>
615      */

616     public static final String JavaDoc CLIENT_CONNECTION_CHECK_PERIOD =
617     "jmx.remote.x.client.connection.check.period";
618
619     /**
620      * Returns the client connection check oeriod.
621      */

622     public static long getConnectionCheckPeriod(Map JavaDoc env) {
623     return getIntegerAttribute(env, CLIENT_CONNECTION_CHECK_PERIOD, 60000L,
624                    0, Long.MAX_VALUE);
625     }
626
627     /**
628      * Converts a map into a valid hash table, i.e.
629      * it removes all the 'null' values from the map.
630      */

631     public static Hashtable JavaDoc mapToHashtable(Map JavaDoc map) {
632         HashMap JavaDoc m = new HashMap JavaDoc(map);
633         if (m.containsKey(null)) m.remove(null);
634         for (Iterator JavaDoc i = m.values().iterator(); i.hasNext(); )
635             if (i.next() == null) i.remove();
636         return new Hashtable JavaDoc(m);
637     }
638
639     private static final class SinkOutputStream extends OutputStream JavaDoc {
640     public void write(byte[] b, int off, int len) {}
641     public void write(int b) {}
642     }
643
644     private static final ClassLogger logger =
645     new ClassLogger("javax.management.remote.misc", "EnvHelp");
646 }
647
Popular Tags