/*
* 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 org.jboss.invocation.jrmp.server;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
import java.lang.reflect.Method;
import javax.management.ObjectName;
import javax.naming.InitialContext;
import org.jboss.invocation.InvokerInterceptor;
import org.jboss.invocation.Invocation;
import org.jboss.invocation.MarshalledInvocation;
import org.jboss.naming.Util;
import org.jboss.proxy.ClientMethodInterceptor;
import org.jboss.proxy.GenericProxyFactory;
import org.jboss.system.Registry;
import org.jboss.system.ServiceMBeanSupport;
import org.jboss.metadata.MetaData;
import org.w3c.dom.Element;
/** Create an interface proxy that uses RMI/JRMP to communicate with the server
* side object that exposes the corresponding JMX invoke operation. Requests
* make through the proxy are sent to the JRMPInvoker instance the proxy
* is bound to.
*
* @author Scott.Stark@jboss.org
* @author <a href="mailto:galder.zamarreno@jboss.com">Galder Zamarreno</a>
* @version $Revision: 81030 $
*/
public class JRMPProxyFactory extends ServiceMBeanSupport
implements JRMPProxyFactoryMBean
{
/** The server side JRMPInvoker mbean that will handle RMI/JRMP transport */
private ObjectName invokerName;
/** The server side mbean that exposes the invoke operation for the
exported interface */
private ObjectName targetName;
/** The Proxy object which uses the proxy as its handler */
protected Object theProxy;
/** The JNDI name under which the proxy will be bound */
private String jndiName;
/** The interface that the proxy implements */
private Class[] exportedInterfaces;
/** The optional definition */
private Element interceptorConfig;
/** The interceptor Classes defined in the interceptorConfig */
private ArrayList interceptorClasses = new ArrayList();
/** invoke target method */
private boolean invokeTargetMethod;
/** methods by their hash code */
private final Map methodMap = new HashMap();
/** signatures by method */
private final Map signatureMap = new HashMap();
public JRMPProxyFactory()
{
interceptorClasses.add(ClientMethodInterceptor.class);
interceptorClasses.add(InvokerInterceptor.class);
}
public ObjectName getInvokerName()
{
return invokerName;
}
public void setInvokerName(ObjectName invokerName)
{
this.invokerName = invokerName;
}
public ObjectName getTargetName()
{
return targetName;
}
public void setTargetName(ObjectName targetName)
{
this.targetName = targetName;
}
public String getJndiName()
{
return jndiName;
}
public void setJndiName(String jndiName)
{
this.jndiName = jndiName;
}
public Class getExportedInterface()
{
return exportedInterfaces[0];
}
public void setExportedInterface(Class exportedInterface)
{
this.exportedInterfaces = new Class[] {exportedInterface};
}
public Class[] getExportedInterfaces()
{
return exportedInterfaces;
}
public void setExportedInterfaces(Class[] exportedInterfaces)
{
this.exportedInterfaces = exportedInterfaces;
}
public boolean getInvokeTargetMethod()
{
return invokeTargetMethod;
}
public void setInvokeTargetMethod(boolean invokeTargetMethod)
{
this.invokeTargetMethod = invokeTargetMethod;
}
public Element getClientInterceptors()
{
return interceptorConfig;
}
public void setClientInterceptors(Element config) throws Exception
{
this.interceptorConfig = config;
Iterator interceptorElements = MetaData.getChildrenByTagName(interceptorConfig, "interceptor");
ClassLoader loader = Thread.currentThread().getContextClassLoader();
interceptorClasses.clear();
while( interceptorElements != null && interceptorElements.hasNext() )
{
Element ielement = (Element) interceptorElements.next();
String className = null;
className = MetaData.getElementContent(ielement);
Class clazz = loader.loadClass(className);
interceptorClasses.add(clazz);
log.debug("added interceptor type: "+clazz);
}
}
public Object getProxy()
{
return theProxy;
}
public Object invoke(Invocation mi) throws Exception
{
final boolean remoteInvocation = mi instanceof MarshalledInvocation;
if(remoteInvocation)
{
((MarshalledInvocation)mi).setMethodMap(methodMap);
}
final Object result;
if(invokeTargetMethod)
{
String signature[] = (String[])signatureMap.get(mi.getMethod());
result = server.invoke(targetName, mi.getMethod().getName(), mi.getArguments(), signature);
}
else
{
result = server.invoke(targetName, "invoke", new Object[]{mi}, Invocation.INVOKE_SIGNATURE);
}
return result;
}
/** Initializes the servlet.
*/
protected void startService() throws Exception
{
/* Create a binding between the invoker name hash and the jmx name
This is used by the JRMPInvoker to map from the Invocation ObjectName
hash value to the target JMX ObjectName.
*/
Integer nameHash = new Integer(getServiceName().hashCode());
Registry.bind(nameHash, getServiceName());
// Create the service proxy
Object cacheID = null;
String proxyBindingName = null;
Class[] ifaces = exportedInterfaces;
ClassLoader loader = Thread.currentThread().getContextClassLoader();
createProxy(cacheID, proxyBindingName, loader, ifaces);
log.debug("Created JRMPPRoxy for service="+targetName
+", nameHash="+nameHash+", invoker="+invokerName);
if( jndiName != null )
{
InitialContext iniCtx = new InitialContext();
Util.rebind(iniCtx, jndiName, theProxy);
log.debug("Bound proxy under jndiName="+jndiName);
}
for(int i = 0; i < exportedInterfaces.length; ++i)
{
final Method[] methods = exportedInterfaces[i].getMethods();
for(int j = 0; j < methods.length; ++j)
{
methodMap.put(new Long(MarshalledInvocation.calculateHash(methods[j])), methods[j]);
String signature[];
final Class[] types = methods[j].getParameterTypes();
if(types == null || types.length == 0)
{
signature = null;
}
else
{
signature = new String[types.length];
for(int typeInd = 0; typeInd < types.length; ++typeInd)
{
signature[typeInd] = types[typeInd].getName();
}
}
signatureMap.put(methods[j], signature);
}
}
}
protected void stopService() throws Exception
{
Integer nameHash = new Integer(getServiceName().hashCode());
Registry.unbind(nameHash);
if( jndiName != null )
{
InitialContext iniCtx = new InitialContext();
Util.unbind(iniCtx, jndiName);
}
this.theProxy = null;
}
protected void destroyService() throws Exception
{
interceptorClasses.clear();
}
protected void createProxy
(
Object cacheID,
String proxyBindingName,
ClassLoader loader,
Class[] ifaces
)
{
GenericProxyFactory proxyFactory = new GenericProxyFactory();
theProxy = proxyFactory.createProxy(cacheID, getServiceName(), invokerName,
jndiName, proxyBindingName, interceptorClasses, loader, ifaces);
}
protected void rebind() throws Exception
{
log.debug("(re-)Binding " + jndiName);
Util.rebind(new InitialContext(), jndiName, theProxy);
}
protected ArrayList getInterceptorClasses()
{
return interceptorClasses;
}
}