jdk/src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java
changeset 1156 bbc2d15aaf7a
child 1222 78e3d021d528
equal deleted inserted replaced
1155:a9a142fcf1b5 1156:bbc2d15aaf7a
       
     1 /*
       
     2  * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package com.sun.jmx.namespace;
       
    27 
       
    28 import com.sun.jmx.defaults.JmxProperties;
       
    29 
       
    30 import java.io.IOException;
       
    31 import java.util.Collections;
       
    32 import java.util.HashMap;
       
    33 import java.util.Map;
       
    34 import java.util.WeakHashMap;
       
    35 import java.util.logging.Level;
       
    36 import java.util.logging.Logger;
       
    37 
       
    38 import javax.management.ListenerNotFoundException;
       
    39 import javax.management.MBeanServerConnection;
       
    40 import javax.management.NotificationFilter;
       
    41 import javax.management.NotificationListener;
       
    42 import javax.management.event.EventClient;
       
    43 import javax.management.namespace.JMXNamespaces;
       
    44 import javax.management.remote.JMXAddressable;
       
    45 import javax.management.remote.JMXConnector;
       
    46 import javax.management.remote.JMXServiceURL;
       
    47 import javax.security.auth.Subject;
       
    48 
       
    49 /**
       
    50  * A collection of methods that provide JMXConnector wrappers for
       
    51  * JMXRemoteNamepaces underlying connectors.
       
    52  * <p><b>
       
    53  * This API is a Sun internal API and is subject to changes without notice.
       
    54  * </b></p>
       
    55  * @since 1.7
       
    56  */
       
    57 public final class JMXNamespaceUtils {
       
    58 
       
    59     /**
       
    60      * A logger for this class.
       
    61      **/
       
    62     private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
       
    63 
       
    64 
       
    65     private static <K,V> Map<K,V> newWeakHashMap() {
       
    66         return new WeakHashMap<K,V>();
       
    67     }
       
    68 
       
    69     /** Creates a new instance of JMXNamespaces */
       
    70     private JMXNamespaceUtils() {
       
    71     }
       
    72 
       
    73     /**
       
    74      * Returns an unmodifiable option map in which the given keys have been
       
    75      * filtered out.
       
    76      * @param keys keys to filter out from the map.
       
    77      * @return An unmodifiable option map in which the given keys have been
       
    78      * filtered out.
       
    79      */
       
    80     public static <K,V> Map<K,V> filterMap(Map<K,V> map, K... keys) {
       
    81         final Map<K,V> filtered;
       
    82         filtered=new HashMap<K,V>(map);
       
    83         for (K key : keys) {
       
    84             filtered.remove(key);
       
    85         }
       
    86         return unmodifiableMap(filtered);
       
    87     }
       
    88 
       
    89     // returns un unmodifiable view of a map.
       
    90     public static <K,V> Map<K,V> unmodifiableMap(Map<K,V> aMap) {
       
    91         if (aMap == null || aMap.isEmpty())
       
    92             return Collections.emptyMap();
       
    93         return Collections.unmodifiableMap(aMap);
       
    94     }
       
    95 
       
    96 
       
    97     /**
       
    98      * A base class that helps writing JMXConnectors that return
       
    99      * MBeanServerConnection wrappers.
       
   100      * This base class wraps an inner JMXConnector (the source), and preserve
       
   101      * its caching policy. If a connection is cached in the source, its wrapper
       
   102      * will be cached in this connector too.
       
   103      * Author's note: rewriting this with java.lang.reflect.Proxy could be
       
   104      * envisaged. It would avoid the combinatory sub-classing introduced by
       
   105      * JMXAddressable.
       
   106      * <p>
       
   107      * Note: all the standard JMXConnector implementations are serializable.
       
   108      *       This implementation here is not. Should it be?
       
   109      *       I believe it must not be serializable unless it becomes
       
   110      *       part of a public API (either standard or officially exposed
       
   111      *       and supported in a documented com.sun package)
       
   112      **/
       
   113      static class JMXCachingConnector
       
   114             implements JMXConnector  {
       
   115 
       
   116         // private static final long serialVersionUID = -2279076110599707875L;
       
   117 
       
   118         final JMXConnector source;
       
   119 
       
   120         // if this object is made serializable, then the variable below
       
   121         // needs to become volatile transient and be lazyly-created...
       
   122         private final
       
   123                 Map<MBeanServerConnection,MBeanServerConnection> connectionMap;
       
   124 
       
   125 
       
   126         public JMXCachingConnector(JMXConnector source) {
       
   127             this.source = checkNonNull(source, "source");
       
   128             connectionMap = newWeakHashMap();
       
   129         }
       
   130 
       
   131         private MBeanServerConnection
       
   132                 getCached(MBeanServerConnection inner) {
       
   133             return connectionMap.get(inner);
       
   134         }
       
   135 
       
   136         private MBeanServerConnection putCached(final MBeanServerConnection inner,
       
   137                 final MBeanServerConnection wrapper) {
       
   138             if (inner == wrapper) return wrapper;
       
   139             synchronized (this) {
       
   140                 final MBeanServerConnection concurrent =
       
   141                         connectionMap.get(inner);
       
   142                 if (concurrent != null) return concurrent;
       
   143                 connectionMap.put(inner,wrapper);
       
   144             }
       
   145             return wrapper;
       
   146         }
       
   147 
       
   148         public void addConnectionNotificationListener(NotificationListener
       
   149                 listener, NotificationFilter filter, Object handback) {
       
   150             source.addConnectionNotificationListener(listener,filter,handback);
       
   151         }
       
   152 
       
   153         public void close() throws IOException {
       
   154             source.close();
       
   155         }
       
   156 
       
   157         public void connect() throws IOException {
       
   158             source.connect();
       
   159         }
       
   160 
       
   161         public void connect(Map<String,?> env) throws IOException {
       
   162             source.connect(env);
       
   163         }
       
   164 
       
   165         public String getConnectionId() throws IOException {
       
   166             return source.getConnectionId();
       
   167         }
       
   168 
       
   169         /**
       
   170          * Preserve caching policy of the underlying connector.
       
   171          **/
       
   172         public MBeanServerConnection
       
   173                 getMBeanServerConnection() throws IOException {
       
   174             final MBeanServerConnection inner =
       
   175                     source.getMBeanServerConnection();
       
   176             final MBeanServerConnection cached = getCached(inner);
       
   177             if (cached != null) return cached;
       
   178             final MBeanServerConnection wrapper = wrap(inner);
       
   179             return putCached(inner,wrapper);
       
   180         }
       
   181 
       
   182         public MBeanServerConnection
       
   183                 getMBeanServerConnection(Subject delegationSubject)
       
   184                 throws IOException {
       
   185             final MBeanServerConnection wrapped =
       
   186                     source.getMBeanServerConnection(delegationSubject);
       
   187             synchronized (this) {
       
   188                 final MBeanServerConnection cached = getCached(wrapped);
       
   189                 if (cached != null) return cached;
       
   190                 final MBeanServerConnection wrapper =
       
   191                     wrapWithSubject(wrapped,delegationSubject);
       
   192                 return putCached(wrapped,wrapper);
       
   193             }
       
   194         }
       
   195 
       
   196         public void removeConnectionNotificationListener(
       
   197                 NotificationListener listener)
       
   198                 throws ListenerNotFoundException {
       
   199             source.removeConnectionNotificationListener(listener);
       
   200         }
       
   201 
       
   202         public void removeConnectionNotificationListener(
       
   203                 NotificationListener l, NotificationFilter f,
       
   204                 Object handback) throws ListenerNotFoundException {
       
   205             source.removeConnectionNotificationListener(l,f,handback);
       
   206         }
       
   207 
       
   208         /**
       
   209          * This is the method that subclass will redefine. This method
       
   210          * is called by {@code this.getMBeanServerConnection()}.
       
   211          * {@code inner} is the connection returned by
       
   212          * {@code source.getMBeanServerConnection()}.
       
   213          **/
       
   214         protected MBeanServerConnection wrap(MBeanServerConnection inner)
       
   215             throws IOException {
       
   216             return inner;
       
   217         }
       
   218 
       
   219         /**
       
   220          * Subclass may also want to redefine this method.
       
   221          * By default it calls wrap(inner). This method
       
   222          * is called by {@code this.getMBeanServerConnection(Subject)}.
       
   223          * {@code inner} is the connection returned by
       
   224          * {@code source.getMBeanServerConnection(Subject)}.
       
   225          **/
       
   226         protected MBeanServerConnection wrapWithSubject(
       
   227                 MBeanServerConnection inner, Subject delegationSubject)
       
   228             throws IOException {
       
   229                 return wrap(inner);
       
   230         }
       
   231 
       
   232         @Override
       
   233         public String toString() {
       
   234             if (source instanceof JMXAddressable) {
       
   235                 final JMXServiceURL address =
       
   236                         ((JMXAddressable)source).getAddress();
       
   237                 if (address != null)
       
   238                     return address.toString();
       
   239             }
       
   240             return source.toString();
       
   241         }
       
   242 
       
   243     }
       
   244 
       
   245 
       
   246     /**
       
   247      * The name space connector can do 'cd'
       
   248      **/
       
   249     static class JMXNamespaceConnector extends JMXCachingConnector {
       
   250 
       
   251         // private static final long serialVersionUID = -4813611540843020867L;
       
   252 
       
   253         private final String toDir;
       
   254         private final boolean closeable;
       
   255 
       
   256         public JMXNamespaceConnector(JMXConnector source, String toDir,
       
   257                 boolean closeable) {
       
   258             super(source);
       
   259             this.toDir = toDir;
       
   260             this.closeable = closeable;
       
   261         }
       
   262 
       
   263         @Override
       
   264         public void close() throws IOException {
       
   265             if (!closeable)
       
   266                 throw new UnsupportedOperationException("close");
       
   267             else super.close();
       
   268         }
       
   269 
       
   270         @Override
       
   271         protected MBeanServerConnection wrap(MBeanServerConnection wrapped)
       
   272                throws IOException {
       
   273             if (LOG.isLoggable(Level.FINER))
       
   274                 LOG.finer("Creating name space proxy connection for source: "+
       
   275                         "namespace="+toDir);
       
   276             return JMXNamespaces.narrowToNamespace(wrapped,toDir);
       
   277         }
       
   278 
       
   279         @Override
       
   280         public String toString() {
       
   281             return "JMXNamespaces.narrowToNamespace("+
       
   282                     super.toString()+
       
   283                     ", \""+toDir+"\")";
       
   284         }
       
   285 
       
   286     }
       
   287 
       
   288     static class JMXEventConnector extends JMXCachingConnector {
       
   289 
       
   290         // private static final long serialVersionUID = 4742659236340242785L;
       
   291 
       
   292         JMXEventConnector(JMXConnector wrapped) {
       
   293             super(wrapped);
       
   294         }
       
   295 
       
   296         @Override
       
   297         protected MBeanServerConnection wrap(MBeanServerConnection inner)
       
   298                 throws IOException {
       
   299             return EventClient.getEventClientConnection(inner);
       
   300         }
       
   301 
       
   302 
       
   303         @Override
       
   304         public String toString() {
       
   305             return "EventClient.withEventClient("+super.toString()+")";
       
   306         }
       
   307     }
       
   308 
       
   309     static class JMXAddressableEventConnector extends JMXEventConnector
       
   310         implements JMXAddressable {
       
   311 
       
   312         // private static final long serialVersionUID = -9128520234812124712L;
       
   313 
       
   314         JMXAddressableEventConnector(JMXConnector wrapped) {
       
   315             super(wrapped);
       
   316         }
       
   317 
       
   318         public JMXServiceURL getAddress() {
       
   319             return ((JMXAddressable)source).getAddress();
       
   320         }
       
   321     }
       
   322 
       
   323     /**
       
   324      * Creates a connector whose MBeamServerConnection will point to the
       
   325      * given sub name space inside the source connector.
       
   326      * @see JMXNamespace
       
   327      **/
       
   328     public static JMXConnector cd(final JMXConnector source,
       
   329                                   final String toNamespace,
       
   330                                   final boolean closeable)
       
   331         throws IOException {
       
   332 
       
   333         checkNonNull(source, "JMXConnector");
       
   334 
       
   335         if (toNamespace == null || toNamespace.equals(""))
       
   336             return source;
       
   337 
       
   338         return new JMXNamespaceConnector(source,toNamespace,closeable);
       
   339     }
       
   340 
       
   341 
       
   342     /**
       
   343      * Returns a JMX Connector that will use an {@link EventClient}
       
   344      * to subscribe for notifications. If the server doesn't have
       
   345      * an {@link EventClientDelegateMBean}, then the connector will
       
   346      * use the legacy notification mechanism instead.
       
   347      *
       
   348      * @param source The underlying JMX Connector wrapped by the returned
       
   349      *               connector.
       
   350      * @return A JMX Connector that will uses an {@link EventClient}, if
       
   351      *         available.
       
   352      * @see EventClient#getEventClientConnection(MBeanServerConnection)
       
   353      */
       
   354     public static JMXConnector withEventClient(final JMXConnector source) {
       
   355         checkNonNull(source, "JMXConnector");
       
   356         if (source instanceof JMXAddressable)
       
   357             return new JMXAddressableEventConnector(source);
       
   358         else
       
   359             return new JMXEventConnector(source);
       
   360     }
       
   361 
       
   362     public static <T> T checkNonNull(T parameter, String name) {
       
   363         if (parameter == null)
       
   364             throw new IllegalArgumentException(name+" must not be null");
       
   365          return parameter;
       
   366     }
       
   367 
       
   368 
       
   369 }