jdk/src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java
changeset 1156 bbc2d15aaf7a
child 1222 78e3d021d528
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,369 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.jmx.namespace;
+
+import com.sun.jmx.defaults.JmxProperties;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanServerConnection;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.event.EventClient;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.remote.JMXAddressable;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXServiceURL;
+import javax.security.auth.Subject;
+
+/**
+ * A collection of methods that provide JMXConnector wrappers for
+ * JMXRemoteNamepaces underlying connectors.
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+public final class JMXNamespaceUtils {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
+
+
+    private static <K,V> Map<K,V> newWeakHashMap() {
+        return new WeakHashMap<K,V>();
+    }
+
+    /** Creates a new instance of JMXNamespaces */
+    private JMXNamespaceUtils() {
+    }
+
+    /**
+     * Returns an unmodifiable option map in which the given keys have been
+     * filtered out.
+     * @param keys keys to filter out from the map.
+     * @return An unmodifiable option map in which the given keys have been
+     * filtered out.
+     */
+    public static <K,V> Map<K,V> filterMap(Map<K,V> map, K... keys) {
+        final Map<K,V> filtered;
+        filtered=new HashMap<K,V>(map);
+        for (K key : keys) {
+            filtered.remove(key);
+        }
+        return unmodifiableMap(filtered);
+    }
+
+    // returns un unmodifiable view of a map.
+    public static <K,V> Map<K,V> unmodifiableMap(Map<K,V> aMap) {
+        if (aMap == null || aMap.isEmpty())
+            return Collections.emptyMap();
+        return Collections.unmodifiableMap(aMap);
+    }
+
+
+    /**
+     * A base class that helps writing JMXConnectors that return
+     * MBeanServerConnection wrappers.
+     * This base class wraps an inner JMXConnector (the source), and preserve
+     * its caching policy. If a connection is cached in the source, its wrapper
+     * will be cached in this connector too.
+     * Author's note: rewriting this with java.lang.reflect.Proxy could be
+     * envisaged. It would avoid the combinatory sub-classing introduced by
+     * JMXAddressable.
+     * <p>
+     * Note: all the standard JMXConnector implementations are serializable.
+     *       This implementation here is not. Should it be?
+     *       I believe it must not be serializable unless it becomes
+     *       part of a public API (either standard or officially exposed
+     *       and supported in a documented com.sun package)
+     **/
+     static class JMXCachingConnector
+            implements JMXConnector  {
+
+        // private static final long serialVersionUID = -2279076110599707875L;
+
+        final JMXConnector source;
+
+        // if this object is made serializable, then the variable below
+        // needs to become volatile transient and be lazyly-created...
+        private final
+                Map<MBeanServerConnection,MBeanServerConnection> connectionMap;
+
+
+        public JMXCachingConnector(JMXConnector source) {
+            this.source = checkNonNull(source, "source");
+            connectionMap = newWeakHashMap();
+        }
+
+        private MBeanServerConnection
+                getCached(MBeanServerConnection inner) {
+            return connectionMap.get(inner);
+        }
+
+        private MBeanServerConnection putCached(final MBeanServerConnection inner,
+                final MBeanServerConnection wrapper) {
+            if (inner == wrapper) return wrapper;
+            synchronized (this) {
+                final MBeanServerConnection concurrent =
+                        connectionMap.get(inner);
+                if (concurrent != null) return concurrent;
+                connectionMap.put(inner,wrapper);
+            }
+            return wrapper;
+        }
+
+        public void addConnectionNotificationListener(NotificationListener
+                listener, NotificationFilter filter, Object handback) {
+            source.addConnectionNotificationListener(listener,filter,handback);
+        }
+
+        public void close() throws IOException {
+            source.close();
+        }
+
+        public void connect() throws IOException {
+            source.connect();
+        }
+
+        public void connect(Map<String,?> env) throws IOException {
+            source.connect(env);
+        }
+
+        public String getConnectionId() throws IOException {
+            return source.getConnectionId();
+        }
+
+        /**
+         * Preserve caching policy of the underlying connector.
+         **/
+        public MBeanServerConnection
+                getMBeanServerConnection() throws IOException {
+            final MBeanServerConnection inner =
+                    source.getMBeanServerConnection();
+            final MBeanServerConnection cached = getCached(inner);
+            if (cached != null) return cached;
+            final MBeanServerConnection wrapper = wrap(inner);
+            return putCached(inner,wrapper);
+        }
+
+        public MBeanServerConnection
+                getMBeanServerConnection(Subject delegationSubject)
+                throws IOException {
+            final MBeanServerConnection wrapped =
+                    source.getMBeanServerConnection(delegationSubject);
+            synchronized (this) {
+                final MBeanServerConnection cached = getCached(wrapped);
+                if (cached != null) return cached;
+                final MBeanServerConnection wrapper =
+                    wrapWithSubject(wrapped,delegationSubject);
+                return putCached(wrapped,wrapper);
+            }
+        }
+
+        public void removeConnectionNotificationListener(
+                NotificationListener listener)
+                throws ListenerNotFoundException {
+            source.removeConnectionNotificationListener(listener);
+        }
+
+        public void removeConnectionNotificationListener(
+                NotificationListener l, NotificationFilter f,
+                Object handback) throws ListenerNotFoundException {
+            source.removeConnectionNotificationListener(l,f,handback);
+        }
+
+        /**
+         * This is the method that subclass will redefine. This method
+         * is called by {@code this.getMBeanServerConnection()}.
+         * {@code inner} is the connection returned by
+         * {@code source.getMBeanServerConnection()}.
+         **/
+        protected MBeanServerConnection wrap(MBeanServerConnection inner)
+            throws IOException {
+            return inner;
+        }
+
+        /**
+         * Subclass may also want to redefine this method.
+         * By default it calls wrap(inner). This method
+         * is called by {@code this.getMBeanServerConnection(Subject)}.
+         * {@code inner} is the connection returned by
+         * {@code source.getMBeanServerConnection(Subject)}.
+         **/
+        protected MBeanServerConnection wrapWithSubject(
+                MBeanServerConnection inner, Subject delegationSubject)
+            throws IOException {
+                return wrap(inner);
+        }
+
+        @Override
+        public String toString() {
+            if (source instanceof JMXAddressable) {
+                final JMXServiceURL address =
+                        ((JMXAddressable)source).getAddress();
+                if (address != null)
+                    return address.toString();
+            }
+            return source.toString();
+        }
+
+    }
+
+
+    /**
+     * The name space connector can do 'cd'
+     **/
+    static class JMXNamespaceConnector extends JMXCachingConnector {
+
+        // private static final long serialVersionUID = -4813611540843020867L;
+
+        private final String toDir;
+        private final boolean closeable;
+
+        public JMXNamespaceConnector(JMXConnector source, String toDir,
+                boolean closeable) {
+            super(source);
+            this.toDir = toDir;
+            this.closeable = closeable;
+        }
+
+        @Override
+        public void close() throws IOException {
+            if (!closeable)
+                throw new UnsupportedOperationException("close");
+            else super.close();
+        }
+
+        @Override
+        protected MBeanServerConnection wrap(MBeanServerConnection wrapped)
+               throws IOException {
+            if (LOG.isLoggable(Level.FINER))
+                LOG.finer("Creating name space proxy connection for source: "+
+                        "namespace="+toDir);
+            return JMXNamespaces.narrowToNamespace(wrapped,toDir);
+        }
+
+        @Override
+        public String toString() {
+            return "JMXNamespaces.narrowToNamespace("+
+                    super.toString()+
+                    ", \""+toDir+"\")";
+        }
+
+    }
+
+    static class JMXEventConnector extends JMXCachingConnector {
+
+        // private static final long serialVersionUID = 4742659236340242785L;
+
+        JMXEventConnector(JMXConnector wrapped) {
+            super(wrapped);
+        }
+
+        @Override
+        protected MBeanServerConnection wrap(MBeanServerConnection inner)
+                throws IOException {
+            return EventClient.getEventClientConnection(inner);
+        }
+
+
+        @Override
+        public String toString() {
+            return "EventClient.withEventClient("+super.toString()+")";
+        }
+    }
+
+    static class JMXAddressableEventConnector extends JMXEventConnector
+        implements JMXAddressable {
+
+        // private static final long serialVersionUID = -9128520234812124712L;
+
+        JMXAddressableEventConnector(JMXConnector wrapped) {
+            super(wrapped);
+        }
+
+        public JMXServiceURL getAddress() {
+            return ((JMXAddressable)source).getAddress();
+        }
+    }
+
+    /**
+     * Creates a connector whose MBeamServerConnection will point to the
+     * given sub name space inside the source connector.
+     * @see JMXNamespace
+     **/
+    public static JMXConnector cd(final JMXConnector source,
+                                  final String toNamespace,
+                                  final boolean closeable)
+        throws IOException {
+
+        checkNonNull(source, "JMXConnector");
+
+        if (toNamespace == null || toNamespace.equals(""))
+            return source;
+
+        return new JMXNamespaceConnector(source,toNamespace,closeable);
+    }
+
+
+    /**
+     * Returns a JMX Connector that will use an {@link EventClient}
+     * to subscribe for notifications. If the server doesn't have
+     * an {@link EventClientDelegateMBean}, then the connector will
+     * use the legacy notification mechanism instead.
+     *
+     * @param source The underlying JMX Connector wrapped by the returned
+     *               connector.
+     * @return A JMX Connector that will uses an {@link EventClient}, if
+     *         available.
+     * @see EventClient#getEventClientConnection(MBeanServerConnection)
+     */
+    public static JMXConnector withEventClient(final JMXConnector source) {
+        checkNonNull(source, "JMXConnector");
+        if (source instanceof JMXAddressable)
+            return new JMXAddressableEventConnector(source);
+        else
+            return new JMXEventConnector(source);
+    }
+
+    public static <T> T checkNonNull(T parameter, String name) {
+        if (parameter == null)
+            throw new IllegalArgumentException(name+" must not be null");
+         return parameter;
+    }
+
+
+}