jdk/src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java
changeset 1604 6d43194ed39c
parent 1603 720c1119ad5e
parent 1594 3cb2a607c347
child 1608 0a375b5fb8e1
equal deleted inserted replaced
1603:720c1119ad5e 1604:6d43194ed39c
     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.Map;
       
    33 import java.util.WeakHashMap;
       
    34 import java.util.logging.Level;
       
    35 import java.util.logging.Logger;
       
    36 
       
    37 import javax.management.ListenerNotFoundException;
       
    38 import javax.management.MBeanServerConnection;
       
    39 import javax.management.NotificationFilter;
       
    40 import javax.management.NotificationListener;
       
    41 import javax.management.event.EventClient;
       
    42 import javax.management.event.EventClientDelegateMBean;
       
    43 import javax.management.namespace.JMXNamespace;
       
    44 import javax.management.namespace.JMXNamespaces;
       
    45 import javax.management.remote.JMXAddressable;
       
    46 import javax.management.remote.JMXConnector;
       
    47 import javax.management.remote.JMXServiceURL;
       
    48 import javax.security.auth.Subject;
       
    49 
       
    50 /**
       
    51  * A collection of methods that provide JMXConnector wrappers for
       
    52  * JMXRemoteNamepaces underlying connectors.
       
    53  * <p><b>
       
    54  * This API is a Sun internal API and is subject to changes without notice.
       
    55  * </b></p>
       
    56  * @since 1.7
       
    57  */
       
    58 public final class JMXNamespaceUtils {
       
    59 
       
    60     /**
       
    61      * A logger for this class.
       
    62      **/
       
    63     private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
       
    64 
       
    65 
       
    66     private static <K,V> Map<K,V> newWeakHashMap() {
       
    67         return new WeakHashMap<K,V>();
       
    68     }
       
    69 
       
    70     /** There are no instances of this class */
       
    71     private JMXNamespaceUtils() {
       
    72     }
       
    73 
       
    74     // returns un unmodifiable view of a map.
       
    75     public static <K,V> Map<K,V> unmodifiableMap(Map<K,V> aMap) {
       
    76         if (aMap == null || aMap.isEmpty())
       
    77             return Collections.emptyMap();
       
    78         return Collections.unmodifiableMap(aMap);
       
    79     }
       
    80 
       
    81 
       
    82     /**
       
    83      * A base class that helps writing JMXConnectors that return
       
    84      * MBeanServerConnection wrappers.
       
    85      * This base class wraps an inner JMXConnector (the source), and preserve
       
    86      * its caching policy. If a connection is cached in the source, its wrapper
       
    87      * will be cached in this connector too.
       
    88      * Author's note: rewriting this with java.lang.reflect.Proxy could be
       
    89      * envisaged. It would avoid the combinatory sub-classing introduced by
       
    90      * JMXAddressable.
       
    91      * <p>
       
    92      * Note: all the standard JMXConnector implementations are serializable.
       
    93      *       This implementation here is not. Should it be?
       
    94      *       I believe it must not be serializable unless it becomes
       
    95      *       part of a public API (either standard or officially exposed
       
    96      *       and supported in a documented com.sun package)
       
    97      **/
       
    98      static class JMXCachingConnector
       
    99             implements JMXConnector  {
       
   100 
       
   101         // private static final long serialVersionUID = -2279076110599707875L;
       
   102 
       
   103         final JMXConnector source;
       
   104 
       
   105         // if this object is made serializable, then the variable below
       
   106         // needs to become volatile transient and be lazyly-created...
       
   107         private final
       
   108                 Map<MBeanServerConnection,MBeanServerConnection> connectionMap;
       
   109 
       
   110 
       
   111         public JMXCachingConnector(JMXConnector source) {
       
   112             this.source = checkNonNull(source, "source");
       
   113             connectionMap = newWeakHashMap();
       
   114         }
       
   115 
       
   116         private MBeanServerConnection
       
   117                 getCached(MBeanServerConnection inner) {
       
   118             return connectionMap.get(inner);
       
   119         }
       
   120 
       
   121         private MBeanServerConnection putCached(final MBeanServerConnection inner,
       
   122                 final MBeanServerConnection wrapper) {
       
   123             if (inner == wrapper) return wrapper;
       
   124             synchronized (this) {
       
   125                 final MBeanServerConnection concurrent =
       
   126                         connectionMap.get(inner);
       
   127                 if (concurrent != null) return concurrent;
       
   128                 connectionMap.put(inner,wrapper);
       
   129             }
       
   130             return wrapper;
       
   131         }
       
   132 
       
   133         public void addConnectionNotificationListener(NotificationListener
       
   134                 listener, NotificationFilter filter, Object handback) {
       
   135             source.addConnectionNotificationListener(listener,filter,handback);
       
   136         }
       
   137 
       
   138         public void close() throws IOException {
       
   139             source.close();
       
   140         }
       
   141 
       
   142         public void connect() throws IOException {
       
   143             source.connect();
       
   144         }
       
   145 
       
   146         public void connect(Map<String,?> env) throws IOException {
       
   147             source.connect(env);
       
   148         }
       
   149 
       
   150         public String getConnectionId() throws IOException {
       
   151             return source.getConnectionId();
       
   152         }
       
   153 
       
   154         /**
       
   155          * Preserve caching policy of the underlying connector.
       
   156          **/
       
   157         public MBeanServerConnection
       
   158                 getMBeanServerConnection() throws IOException {
       
   159             final MBeanServerConnection inner =
       
   160                     source.getMBeanServerConnection();
       
   161             final MBeanServerConnection cached = getCached(inner);
       
   162             if (cached != null) return cached;
       
   163             final MBeanServerConnection wrapper = wrap(inner);
       
   164             return putCached(inner,wrapper);
       
   165         }
       
   166 
       
   167         public MBeanServerConnection
       
   168                 getMBeanServerConnection(Subject delegationSubject)
       
   169                 throws IOException {
       
   170             final MBeanServerConnection wrapped =
       
   171                     source.getMBeanServerConnection(delegationSubject);
       
   172             synchronized (this) {
       
   173                 final MBeanServerConnection cached = getCached(wrapped);
       
   174                 if (cached != null) return cached;
       
   175                 final MBeanServerConnection wrapper =
       
   176                     wrapWithSubject(wrapped,delegationSubject);
       
   177                 return putCached(wrapped,wrapper);
       
   178             }
       
   179         }
       
   180 
       
   181         public void removeConnectionNotificationListener(
       
   182                 NotificationListener listener)
       
   183                 throws ListenerNotFoundException {
       
   184             source.removeConnectionNotificationListener(listener);
       
   185         }
       
   186 
       
   187         public void removeConnectionNotificationListener(
       
   188                 NotificationListener l, NotificationFilter f,
       
   189                 Object handback) throws ListenerNotFoundException {
       
   190             source.removeConnectionNotificationListener(l,f,handback);
       
   191         }
       
   192 
       
   193         /**
       
   194          * This is the method that subclass will redefine. This method
       
   195          * is called by {@code this.getMBeanServerConnection()}.
       
   196          * {@code inner} is the connection returned by
       
   197          * {@code source.getMBeanServerConnection()}.
       
   198          **/
       
   199         protected MBeanServerConnection wrap(MBeanServerConnection inner)
       
   200             throws IOException {
       
   201             return inner;
       
   202         }
       
   203 
       
   204         /**
       
   205          * Subclass may also want to redefine this method.
       
   206          * By default it calls wrap(inner). This method
       
   207          * is called by {@code this.getMBeanServerConnection(Subject)}.
       
   208          * {@code inner} is the connection returned by
       
   209          * {@code source.getMBeanServerConnection(Subject)}.
       
   210          **/
       
   211         protected MBeanServerConnection wrapWithSubject(
       
   212                 MBeanServerConnection inner, Subject delegationSubject)
       
   213             throws IOException {
       
   214                 return wrap(inner);
       
   215         }
       
   216 
       
   217         @Override
       
   218         public String toString() {
       
   219             if (source instanceof JMXAddressable) {
       
   220                 final JMXServiceURL address =
       
   221                         ((JMXAddressable)source).getAddress();
       
   222                 if (address != null)
       
   223                     return address.toString();
       
   224             }
       
   225             return source.toString();
       
   226         }
       
   227 
       
   228     }
       
   229 
       
   230 
       
   231     /**
       
   232      * The name space connector can do 'cd'
       
   233      **/
       
   234     static class JMXNamespaceConnector extends JMXCachingConnector {
       
   235 
       
   236         // private static final long serialVersionUID = -4813611540843020867L;
       
   237 
       
   238         private final String toDir;
       
   239         private final boolean closeable;
       
   240 
       
   241         public JMXNamespaceConnector(JMXConnector source, String toDir,
       
   242                 boolean closeable) {
       
   243             super(source);
       
   244             this.toDir = toDir;
       
   245             this.closeable = closeable;
       
   246         }
       
   247 
       
   248         @Override
       
   249         public void close() throws IOException {
       
   250             if (!closeable)
       
   251                 throw new UnsupportedOperationException("close");
       
   252             else super.close();
       
   253         }
       
   254 
       
   255         @Override
       
   256         protected MBeanServerConnection wrap(MBeanServerConnection wrapped)
       
   257                throws IOException {
       
   258             if (LOG.isLoggable(Level.FINER))
       
   259                 LOG.finer("Creating name space proxy connection for source: "+
       
   260                         "namespace="+toDir);
       
   261             return JMXNamespaces.narrowToNamespace(wrapped,toDir);
       
   262         }
       
   263 
       
   264         @Override
       
   265         public String toString() {
       
   266             return "JMXNamespaces.narrowToNamespace("+
       
   267                     super.toString()+
       
   268                     ", \""+toDir+"\")";
       
   269         }
       
   270 
       
   271     }
       
   272 
       
   273     static class JMXEventConnector extends JMXCachingConnector {
       
   274 
       
   275         // private static final long serialVersionUID = 4742659236340242785L;
       
   276 
       
   277         JMXEventConnector(JMXConnector wrapped) {
       
   278             super(wrapped);
       
   279         }
       
   280 
       
   281         @Override
       
   282         protected MBeanServerConnection wrap(MBeanServerConnection inner)
       
   283                 throws IOException {
       
   284             return EventClient.getEventClientConnection(inner);
       
   285         }
       
   286 
       
   287 
       
   288         @Override
       
   289         public String toString() {
       
   290             return "EventClient.withEventClient("+super.toString()+")";
       
   291         }
       
   292     }
       
   293 
       
   294     static class JMXAddressableEventConnector extends JMXEventConnector
       
   295         implements JMXAddressable {
       
   296 
       
   297         // private static final long serialVersionUID = -9128520234812124712L;
       
   298 
       
   299         JMXAddressableEventConnector(JMXConnector wrapped) {
       
   300             super(wrapped);
       
   301         }
       
   302 
       
   303         public JMXServiceURL getAddress() {
       
   304             return ((JMXAddressable)source).getAddress();
       
   305         }
       
   306     }
       
   307 
       
   308     /**
       
   309      * Creates a connector whose MBeamServerConnection will point to the
       
   310      * given sub name space inside the source connector.
       
   311      * @see JMXNamespace
       
   312      **/
       
   313     public static JMXConnector cd(final JMXConnector source,
       
   314                                   final String toNamespace,
       
   315                                   final boolean closeable)
       
   316         throws IOException {
       
   317 
       
   318         checkNonNull(source, "JMXConnector");
       
   319 
       
   320         if (toNamespace == null || toNamespace.equals(""))
       
   321             return source;
       
   322 
       
   323         return new JMXNamespaceConnector(source,toNamespace,closeable);
       
   324     }
       
   325 
       
   326 
       
   327     /**
       
   328      * Returns a JMX Connector that will use an {@link EventClient}
       
   329      * to subscribe for notifications. If the server doesn't have
       
   330      * an {@link EventClientDelegateMBean}, then the connector will
       
   331      * use the legacy notification mechanism instead.
       
   332      *
       
   333      * @param source The underlying JMX Connector wrapped by the returned
       
   334      *               connector.
       
   335      * @return A JMX Connector that will uses an {@link EventClient}, if
       
   336      *         available.
       
   337      * @see EventClient#getEventClientConnection(MBeanServerConnection)
       
   338      */
       
   339     public static JMXConnector withEventClient(final JMXConnector source) {
       
   340         checkNonNull(source, "JMXConnector");
       
   341         if (source instanceof JMXAddressable)
       
   342             return new JMXAddressableEventConnector(source);
       
   343         else
       
   344             return new JMXEventConnector(source);
       
   345     }
       
   346 
       
   347     public static <T> T checkNonNull(T parameter, String name) {
       
   348         if (parameter == null)
       
   349             throw new IllegalArgumentException(name+" must not be null");
       
   350          return parameter;
       
   351     }
       
   352 
       
   353 
       
   354 }