5072476: RFE: support cascaded (federated) MBean Servers
6299231: Add support for named MBean Servers
Summary: New javax.management.namespace package.
Reviewed-by: emcmanus
/*
* 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}.
* <p>
* 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.
* </p>
* <p>
* 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:
* </p>
* <pre>
* final String namespace = "mynamespace";
* final ObjectName name = {@link JMXNamespaces#getNamespaceObjectName
* JMXNamespaces.getNamespaceObjectName(namespace)};
* final JMXServiceURL remoteServerURL = .... ;
* final Map<String,Object> 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);
* </pre>
* <p>
* 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}.
* </p>
* <p>
* 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.
* </p>
* <p>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.
* </p>
* @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<String,?> optionsMap;
private volatile MBeanServerConnection server = null;
private volatile JMXConnector conn = null;
private volatile ClassLoader defaultClassLoader = null;
private volatile boolean probed;
/**
* Creates a new instance of {@code JMXRemoteNamespace}.
* <p>
* This constructor is provided for subclasses.
* To create a new instance of {@code JMXRemoteNamespace} call
* {@link #newJMXRemoteNamespace
* JMXRemoteNamespace.newJMXRemoteNamespace(sourceURL, optionsMap)}.
* </p>
* @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<String,?> optionsMap) {
super(new JMXRemoteNamespaceDelegate());
((JMXRemoteNamespaceDelegate)super.getSourceServer()).
initParentOnce(this);
// URL must not be null.
this.jmxURL = JMXNamespaceUtils.checkNonNull(sourceURL,"url");
this.broadcaster =
new NotificationBroadcasterSupport(connectNotification);
// handles options
this.optionsMap = JMXNamespaceUtils.unmodifiableMap(optionsMap);
// handles (dis)connection events
this.listener = new ConnectionListener(this);
// XXX TODO: remove the probe, or simplify it.
this.probed = false;
}
/**
* Returns the {@code JMXServiceURL} that is (or will be) used to
* connect to the remote name space. <p>
* @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. <p>
* 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<String,?> getEnvMap() {
return optionsMap;
}
boolean isProbeRequested() {
return probed==false;
}
public void addNotificationListener(NotificationListener listener,
NotificationFilter filter, Object handback) {
broadcaster.addNotificationListener(listener, filter, handback);
}
/**
* A subclass that needs to send its own notifications must override
* this method in order to return an {@link MBeanNotificationInfo
* MBeanNotificationInfo[]} array containing both its own notification
* infos and the notification infos of its super class. <p>
* The implementation should probably look like:
* <pre>
* final MBeanNotificationInfo[] myOwnNotifs = { .... };
* final MBeanNotificationInfo[] parentNotifs =
* super.getNotificationInfo();
* final Set<MBeanNotificationInfo> mergedResult =
* new HashSet<MBeanNotificationInfo>();
* mergedResult.addAll(Arrays.asList(myOwnNotifs));
* mergedResult.addAll(Arrays.asList(parentNotifs));
* return mergeResult.toArray(
* new MBeanNotificationInfo[mergedResult.size()]);
* </pre>
*/
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:
* <ul><li>
* If {@code n.getSequenceNumber() <= 0} set it to the next available
* sequence number.</li>
* <li>If {@code n.getSource() == null}, set it to the value returned by {@link
* #getObjectName getObjectName()}.
* </li></ul>
* <p>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}.
* </p>
* @param n The notification to send to registered listeners.
* @see javax.management.NotificationBroadcasterSupport
* @see #getNotificationInfo
**/
protected void sendNotification(Notification n) {
if (n.getSequenceNumber()<=0)
n.setSequenceNumber(getNextSeqNumber());
if (n.getSource()==null)
n.setSource(getObjectName());
broadcaster.sendNotification(n);
}
private void checkState(ConnectionListener listener,
JMXConnectionNotification cn,
JMXConnector emittingConnector) {
// Due to the asynchronous handling of notifications, it is
// possible that this method is called for a JMXConnector
// (or connection) which is already closed and replaced by a newer
// one.
//
// This method attempts to determine the real state of the
// connection - which might be different from what the notification
// says.
//
// This is quite complex logic - because we try not to hold any
// lock while evaluating the true value of the connected state,
// while anyone might also call close() or connect() from a
// different thread.
//
// The method switchConnection() (called from here too) also has the
// same kind of complex logic.
//
// We use the JMXConnector has a handback to the notification listener
// (emittingConnector) in order to be able to determine whether the
// notification concerns the current connector in use, or an older
// one.
//
boolean remove = false;
// whether the emittingConnector is already 'removed'
synchronized (this) {
if (this.conn != emittingConnector ||
JMXConnectionNotification.FAILED.equals(cn.getType()))
remove = true;
}
// We need to unregister our listener from this 'removed' connector.
// This is the only place where we remove the listener.
//
if (remove) {
try {
// This may fail if the connector is already closed.
// But better unregister anyway...
//
emittingConnector.removeConnectionNotificationListener(
listener,null,
emittingConnector);
} catch (Exception x) {
LOG.log(Level.FINE,
"Failed to unregister connection listener"+x);
LOG.log(Level.FINEST,
"Failed to unregister connection listener",x);
}
try {
// This may fail if the connector is already closed.
// But better call close twice and get an exception than
// leaking...
//
emittingConnector.close();
} catch (Exception x) {
LOG.log(Level.FINEST,
"Failed to close old connector " +
"(failure was expected): "+x);
}
}
// Now we checked whether our current connector is still alive.
//
boolean closed = false;
final JMXConnector thisconn = this.conn;
try {
if (thisconn != null)
thisconn.getConnectionId();
} catch (IOException x) {
LOG.finest("Connector already closed: "+x);
closed = true;
}
// We got an IOException - the connector is not connected.
// Need to forget it and switch our state to closed.
//
if (closed) {
switchConnection(thisconn,null,null);
try {
// Usually this will fail... Better call close twice
// and get an exception than leaking...
//
if (thisconn != emittingConnector || !remove)
thisconn.close();
} catch (IOException x) {
LOG.log(Level.FINEST,
"Failed to close connector (failure was expected): "
+x);
}
}
}
private final void switchConnection(JMXConnector oldc,
JMXConnector newc,
MBeanServerConnection mbs) {
boolean connect = false;
boolean close = false;
synchronized (this) {
if (oldc != conn) {
if (newc != null) {
try {
newc.close();
} catch (IOException x) {
LOG.log(Level.FINEST,
"Failed to close connector",x);
}
}
return;
}
if (conn == null && newc != null) connect=true;
if (newc == null && conn != null) close = true;
conn = newc;
server = mbs;
}
if (connect || close) {
boolean oldstate = close;
boolean newstate = connect;
final ObjectName myName = getObjectName();
// In the uncommon case where the MBean is connected before
// being registered, myName can be null...
// If myName is null - we use 'this' as the source instead...
//
final Object source = (myName==null)?this:myName;
final AttributeChangeNotification acn =
new AttributeChangeNotification(source,
getNextSeqNumber(),System.currentTimeMillis(),
String.valueOf(source)+
(newstate?" connected":" closed"),
"Connected",
"boolean",
Boolean.valueOf(oldstate),
Boolean.valueOf(newstate));
sendNotification(acn);
}
}
private void closeall(JMXConnector... a) {
for (JMXConnector c : a) {
try {
if (c != null) c.close();
} catch (Exception x) {
// OK: we're gonna throw the original exception later.
LOG.finest("Ignoring exception when closing connector: "+x);
}
}
}
JMXConnector connect(JMXServiceURL url, Map<String,?> env)
throws IOException {
final JMXConnector c = newJMXConnector(jmxURL, env);
c.connect(env);
return c;
}
/**
* Creates a new JMXConnector with the specified {@code url} and
* {@code env} options map.
* <p>
* This method first calls {@link JMXConnectorFactory#newJMXConnector
* JMXConnectorFactory.newJMXConnector(jmxURL, env)} to obtain a new
* JMX connector, and returns that.
* </p>
* <p>
* 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:
* <pre>
* class JMXRemoteSubNamespace extends JMXRemoteNamespace {
* private final String subnamespace;
* JMXRemoteSubNamespace(JMXServiceURL url,
* Map{@code <String,?>} env, String subnamespace) {
* super(url,options);
* this.subnamespace = subnamespace;
* }
* protected JMXConnector newJMXConnector(JMXServiceURL url,
* Map<String,?> env) throws IOException {
* final JMXConnector inner = super.newJMXConnector(url,env);
* return {@link JMXNamespaces#narrowToNamespace(JMXConnector,String)
* JMXNamespaces.narrowToNamespace(inner,subnamespace)};
* }
* }
* </pre>
* </p>
* <p>
* 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.
* <p>
* 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.
* <br>
* 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:
* </p>
* <pre>
* class JMXRemoteEventClientNamespace extends JMXRemoteNamespace {
* JMXRemoteSubNamespaceConnector(JMXServiceURL url,
* Map<String,?> env) {
* super(url,options);
* }
* protected JMXConnector newJMXConnector(JMXServiceURL url,
* Map<String,?> env) throws IOException {
* final JMXConnector inner = super.newJMXConnector(url,env);
* return {@link EventClient#withEventClient(
* JMXConnector) EventClient.withEventClient(inner)};
* }
* }
* </pre>
* <p>
* 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.<br>
* 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).
* </p>
* @param url The JMXServiceURL of the remote server.
* @param optionsMap An unmodifiable options map that will be passed to the
* {@link JMXConnectorFactory} when {@linkplain
* JMXConnectorFactory#newJMXConnector creating} the
* {@link JMXConnector} that can connect to the remote source
* MBean Server.
* @return An unconnected JMXConnector to use to connect to the remote
* server
* @throws java.io.IOException if the connector could not be created.
* @see JMXConnectorFactory#newJMXConnector(javax.management.remote.JMXServiceURL, java.util.Map)
* @see #JMXRemoteNamespace
*/
protected JMXConnector newJMXConnector(JMXServiceURL url,
Map<String,?> optionsMap) throws IOException {
final JMXConnector c =
JMXConnectorFactory.newJMXConnector(jmxURL, optionsMap);
// TODO: uncomment this when contexts are added
// return ClientContext.withDynamicContext(c);
return c;
}
public void connect() throws IOException {
if (conn != null) {
try {
// This is much too fragile. It must go away!
PROBE_LOG.finest("Probing again...");
triggerProbe(getMBeanServerConnection());
} catch(Exception x) {
close();
Throwable cause = x;
// if the cause is a security exception - rethrows it...
while (cause != null) {
if (cause instanceof SecurityException)
throw (SecurityException) cause;
cause = cause.getCause();
}
throw new IOException("connection failed: cycle?",x);
}
}
LOG.fine("connecting...");
// TODO remove these traces
// System.err.println(getInitParameter()+" connecting");
final Map<String,Object> env =
new HashMap<String,Object>(getEnvMap());
try {
// XXX: We should probably document this...
// This allows to specify a loader name - which will be
// retrieved from the paret MBeanServer.
defaultClassLoader =
EnvHelp.resolveServerClassLoader(env,getMBeanServer());
} catch (InstanceNotFoundException x) {
final IOException io =
new IOException("ClassLoader not found");
io.initCause(x);
throw io;
}
env.put(JMXConnectorFactory.DEFAULT_CLASS_LOADER,defaultClassLoader);
final JMXServiceURL url = getJMXServiceURL();
final JMXConnector aconn = connect(url,env);
final MBeanServerConnection msc;
try {
msc = aconn.getMBeanServerConnection();
aconn.addConnectionNotificationListener(listener,null,aconn);
} catch (IOException io) {
closeall(aconn);
throw io;
} catch (RuntimeException x) {
closeall(aconn);
throw x;
}
// XXX Revisit here
// Note from the author: This business of switching connection is
// incredibly complex. Isn't there any means to simplify it?
//
switchConnection(conn,aconn,msc);
try {
triggerProbe(msc);
} catch(Exception x) {
close();
Throwable cause = x;
// if the cause is a security exception - rethrows it...
while (cause != null) {
if (cause instanceof SecurityException)
throw (SecurityException) cause;
cause = cause.getCause();
}
throw new IOException("connection failed: cycle?",x);
}
LOG.fine("connected.");
}
// If this is a self-linking namespace, this method should trigger
// the emission of a probe in the wrapping NamespaceInterceptor.
// The first call to source() in the wrapping NamespaceInterceptor
// causes the emission of the probe.
//
// Note: the MBeanServer returned by getSourceServer
// (our private JMXRemoteNamespaceDelegate inner class)
// implements a sun private interface (DynamicProbe) which is
// used by the NamespaceInterceptor to determine whether it should
// send a probe or not.
// We needed this interface here because the NamespaceInterceptor
// has otherwise no means to knows that this object has just
// connected, and that a new probe should be sent.
//
// Probes work this way: the NamespaceInterceptor sets a flag and sends
// a queryNames() request. If a queryNames() request comes in when the flag
// is on, then it deduces that there is a self-linking loop - and instead
// of calling queryNames() on the JMXNamespace (which would cause the
// loop to go on) it breaks the recursion by returning the probe ObjectName.
// If the NamespaceInterceptor receives the probe ObjectName as result of
// its original queryNames() it knows that it has been looping back on
// itslef and throws an Exception - which will be raised through this
// method, thus preventing the connection to be established...
//
// More info in the com.sun.jmx.namespace.NamespaceInterceptor class
//
// XXX: TODO this probe thing is way too complex and fragile.
// This *must* go away or be replaced by something simpler.
// ideas are welcomed.
//
private void triggerProbe(final MBeanServerConnection msc)
throws MalformedObjectNameException, IOException {
// Query Pattern that we will send through the source server in order
// to detect self-linking namespaces.
//
//
final ObjectName pattern;
pattern = ObjectName.getInstance("*" +
JMXNamespaces.NAMESPACE_SEPARATOR + ":" +
JMXNamespace.TYPE_ASSIGNMENT);
probed = false;
try {
msc.queryNames(pattern, null);
probed = true;
} catch (AccessControlException x) {
// if we have an MBeanPermission missing then do nothing...
if (!(x.getPermission() instanceof MBeanPermission))
throw x;
PROBE_LOG.finer("Can't check for cycles: " + x);
probed = false; // no need to do it again...
}
}
public void close() throws IOException {
if (conn == null) return;
LOG.fine("closing...");
// System.err.println(toString()+": closing...");
conn.close();
// System.err.println(toString()+": connector closed");
switchConnection(conn,null,null);
LOG.fine("closed.");
// System.err.println(toString()+": closed");
}
MBeanServerConnection getMBeanServerConnection() {
if (conn == null)
throw newRuntimeIOException("getMBeanServerConnection: not connected");
return server;
}
// Better than throwing UndeclaredThrowableException ...
private RuntimeException newRuntimeIOException(String msg) {
final IllegalStateException illegal = new IllegalStateException(msg);
return Util.newRuntimeIOException(new IOException(msg,illegal));
}
/**
* Returns the default class loader used by the underlying
* {@link JMXConnector}.
* @return the default class loader used when communicating with the
* remote source MBean server.
**/
ClassLoader getDefaultClassLoader() {
if (conn == null)
throw newRuntimeIOException("getMBeanServerConnection: not connected");
return defaultClassLoader;
}
public boolean isConnected() {
// This is a pleonasm
return (conn != null) && (server != null);
}
/**
* This name space handler will automatically {@link #close} its
* connection with the remote source in {@code preDeregister}.
**/
@Override
public void preDeregister() throws Exception {
try {
close();
} catch (IOException x) {
LOG.fine("Failed to close properly - exception ignored: " + x);
LOG.log(Level.FINEST,
"Failed to close properly - exception ignored",x);
}
super.preDeregister();
}
/**
* This method calls {@link
* javax.management.MBeanServerConnection#getMBeanCount
* getMBeanCount()} on the remote namespace.
* @throws java.io.IOException if an {@link IOException} is raised when
* communicating with the remote source namespace.
*/
@Override
public Integer getMBeanCount() throws IOException {
return getMBeanServerConnection().getMBeanCount();
}
/**
* This method returns the result of calling {@link
* javax.management.MBeanServerConnection#getDomains
* getDomains()} on the remote namespace.
* @throws java.io.IOException if an {@link IOException} is raised when
* communicating with the remote source namespace.
*/
@Override
public String[] getDomains() throws IOException {
return getMBeanServerConnection().getDomains();
}
/**
* This method returns the result of calling {@link
* javax.management.MBeanServerConnection#getDefaultDomain
* getDefaultDomain()} on the remote namespace.
* @throws java.io.IOException if an {@link IOException} is raised when
* communicating with the remote source namespace.
*/
@Override
public String getDefaultDomain() throws IOException {
return getMBeanServerConnection().getDefaultDomain();
}
/**
* Creates a new instance of {@code JMXRemoteNamespace}.
* @param sourceURL a JMX service URL that can be used to 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 An options map that will be passed to the
* {@link JMXConnectorFactory} when {@linkplain
* JMXConnectorFactory#newJMXConnector creating} the
* {@link JMXConnector} used to connect to the remote source
* MBean Server. Can be null, which is equivalent to an empty map.
* @see #JMXRemoteNamespace JMXRemoteNamespace(sourceURL,optionsMap)
* @see JMXConnectorFactory#newJMXConnector(javax.management.remote.JMXServiceURL, java.util.Map)
*/
public static JMXRemoteNamespace newJMXRemoteNamespace(
JMXServiceURL sourceURL,
Map<String,?> optionsMap) {
return new JMXRemoteNamespace(sourceURL, optionsMap);
}
}