/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, 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 javax.management.remote.rmi;
import java.io.IOException;
import java.rmi.MarshalledObject;
import java.rmi.server.Unreferenced;
import java.security.SecureClassLoader;
import java.util.HashMap;
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.NotCompliantMBeanException;
import javax.management.NotificationFilter;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import javax.management.loading.ClassLoaderRepository;
import javax.management.remote.NotificationResult;
import javax.security.auth.Subject;
import org.jboss.logging.Logger;
import org.jboss.mx.remoting.rmi.ClientListenerHolder;
import org.jboss.mx.remoting.rmi.ClientNotificationProxy;
/**
* This is the implementation for the rmi connection. The un-marhsalling of marshalled
* objects is driven by the JSR-160 spec, section 2.11.2. Since this section is not very
* clear in certain situations, here is how it has been implemented:<p><ul>
* <li> <i>setAttribute(s)</i> uses the target MBean's classloader, then tries the default class loader </li>
* <li> <i>invoke</i> uses the target MBean's classloader, then tries the default class loader </li>
* <li> <i>createMBean</i> uses the traget MBeanServer's classloader, then tries the default class loader </li>
* <li> <i>queryNames/queryMBeans</i> uses only the default class loader </li>
* <li> <i>NotificationFilter/Object handback</i> uses the notification broadcaster's classloader, then
* tries the default class loader </li>
* </ul>
*
* @author <a href="mailto:tom.elrod@jboss.com">Tom Elrod</a>
*/
public class RMIConnectionImpl implements RMIConnection, Unreferenced
{
private RMIServerImpl rmiServer;
private String connectionId;
private ClassLoader defaultClassLoader;
private Subject subject;
private Map environment;
private ClientNotificationProxy notificationProxy;
protected static Logger log = Logger.getLogger(RMIConnectionImpl.class.getName());
//TODO: -TME -Need to fill out all the implementations which often does not do security checks, etc.
public RMIConnectionImpl(RMIServerImpl rmiServer, String connectionId, ClassLoader defaultClassLoader,
Subject subject, Map env)
{
this.rmiServer = rmiServer;
this.connectionId = connectionId;
this.defaultClassLoader = defaultClassLoader;
this.subject = subject;
if(env != null)
{
this.environment = env;
}
else
{
this.environment = new HashMap();
}
notificationProxy = new ClientNotificationProxy();
}
public String getConnectionId() throws IOException
{
return connectionId;
}
public void close() throws IOException
{
ClientListenerHolder[] holders = notificationProxy.getListeners();
if(holders != null)
{
for(int x = 0; x < holders.length; x++)
{
ClientListenerHolder holder = holders[x];
try
{
rmiServer.getMBeanServer().removeNotificationListener(holder.getObjectName(), notificationProxy, holder.getFilter(), holder.getHandback());
}
catch(InstanceNotFoundException e)
{
log.debug("Could not remove listener for target mbean " + holder.getObjectName() + " as instance is not found.");
}
catch(ListenerNotFoundException e)
{
log.debug("Could not remove listener for target mbean " + holder.getObjectName() + " as listener is not found.");
}
}
}
rmiServer.clientClosed(this);
}
public ObjectInstance createMBean(String className, ObjectName name, Subject delegationSubject)
throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, IOException
{
return rmiServer.getMBeanServer().createMBean(className, name);
}
public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName, Subject delegationSubject)
throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException,
MBeanException, NotCompliantMBeanException, InstanceNotFoundException, IOException
{
return rmiServer.getMBeanServer().createMBean(className, name, loaderName);
}
/**
* Loads the marshalled object using first the ClassLoaders passed as a parameter (if it is not null) and
* if that throws a ClassNotFoundException, will try the default class loader.
*
* @param classLoaderReposWrapper
* @param params
* @return
* @throws IOException
*/
private Object loadMarshalledObject(ClassLoader classLoaderReposWrapper, MarshalledObject params)
throws IOException
{
Object arg;
// try using the class loader of the mbean server before default class loader
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
// per spec (end of section 2), should try the classloader for the mbean server first
if(classLoaderReposWrapper != null)
{
try
{
Thread.currentThread().setContextClassLoader(classLoaderReposWrapper);
arg = params.get();
}
catch(ClassNotFoundException e)
{
Thread.currentThread().setContextClassLoader(defaultClassLoader);
try
{
arg = params.get();
}
catch(ClassNotFoundException e1)
{
throw new IOException(e1.getMessage());
}
}
finally
{
Thread.currentThread().setContextClassLoader(originalClassLoader);
}
}
else
{
try
{
Thread.currentThread().setContextClassLoader(defaultClassLoader);
arg = params.get();
}
catch(ClassNotFoundException cnfe)
{
throw new IOException(cnfe.getMessage());
}
finally
{
Thread.currentThread().setContextClassLoader(originalClassLoader);
}
}
return arg;
}
public ObjectInstance createMBean(String className, ObjectName name, MarshalledObject params,
String[] signature, Subject delegationSubject)
throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException,
MBeanException, NotCompliantMBeanException, IOException
{
// since have a MarshalledObject to deal with, need to use classloader from mbean server
ClassLoaderRepository classLoaderRepos = rmiServer.getMBeanServer().getClassLoaderRepository();
ClassLoader classLoaderReposWrapper = new ClassLoaderRepositoryWrapper(classLoaderRepos);
Object arg = loadMarshalledObject(classLoaderReposWrapper, params);
Object[] args = new Object[]{arg};
return rmiServer.getMBeanServer().createMBean(className, name, args, signature);
}
public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName,
MarshalledObject params, String[] signature, Subject delegationSubject)
throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException,
MBeanException, NotCompliantMBeanException, InstanceNotFoundException, IOException
{
ClassLoader mbeanSvrClassLoader = rmiServer.getMBeanServer().getClassLoader(loaderName);
Object arg = loadMarshalledObject(mbeanSvrClassLoader, params);
Object[] args = new Object[]{arg};
return rmiServer.getMBeanServer().createMBean(className, name, loaderName, args, signature);
}
public void unregisterMBean(ObjectName name, Subject delegationSubject)
throws InstanceNotFoundException, MBeanRegistrationException, IOException
{
rmiServer.getMBeanServer().unregisterMBean(name);
}
public ObjectInstance getObjectInstance(ObjectName name, Subject delegationSubject) throws InstanceNotFoundException, IOException
{
return rmiServer.getMBeanServer().getObjectInstance(name);
}
public Set queryMBeans(ObjectName name, MarshalledObject query, Subject delegationSubject) throws IOException
{
QueryExp filter = (QueryExp) loadMarshalledObject(query);
return rmiServer.getMBeanServer().queryMBeans(name, filter);
}
/**
* Loads the marshall object using only the default class loader only.
*
* @param object
* @return
* @throws IOException
*/
private Object loadMarshalledObject(MarshalledObject object) throws IOException
{
return loadMarshalledObject(null, object);
}
public Set queryNames(ObjectName name, MarshalledObject query, Subject delegationSubject) throws IOException
{
QueryExp filter = (QueryExp) loadMarshalledObject(query);
return rmiServer.getMBeanServer().queryNames(name, filter);
}
public boolean isRegistered(ObjectName name, Subject delegationSubject) throws IOException
{
return rmiServer.getMBeanServer().isRegistered(name);
}
public Integer getMBeanCount(Subject delegationSubject) throws IOException
{
return rmiServer.getMBeanServer().getMBeanCount();
}
public Object getAttribute(ObjectName name, String attribute, Subject delegationSubject)
throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException, IOException
{
return rmiServer.getMBeanServer().getAttribute(name, attribute);
}
public AttributeList getAttributes(ObjectName name, String[] attributes, Subject delegationSubject)
throws InstanceNotFoundException, ReflectionException, IOException
{
return rmiServer.getMBeanServer().getAttributes(name, attributes);
}
public void setAttribute(ObjectName name, MarshalledObject attribute, Subject delegationSubject)
throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException,
MBeanException, ReflectionException, IOException
{
ClassLoader mbeanLoader = rmiServer.getMBeanServer().getClassLoaderFor(name);
Attribute attrib = (Attribute) loadMarshalledObject(mbeanLoader, attribute);
rmiServer.getMBeanServer().setAttribute(name, attrib);
}
public AttributeList setAttributes(ObjectName name, MarshalledObject attributes, Subject delegationSubject)
throws InstanceNotFoundException, ReflectionException, IOException
{
ClassLoader mbeanLoader = rmiServer.getMBeanServer().getClassLoaderFor(name);
AttributeList attrib = (AttributeList) loadMarshalledObject(mbeanLoader, attributes);
return rmiServer.getMBeanServer().setAttributes(name, attrib);
}
public Object invoke(ObjectName name, String operationName, MarshalledObject params, String[] signature, Subject delegationSubject) throws InstanceNotFoundException, MBeanException, ReflectionException, IOException
{
ClassLoader mbeanLoader = rmiServer.getMBeanServer().getClassLoaderFor(name);
Object[] args = (Object[]) loadMarshalledObject(mbeanLoader, params);
return rmiServer.getMBeanServer().invoke(name, operationName, args, signature);
}
public String getDefaultDomain(Subject delegationSubject) throws IOException
{
return rmiServer.getMBeanServer().getDefaultDomain();
}
public String[] getDomains(Subject delegationSubject) throws IOException
{
return rmiServer.getMBeanServer().getDomains();
}
public MBeanInfo getMBeanInfo(ObjectName name, Subject delegationSubject) throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException
{
return rmiServer.getMBeanServer().getMBeanInfo(name);
}
public boolean isInstanceOf(ObjectName name, String className, Subject delegationSubject) throws InstanceNotFoundException, IOException
{
return rmiServer.getMBeanServer().isInstanceOf(name, className);
}
public void addNotificationListener(ObjectName name, ObjectName listener, MarshalledObject filter, MarshalledObject handback, Subject delegationSubject) throws InstanceNotFoundException, IOException
{
ClassLoader mbeanLoader = rmiServer.getMBeanServer().getClassLoaderFor(name);
NotificationFilter f = (NotificationFilter) loadMarshalledObject(mbeanLoader, filter);
Object o = loadMarshalledObject(mbeanLoader, handback);
rmiServer.getMBeanServer().addNotificationListener(name, listener, f, o);
}
public void removeNotificationListener(ObjectName name, ObjectName listener, Subject delegationSubject) throws InstanceNotFoundException, ListenerNotFoundException, IOException
{
rmiServer.getMBeanServer().removeNotificationListener(name, listener);
}
public void removeNotificationListener(ObjectName name, ObjectName listener, MarshalledObject filter, MarshalledObject handback, Subject delegationSubject) throws InstanceNotFoundException, ListenerNotFoundException, IOException
{
ClassLoader mbeanLoader = rmiServer.getMBeanServer().getClassLoaderFor(name);
NotificationFilter f = (NotificationFilter) loadMarshalledObject(mbeanLoader, filter);
Object o = loadMarshalledObject(mbeanLoader, handback);
rmiServer.getMBeanServer().removeNotificationListener(name, listener, f, o);
}
public Integer[] addNotificationListeners(ObjectName[] names, MarshalledObject[] filters, Subject[] delegationSubjects) throws InstanceNotFoundException, IOException
{
if(names == null || names.length == 0)
{
throw new IllegalArgumentException("Can not add notification listener without providing target mbean object name.");
}
Integer[] ids = new Integer[names.length];
for(int x = 0; x < names.length; x++)
{
ObjectName name = names[x];
MarshalledObject marshalledFilter = filters[x];
NotificationFilter filter = null;
if(marshalledFilter != null)
{
ClassLoader mbeanLoader = rmiServer.getMBeanServer().getClassLoaderFor(name);
filter = (NotificationFilter) loadMarshalledObject(mbeanLoader, marshalledFilter);
}
Integer id = notificationProxy.createListenerId(name, filter);
try
{
rmiServer.getMBeanServer().addNotificationListener(name, notificationProxy, filter, id);
}
catch(InstanceNotFoundException e)
{
notificationProxy.removeListener(id);
throw e;
}
ids[x] = id;
}
return ids;
}
public void removeNotificationListeners(ObjectName name, Integer[] listenerIDs, Subject delegationSubject) throws InstanceNotFoundException, ListenerNotFoundException, IOException
{
for(int x = 0; x < listenerIDs.length; ++x)
{
Integer id = listenerIDs[x];
NotificationFilter filter = notificationProxy.removeListener(id);
rmiServer.getMBeanServer().removeNotificationListener(name, notificationProxy, filter, id);
}
}
public NotificationResult fetchNotifications(long clientSequenceNumber, int maxNotifications, long timeout) throws IOException
{
return notificationProxy.fetchNotifications(clientSequenceNumber, maxNotifications, timeout);
}
/**
* Called by the RMI runtime sometime after the runtime determines that
* the reference list, the list of clients referencing the remote object,
* becomes empty.
*
* @since JDK1.1
*/
public void unreferenced()
{
log.debug("RMIConnectionImpl::unreferenced called (meaning no client is referencing this connections any longer. " +
"Will close connection.");
try
{
close();
}
catch(IOException e)
{
log.error("Error closing connection due to unreferenced.", e);
}
}
private static class ClassLoaderRepositoryWrapper extends SecureClassLoader
{
private final ClassLoaderRepository mbeanSvrRepository;
private ClassLoaderRepositoryWrapper(ClassLoaderRepository repos)
{
this.mbeanSvrRepository = repos;
}
public Class loadClass(String name) throws ClassNotFoundException
{
return mbeanSvrRepository.loadClass(name);
}
}
}