/* * 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 javax.management.namespace; import com.sun.jmx.defaults.JmxProperties; import com.sun.jmx.mbeanserver.Util; import com.sun.jmx.namespace.JMXNamespaceUtils; import com.sun.jmx.namespace.NamespaceInterceptor.DynamicProbe; import com.sun.jmx.remote.util.EnvHelp; import java.io.IOException; import java.security.AccessControlException; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.management.AttributeChangeNotification; import javax.management.InstanceNotFoundException; import javax.management.ListenerNotFoundException; import javax.management.MBeanNotificationInfo; import javax.management.MBeanPermission; import javax.management.MBeanServerConnection; import javax.management.MalformedObjectNameException; import javax.management.Notification; import javax.management.NotificationBroadcasterSupport; import javax.management.NotificationEmitter; import javax.management.NotificationFilter; import javax.management.NotificationListener; import javax.management.ObjectName; import javax.management.event.EventClient; import javax.management.remote.JMXConnectionNotification; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; /** * A {@link JMXNamespace} that will connect to a remote MBeanServer * by creating a {@link javax.management.remote.JMXConnector} from a * {@link javax.management.remote.JMXServiceURL}. *
* You can call {@link #connect() connect()} and {@link #close close()} * several times. This MBean will emit an {@link AttributeChangeNotification} * when the value of its {@link #isConnected Connected} attribute changes. *
** The JMX Remote Namespace MBean is not connected until {@link * #connect() connect()} is explicitly called. The usual sequence of code to * create a JMX Remote Namespace is thus: *
*
* final String namespace = "mynamespace";
* final ObjectName name = {@link JMXNamespaces#getNamespaceObjectName
* JMXNamespaces.getNamespaceObjectName(namespace)};
* final JMXServiceURL remoteServerURL = .... ;
* final Map optionsMap = .... ;
* final MBeanServer masterMBeanServer = .... ;
* final JMXRemoteNamespace namespaceMBean = {@link #newJMXRemoteNamespace
* JMXRemoteNamespace.newJMXRemoteNamespace(remoteServerURL, optionsMap)};
* masterMBeanServer.registerMBean(namespaceMBean, name);
* namespaceMBean.connect();
* // or: masterMBeanServer.invoke(name, {@link #connect() "connect"}, null, null);
*
* * The JMX Remote Namespace MBean will register for {@linkplain * JMXConnectionNotification JMX Connection Notifications} with its underlying * {@link JMXConnector}. When a JMX Connection Notification indicates that * the underlying connection has failed, the JMX Remote Namespace MBean * closes its underlying connector and switches its {@link #isConnected * Connected} attribute to false, emitting an {@link * AttributeChangeNotification}. *
** At this point, a managing application (or an administrator connected * through a management console) can attempt to reconnect the * JMX Remote Namespace MBean by calling its {@link #connect() connect()} method * again. *
*Note that when the connection with the remote namespace fails, or when * {@link #close} is called, then any notification subscription to * MBeans registered in that namespace will be lost - unless a custom * {@linkplain javax.management.event event service} supporting connection-less * mode was used. *
* @since 1.7 */ public class JMXRemoteNamespace extends JMXNamespace implements JMXRemoteNamespaceMBean, NotificationEmitter { /** * A logger for this class. */ private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; private static final Logger PROBE_LOG = Logger.getLogger( JmxProperties.NAMESPACE_LOGGER_NAME+".probe"); // This connection listener is used to listen for connection events from // the underlying JMXConnector. It is used in particular to maintain the // "connected" state in this MBean. // private static class ConnectionListener implements NotificationListener { private final JMXRemoteNamespace handler; private ConnectionListener(JMXRemoteNamespace handler) { this.handler = handler; } public void handleNotification(Notification notification, Object handback) { if (!(notification instanceof JMXConnectionNotification)) return; final JMXConnectionNotification cn = (JMXConnectionNotification)notification; handler.checkState(this,cn,(JMXConnector)handback); } } // When the JMXRemoteNamespace is originally created, it is not connected, // which means that the source MBeanServer should be one that throws // exceptions for most methods. When it is subsequently connected, // the methods should be forwarded to the MBeanServerConnection. // We handle this using MBeanServerConnectionWrapper. The // MBeanServerConnection that is supplied to the constructor of // MBeanServerConnectionWrapper is ignored (and in fact it is null) // because the one that is actually used is the one supplied by the // override of getMBeanServerConnection(). private static class JMXRemoteNamespaceDelegate extends MBeanServerConnectionWrapper implements DynamicProbe { private volatile JMXRemoteNamespace parent=null; JMXRemoteNamespaceDelegate() { super(null,null); } @Override public MBeanServerConnection getMBeanServerConnection() { return parent.getMBeanServerConnection(); } @Override public ClassLoader getDefaultClassLoader() { return parent.getDefaultClassLoader(); } // Because this class is instantiated in the super() call from the // constructor of JMXRemoteNamespace, it cannot be an inner class. // This method achieves the effect that an inner class would have // had, of giving the class a reference to the outer "this". synchronized void initParentOnce(JMXRemoteNamespace parent) { if (this.parent != null) throw new UnsupportedOperationException("parent already set"); this.parent=parent; } public boolean isProbeRequested() { return this.parent.isProbeRequested(); } } private static final MBeanNotificationInfo connectNotification = new MBeanNotificationInfo(new String[] { AttributeChangeNotification.ATTRIBUTE_CHANGE}, "Connected", "Emitted when the Connected state of this object changes"); private static long seqNumber=0; private final NotificationBroadcasterSupport broadcaster; private final ConnectionListener listener; private final JMXServiceURL jmxURL; private final Map* This constructor is provided for subclasses. * To create a new instance of {@code JMXRemoteNamespace} call * {@link #newJMXRemoteNamespace * JMXRemoteNamespace.newJMXRemoteNamespace(sourceURL, optionsMap)}. *
* @param sourceURL a JMX service URL that can be used to {@linkplain * #connect() connect} to the * source MBean Server. The source MBean Server is the remote * MBean Server which contains the MBeans that will be mirrored * in this namespace. * @param optionsMap the options map that will be passed to the * {@link JMXConnectorFactory} when {@linkplain * JMXConnectorFactory#newJMXConnector creating} the * {@link JMXConnector} used to {@linkplain #connect() connect} * to the remote source MBean Server. Can be null, which is * equivalent to an empty map. * @see #newJMXRemoteNamespace JMXRemoteNamespace.newJMXRemoteNamespace * @see #connect */ protected JMXRemoteNamespace(JMXServiceURL sourceURL, Map* @see #connect * @return The {@code JMXServiceURL} used to connect to the remote * name space. */ public JMXServiceURL getJMXServiceURL() { return jmxURL; } /** * In this class, this method never returns {@code null}, and the * address returned is the {@link #getJMXServiceURL JMXServiceURL} * that is used by this object to {@linkplain #connect} to the remote * name space.
* This behaviour might be overriden by subclasses, if needed.
* For instance, a subclass might want to return {@code null} if it
* doesn't want to expose that JMXServiceURL.
*/
public JMXServiceURL getAddress() {
return getJMXServiceURL();
}
private Map
* The implementation should probably look like:
* This method can be called by subclasses in order to send their own
* notifications.
* In that case, these subclasses might also need to override
* {@link #getNotificationInfo} in order to declare their own
* {@linkplain MBeanNotificationInfo notification types}.
*
* This method first calls {@link JMXConnectorFactory#newJMXConnector
* JMXConnectorFactory.newJMXConnector(jmxURL, env)} to obtain a new
* JMX connector, and returns that.
*
* A subclass of {@link JMXRemoteNamespace} can provide an implementation
* that connects to a sub namespace of the remote server by subclassing
* this class in the following way:
*
* final MBeanNotificationInfo[] myOwnNotifs = { .... };
* final MBeanNotificationInfo[] parentNotifs =
* super.getNotificationInfo();
* final Set
*/
public MBeanNotificationInfo[] getNotificationInfo() {
return broadcaster.getNotificationInfo();
}
public void removeNotificationListener(NotificationListener listener)
throws ListenerNotFoundException {
broadcaster.removeNotificationListener(listener);
}
public void removeNotificationListener(NotificationListener listener,
NotificationFilter filter, Object handback)
throws ListenerNotFoundException {
broadcaster.removeNotificationListener(listener, filter, handback);
}
private static synchronized long getNextSeqNumber() {
return seqNumber++;
}
/**
* Sends a notification to registered listeners. Before the notification
* is sent, the following steps are performed:
*
*
* class JMXRemoteSubNamespace extends JMXRemoteNamespace {
* private final String subnamespace;
* JMXRemoteSubNamespace(JMXServiceURL url,
* Map{@code
*
* Some connectors, like the JMXMP connector server defined by the * version 1.2 of the JMX API may not have been upgraded to use the * new {@linkplain javax.management.event Event Service} defined in this * version of the JMX API. *
* In that case, and if the remote server to which this JMXRemoteNamespace
* connects also contains namespaces, it may be necessary to configure
* explicitly an {@linkplain
* javax.management.event.EventClientDelegate#newForwarder()
* Event Client Forwarder} on the remote server side, and to force the use
* of an {@link EventClient} on this client side.
*
* A subclass of {@link JMXRemoteNamespace} can provide an implementation
* of {@code newJMXConnector} that will force notification subscriptions
* to flow through an {@link EventClient} over a legacy protocol by
* overriding this method in the following way:
*
* class JMXRemoteEventClientNamespace extends JMXRemoteNamespace {
* JMXRemoteSubNamespaceConnector(JMXServiceURL url,
* Map env) {
* super(url,options);
* }
* protected JMXConnector newJMXConnector(JMXServiceURL url,
* Map env) throws IOException {
* final JMXConnector inner = super.newJMXConnector(url,env);
* return {@link EventClient#withEventClient(
* JMXConnector) EventClient.withEventClient(inner)};
* }
* }
*
*
* Note that the remote server also needs to provide an {@link
* javax.management.event.EventClientDelegateMBean}: only configuring
* the client side (this object) is not enough.
* In summary, this technique should be used if the remote server
* supports JMX namespaces, but uses a JMX Connector Server whose
* implementation does not transparently use the new Event Service
* (as would be the case with the JMXMPConnectorServer implementation
* from the reference implementation of the JMX Remote API 1.0
* specification).
*