/**
* Helios, OpenSource Monitoring
* Brought to you by the Helios Development Group
*
* Copyright 2007, Helios Development Group and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
*/
package org.helios.gmx.jmx.remote;
import groovy.lang.Binding;
import groovy.lang.Closure;
import groovy.lang.GroovyShell;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.NotCompliantMBeanException;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.OperationsException;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import javax.management.loading.ClassLoaderRepository;
/**
* <p>Title: RemotableMBeanServer</p>
* <p>Description: An MBean that provides remote MBeanServer operations.</p>
* <p>Company: Helios Development Group LLC</p>
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p><code>org.helios.gmx.jmx.remote.RemotableMBeanServer</code></p>
*/
public class RemotableMBeanServer implements RemotableMBeanServerMBean, Serializable {
/** */
private static final long serialVersionUID = 5409466004786907167L;
/** The registration injected MBeanServer */
protected transient MBeanServer server = null;
/** The ObjectName this MBean is registered as */
protected ObjectName objectName = null;
/** The URL of the reverse class loader */
protected URL reverseClassLoadURL;
/** This MBean's class loader */
protected final ClassLoader classLoader;
/** The reverse class loader host */
protected String reverseClassLoadHost;
/** The reverse class loader port */
protected int reverseClassLoadPort;
/** Invocation Context Classloader */
protected ClassLoader invocationContextClassLoader = null;
/** The Gmx class */
protected final Class<?> gmxClass;
/** The Gmx newInstance method */
protected final Method newInstance;
/** The Gmx instance */
protected Object gmx;
/**
* Creates a new RemotableMBeanServer
* @param reverseClassLoadURL The URL of the reverse class loader
*/
public RemotableMBeanServer(URL reverseClassLoadURL) {
this();
this.reverseClassLoadURL = reverseClassLoadURL;
reverseClassLoadHost = this.reverseClassLoadURL.getHost();
reverseClassLoadPort = this.reverseClassLoadURL.getPort();
invocationContextClassLoader = new URLClassLoader(new URL[]{this.reverseClassLoadURL}, classLoader);
}
/**
* Creates a new RemotableMBeanServer
*/
public RemotableMBeanServer() {
classLoader = getClass().getClassLoader();
try {
gmxClass = Class.forName("org.helios.gmx.Gmx");
} catch (Exception e) {
throw new RuntimeException("Failed to load Gmx class", e);
}
try {
newInstance = gmxClass.getDeclaredMethod("newInstance", MBeanServer.class);
} catch (Exception e) {
throw new RuntimeException("Failed to reflect Gmx.newInstance method", e);
}
}
/**
* Sets the reverse class loader URL
* @param reverseClassLoadURL The URL of the reverse class loader
*/
@Override
public void setReverseClassLoadURL(URL reverseClassLoadURL) {
this.reverseClassLoadURL = reverseClassLoadURL;
reverseClassLoadHost = this.reverseClassLoadURL.getHost();
reverseClassLoadPort = this.reverseClassLoadURL.getPort();
invocationContextClassLoader = new URLClassLoader(new URL[]{this.reverseClassLoadURL}, classLoader);
}
/**
* Invokes the closure extracted from the passed byte array and returns the result
* @param closureBytes The closure serialized as a byte array
* @param arguments optional arguments
* @return the return value of the closure
*/
public Object invokeClosure(byte[] closureBytes, Object...arguments) {
System.out.println("\n\tExtracting Closure from byte array:" + closureBytes.length + " Bytes\n");
Closure<?> closure = null;
ClassLoader current = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(invocationContextClassLoader);
ByteArrayInputStream bais = new ByteArrayInputStream(closureBytes);
ObjectInputStream ois = new ObjectInputStream(bais);
closure = (Closure<?>)ois.readObject();
return invokeClosure(closure, arguments);
} catch (Exception e) {
e.printStackTrace(System.err);
throw new RuntimeException("Failed to extract closure form byte array", e);
} finally {
Thread.currentThread().setContextClassLoader(current);
}
}
/**
* Invokes the passed closure and returns the result
* @param closure The closure
* @param arguments optional arguments
* @return the return value of the closure
*/
@Override
public Object invokeClosure(Closure<?> closure, Object[] arguments) {
System.out.println("\n\tInvoking Closure\n");
ClassLoader current = Thread.currentThread().getContextClassLoader();
try {
//Thread.currentThread().setContextClassLoader(invocationContextClassLoader);
int argsSize = (arguments==null ? 0 : arguments.length);
Object[] args = new Object[argsSize+1];
args[0] = gmx;
for(int i = 0; i < argsSize; i++) {
args[i+1] = arguments[i];
}
Object val = closure.call(args);
return val;
} catch (Exception e) {
e.printStackTrace(System.err);
throw new RuntimeException("Failed to invoke closure", e);
} finally {
Thread.currentThread().setContextClassLoader(current);
}
}
/**
* Invokes the submitted script passing in the MBeanServer as a binding and returning the script's return value.
* @param script The script to execute.
* @param args Arguments to the script
* @return the script's return value
*/
public Object invokeScript(String script, Object...args) {
try {
Map<String, Object> binds = new HashMap<String, Object>(1);
binds.put("server", server);
binds.put("arguments", args);
GroovyShell shell = new GroovyShell(new Binding(binds));
return shell.evaluate(script);
} catch (Exception e) {
e.printStackTrace(System.err);
throw new RuntimeException("Failed to invoke script", e);
}
}
/**
* The URL of the reverse class loader
* @return the reverseClassLoadURL
*/
public URL getReverseClassLoadURL() {
return reverseClassLoadURL;
}
/**
* This MBean's class loader name
* @return the classLoader
*/
public String getClassLoader() {
return classLoader.toString();
}
/**
* The reverse class loader host
* @return the reverseClassLoadHost
*/
public String getReverseClassLoadHost() {
return reverseClassLoadHost;
}
/**
* The reverse class loader port
* @return the reverseClassLoadPort
*/
public int getReverseClassLoadPort() {
return reverseClassLoadPort;
}
/**
* Returns the JMX domain names of all located MBeanServers in this JVM
* @return the JMX domain names of all located MBeanServers in this JVM
*/
public String[] getMBeanServerDomains() {
try {
Set<String> domains = new HashSet<String>();
for(MBeanServer server: MBeanServerFactory.findMBeanServer(null)) {
domains.add(server.getDefaultDomain()==null ? "null" : server.getDefaultDomain());
}
return domains.toArray(new String[domains.size()]);
} catch (Exception e) {
e.printStackTrace(System.err);
return new String[]{};
}
}
/**
* {@inheritDoc}
* @see javax.management.MBeanRegistration#postDeregister()
*/
@Override
public void postDeregister() {
}
/**
* {@inheritDoc}
* @see javax.management.MBeanRegistration#postRegister(java.lang.Boolean)
*/
@Override
public void postRegister(Boolean registrationDone) {
}
/**
* {@inheritDoc}
* @see javax.management.MBeanRegistration#preDeregister()
*/
@Override
public void preDeregister() throws Exception {
}
/**
* {@inheritDoc}
* @see javax.management.MBeanRegistration#preRegister(javax.management.MBeanServer, javax.management.ObjectName)
*/
@Override
public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {
this.server = server;
objectName = name;
try {
gmx = newInstance.invoke(null, server);
} catch (Exception e) {
throw new RuntimeException("Failed to instantiate Gmx", e);
}
return name;
}
/**
* @param name
* @param listener
* @param filter
* @param handback
* @throws InstanceNotFoundException
* @see javax.management.MBeanServer#addNotificationListener(javax.management.ObjectName, javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
*/
public void addNotificationListener(ObjectName name,
NotificationListener listener, NotificationFilter filter,
Object handback) throws InstanceNotFoundException {
server.addNotificationListener(name, listener, filter, handback);
}
/**
* @param name
* @param listener
* @param filter
* @param handback
* @throws InstanceNotFoundException
* @see javax.management.MBeanServer#addNotificationListener(javax.management.ObjectName, javax.management.ObjectName, javax.management.NotificationFilter, java.lang.Object)
*/
public void addNotificationListener(ObjectName name, ObjectName listener,
NotificationFilter filter, Object handback)
throws InstanceNotFoundException {
server.addNotificationListener(name, listener, filter, handback);
}
/**
* @param className
* @param name
* @param params
* @param signature
* @return
* @throws ReflectionException
* @throws InstanceAlreadyExistsException
* @throws MBeanRegistrationException
* @throws MBeanException
* @throws NotCompliantMBeanException
* @see javax.management.MBeanServer#createMBean(java.lang.String, javax.management.ObjectName, java.lang.Object[], java.lang.String[])
*/
public ObjectInstance createMBean(String className, ObjectName name,
Object[] params, String[] signature) throws ReflectionException,
InstanceAlreadyExistsException, MBeanRegistrationException,
MBeanException, NotCompliantMBeanException {
return server.createMBean(className, name, params, signature);
}
/**
* @param className
* @param name
* @param loaderName
* @param params
* @param signature
* @return
* @throws ReflectionException
* @throws InstanceAlreadyExistsException
* @throws MBeanRegistrationException
* @throws MBeanException
* @throws NotCompliantMBeanException
* @throws InstanceNotFoundException
* @see javax.management.MBeanServer#createMBean(java.lang.String, javax.management.ObjectName, javax.management.ObjectName, java.lang.Object[], java.lang.String[])
*/
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName, Object[] params, String[] signature)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException, InstanceNotFoundException {
return server.createMBean(className, name, loaderName, params,
signature);
}
/**
* @param className
* @param name
* @param loaderName
* @return
* @throws ReflectionException
* @throws InstanceAlreadyExistsException
* @throws MBeanRegistrationException
* @throws MBeanException
* @throws NotCompliantMBeanException
* @throws InstanceNotFoundException
* @see javax.management.MBeanServer#createMBean(java.lang.String, javax.management.ObjectName, javax.management.ObjectName)
*/
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName) throws ReflectionException,
InstanceAlreadyExistsException, MBeanRegistrationException,
MBeanException, NotCompliantMBeanException,
InstanceNotFoundException {
return server.createMBean(className, name, loaderName);
}
/**
* @param className
* @param name
* @return
* @throws ReflectionException
* @throws InstanceAlreadyExistsException
* @throws MBeanRegistrationException
* @throws MBeanException
* @throws NotCompliantMBeanException
* @see javax.management.MBeanServer#createMBean(java.lang.String, javax.management.ObjectName)
*/
public ObjectInstance createMBean(String className, ObjectName name)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException {
return server.createMBean(className, name);
}
/**
* @param name
* @param data
* @return
* @throws InstanceNotFoundException
* @throws OperationsException
* @deprecated
* @see javax.management.MBeanServer#deserialize(javax.management.ObjectName, byte[])
*/
public ObjectInputStream deserialize(ObjectName name, byte[] data)
throws InstanceNotFoundException, OperationsException {
return server.deserialize(name, data);
}
/**
* @param className
* @param data
* @return
* @throws OperationsException
* @throws ReflectionException
* @deprecated
* @see javax.management.MBeanServer#deserialize(java.lang.String, byte[])
*/
public ObjectInputStream deserialize(String className, byte[] data)
throws OperationsException, ReflectionException {
return server.deserialize(className, data);
}
/**
* @param className
* @param loaderName
* @param data
* @return
* @throws InstanceNotFoundException
* @throws OperationsException
* @throws ReflectionException
* @deprecated
* @see javax.management.MBeanServer#deserialize(java.lang.String, javax.management.ObjectName, byte[])
*/
public ObjectInputStream deserialize(String className,
ObjectName loaderName, byte[] data)
throws InstanceNotFoundException, OperationsException,
ReflectionException {
return server.deserialize(className, loaderName, data);
}
/**
* @param name
* @param attribute
* @return
* @throws MBeanException
* @throws AttributeNotFoundException
* @throws InstanceNotFoundException
* @throws ReflectionException
* @see javax.management.MBeanServer#getAttribute(javax.management.ObjectName, java.lang.String)
*/
public Object getAttribute(ObjectName name, String attribute)
throws MBeanException, AttributeNotFoundException,
InstanceNotFoundException, ReflectionException {
return server.getAttribute(name, attribute);
}
/**
* @param name
* @param attributes
* @return
* @throws InstanceNotFoundException
* @throws ReflectionException
* @see javax.management.MBeanServer#getAttributes(javax.management.ObjectName, java.lang.String[])
*/
public AttributeList getAttributes(ObjectName name, String[] attributes)
throws InstanceNotFoundException, ReflectionException {
return server.getAttributes(name, attributes);
}
/**
* @param loaderName
* @return
* @throws InstanceNotFoundException
* @see javax.management.MBeanServer#getClassLoader(javax.management.ObjectName)
*/
public ClassLoader getClassLoader(ObjectName loaderName)
throws InstanceNotFoundException {
return server.getClassLoader(loaderName);
}
/**
* @param mbeanName
* @return
* @throws InstanceNotFoundException
* @see javax.management.MBeanServer#getClassLoaderFor(javax.management.ObjectName)
*/
public ClassLoader getClassLoaderFor(ObjectName mbeanName)
throws InstanceNotFoundException {
return server.getClassLoaderFor(mbeanName);
}
/**
* @return
* @see javax.management.MBeanServer#getClassLoaderRepository()
*/
public ClassLoaderRepository getClassLoaderRepository() {
return server.getClassLoaderRepository();
}
/**
* @return
* @see javax.management.MBeanServer#getDefaultDomain()
*/
public String getDefaultDomain() {
return server.getDefaultDomain();
}
/**
* @return
* @see javax.management.MBeanServer#getDomains()
*/
public String[] getDomains() {
return server.getDomains();
}
/**
* @return
* @see javax.management.MBeanServer#getMBeanCount()
*/
public Integer getMBeanCount() {
return server.getMBeanCount();
}
/**
* @param name
* @return
* @throws InstanceNotFoundException
* @throws IntrospectionException
* @throws ReflectionException
* @see javax.management.MBeanServer#getMBeanInfo(javax.management.ObjectName)
*/
public MBeanInfo getMBeanInfo(ObjectName name)
throws InstanceNotFoundException, IntrospectionException,
ReflectionException {
return server.getMBeanInfo(name);
}
/**
* @param name
* @return
* @throws InstanceNotFoundException
* @see javax.management.MBeanServer#getObjectInstance(javax.management.ObjectName)
*/
public ObjectInstance getObjectInstance(ObjectName name)
throws InstanceNotFoundException {
return server.getObjectInstance(name);
}
/**
* @param className
* @param params
* @param signature
* @return
* @throws ReflectionException
* @throws MBeanException
* @see javax.management.MBeanServer#instantiate(java.lang.String, java.lang.Object[], java.lang.String[])
*/
public Object instantiate(String className, Object[] params,
String[] signature) throws ReflectionException, MBeanException {
return server.instantiate(className, params, signature);
}
/**
* @param className
* @param loaderName
* @param params
* @param signature
* @return
* @throws ReflectionException
* @throws MBeanException
* @throws InstanceNotFoundException
* @see javax.management.MBeanServer#instantiate(java.lang.String, javax.management.ObjectName, java.lang.Object[], java.lang.String[])
*/
public Object instantiate(String className, ObjectName loaderName,
Object[] params, String[] signature) throws ReflectionException,
MBeanException, InstanceNotFoundException {
return server.instantiate(className, loaderName, params, signature);
}
/**
* @param className
* @param loaderName
* @return
* @throws ReflectionException
* @throws MBeanException
* @throws InstanceNotFoundException
* @see javax.management.MBeanServer#instantiate(java.lang.String, javax.management.ObjectName)
*/
public Object instantiate(String className, ObjectName loaderName)
throws ReflectionException, MBeanException,
InstanceNotFoundException {
return server.instantiate(className, loaderName);
}
/**
* @param className
* @return
* @throws ReflectionException
* @throws MBeanException
* @see javax.management.MBeanServer#instantiate(java.lang.String)
*/
public Object instantiate(String className) throws ReflectionException,
MBeanException {
return server.instantiate(className);
}
/**
* @param name
* @param operationName
* @param params
* @param signature
* @return
* @throws InstanceNotFoundException
* @throws MBeanException
* @throws ReflectionException
* @see javax.management.MBeanServer#invoke(javax.management.ObjectName, java.lang.String, java.lang.Object[], java.lang.String[])
*/
public Object invoke(ObjectName name, String operationName,
Object[] params, String[] signature)
throws InstanceNotFoundException, MBeanException,
ReflectionException {
return server.invoke(name, operationName, params, signature);
}
/**
* @param name
* @param className
* @return
* @throws InstanceNotFoundException
* @see javax.management.MBeanServer#isInstanceOf(javax.management.ObjectName, java.lang.String)
*/
public boolean isInstanceOf(ObjectName name, String className)
throws InstanceNotFoundException {
return server.isInstanceOf(name, className);
}
/**
* @param name
* @return
* @see javax.management.MBeanServer#isRegistered(javax.management.ObjectName)
*/
public boolean isRegistered(ObjectName name) {
return server.isRegistered(name);
}
/**
* @param name
* @param query
* @return
* @see javax.management.MBeanServer#queryMBeans(javax.management.ObjectName, javax.management.QueryExp)
*/
public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
return server.queryMBeans(name, query);
}
/**
* @param name
* @param query
* @return
* @see javax.management.MBeanServer#queryNames(javax.management.ObjectName, javax.management.QueryExp)
*/
public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
return server.queryNames(name, query);
}
/**
* @param object
* @param name
* @return
* @throws InstanceAlreadyExistsException
* @throws MBeanRegistrationException
* @throws NotCompliantMBeanException
* @see javax.management.MBeanServer#registerMBean(java.lang.Object, javax.management.ObjectName)
*/
public ObjectInstance registerMBean(Object object, ObjectName name)
throws InstanceAlreadyExistsException, MBeanRegistrationException,
NotCompliantMBeanException {
return server.registerMBean(object, name);
}
/**
* @param name
* @param listener
* @param filter
* @param handback
* @throws InstanceNotFoundException
* @throws ListenerNotFoundException
* @see javax.management.MBeanServer#removeNotificationListener(javax.management.ObjectName, javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
*/
public void removeNotificationListener(ObjectName name,
NotificationListener listener, NotificationFilter filter,
Object handback) throws InstanceNotFoundException,
ListenerNotFoundException {
server.removeNotificationListener(name, listener, filter, handback);
}
/**
* @param name
* @param listener
* @throws InstanceNotFoundException
* @throws ListenerNotFoundException
* @see javax.management.MBeanServer#removeNotificationListener(javax.management.ObjectName, javax.management.NotificationListener)
*/
public void removeNotificationListener(ObjectName name,
NotificationListener listener) throws InstanceNotFoundException,
ListenerNotFoundException {
server.removeNotificationListener(name, listener);
}
/**
* @param name
* @param listener
* @param filter
* @param handback
* @throws InstanceNotFoundException
* @throws ListenerNotFoundException
* @see javax.management.MBeanServer#removeNotificationListener(javax.management.ObjectName, javax.management.ObjectName, javax.management.NotificationFilter, java.lang.Object)
*/
public void removeNotificationListener(ObjectName name,
ObjectName listener, NotificationFilter filter, Object handback)
throws InstanceNotFoundException, ListenerNotFoundException {
server.removeNotificationListener(name, listener, filter, handback);
}
/**
* @param name
* @param listener
* @throws InstanceNotFoundException
* @throws ListenerNotFoundException
* @see javax.management.MBeanServer#removeNotificationListener(javax.management.ObjectName, javax.management.ObjectName)
*/
public void removeNotificationListener(ObjectName name, ObjectName listener)
throws InstanceNotFoundException, ListenerNotFoundException {
server.removeNotificationListener(name, listener);
}
/**
* @param name
* @param attribute
* @throws InstanceNotFoundException
* @throws AttributeNotFoundException
* @throws InvalidAttributeValueException
* @throws MBeanException
* @throws ReflectionException
* @see javax.management.MBeanServer#setAttribute(javax.management.ObjectName, javax.management.Attribute)
*/
public void setAttribute(ObjectName name, Attribute attribute)
throws InstanceNotFoundException, AttributeNotFoundException,
InvalidAttributeValueException, MBeanException, ReflectionException {
server.setAttribute(name, attribute);
}
/**
* @param name
* @param attributes
* @return
* @throws InstanceNotFoundException
* @throws ReflectionException
* @see javax.management.MBeanServer#setAttributes(javax.management.ObjectName, javax.management.AttributeList)
*/
public AttributeList setAttributes(ObjectName name, AttributeList attributes)
throws InstanceNotFoundException, ReflectionException {
return server.setAttributes(name, attributes);
}
/**
* @param name
* @throws InstanceNotFoundException
* @throws MBeanRegistrationException
* @see javax.management.MBeanServer#unregisterMBean(javax.management.ObjectName)
*/
public void unregisterMBean(ObjectName name)
throws InstanceNotFoundException, MBeanRegistrationException {
server.unregisterMBean(name);
}
/**
* Merges internally supplied arguments to a closure with caller supplied arguments into one flat object array.
* @param suppliedArgs The caller supplied arguments
* @param injectedArgs The internal API supplied arguments
* @return an Object array
*/
public static Object[] mergeArguments(Object suppliedArgs, Object...injectedArgs) {
Object[] flattened = null;
List<Object> fobjs = new ArrayList<Object>();
if(injectedArgs!=null && injectedArgs.length>0) {
Collections.addAll(fobjs, injectedArgs);
}
if(suppliedArgs!=null) {
if(Object[].class.isAssignableFrom(suppliedArgs.getClass())) {
Collections.addAll(fobjs, (Object[])suppliedArgs);
} else {
fobjs.add(suppliedArgs);
}
}
flattened = new Object[fobjs.size()];
return fobjs.toArray(flattened);
}
}