/*
* 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.unified.server;
import java.rmi.MarshalledObject;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.jboss.invocation.Invocation;
import org.jboss.invocation.unified.interfaces.UnifiedInvokerProxy;
import org.jboss.mx.util.JMXExceptionDecoder;
import org.jboss.remoting.InvocationRequest;
import org.jboss.remoting.InvokerLocator;
import org.jboss.remoting.ServerInvocationHandler;
import org.jboss.remoting.ServerInvoker;
import org.jboss.remoting.callback.InvokerCallbackHandler;
import org.jboss.remoting.transport.ConnectorMBean;
import org.jboss.system.Registry;
import org.jboss.system.ServiceMBeanSupport;
/**
* This is a detached invoker which sits on top of jboss remoting.
* Since this uses remoting, the transport protocol used is defined within
* the remoting service and this, the UnifiedInvoker, is declared as the handler.
*
* @author <a href="mailto:tom.elrod@jboss.com">Tom Elrod</a>
*/
public class UnifiedInvoker extends ServiceMBeanSupport implements ServerInvocationHandler, UnifiedInvokerMBean
{
private ConnectorMBean connector;
private ServerInvoker serverInvoker;
private MBeanServer mbServer;
private boolean strictRMIException = false;
private UnifiedInvokerProxy proxy;
private String subsystem = "invoker";
/**
* If set to true, this will cause the UnifiedInvokerProxy (on the client side) to
* wrap all RemoteExceptions thrown from the server in a new ServerException. If false,
* will unwrap the original exception thrown from withint the RemoteException and throw that.
* The default is false.
*
* @param isStrict
*/
public void setStrictRMIException(boolean isStrict)
{
this.strictRMIException = isStrict;
}
/**
* A return of true means that the UnifiedInvokerProxy (on the client side) will wrap all
* RemoteExceptions within a new ServerException. A return of false, will unwrap the original
* exception thrown from within the RemoteException and throw that. The default, if not explicitly set,
* is false.
*
* @return
*/
public boolean getStrictRMIException()
{
return strictRMIException;
}
/**
* Gets the remoting subsystem being used.
* @return
*/
public String getSubSystem()
{
return subsystem;
}
public void setSubSystem(String subsystem)
{
this.subsystem = subsystem;
}
/**
* This may be called if set depends in config with optional-attribute-name.
*
* @param connector
*/
public void setConnector(ConnectorMBean connector)
{
this.connector = connector;
}
protected void createService() throws Exception
{
if(connector != null)
{
try
{
connector.addInvocationHandler(getSubSystem(), this);
}
catch(Exception e)
{
log.error("Error adding unified invoker as handler upon connector being set.", e);
}
}
}
/**
* Will get the invoker locator from the server invoker, start the server invoker, create the proxy,
* and bind the proxy.
*
* @throws Exception
*/
protected void startService() throws Exception
{
log.debug("Starting unified invoker service.");
InvokerLocator locator = null;
if(serverInvoker != null)
{
locator = serverInvoker.getLocator();
if(!serverInvoker.isStarted())
{
serverInvoker.start();
}
}
else if(connector != null)
{
locator = connector.getLocator();
}
else
{
/**
* This will happen in one of two scenarios. One, the unified invoker was not declared in as
* service before the connector AND was not specified as the handler within the connector config.
* Two, the unified invoker service config did not use the proxy-type attribute within the depends
* tag to have the container set the connector upon creating the unified invoker.
*/
log.error("Error referencing either remoting connector or server invoker to be used. " +
"Please check configuration to make sure proper dependancies are set.");
throw new RuntimeException("Error getting locator because server invoker is null.");
}
proxy = new UnifiedInvokerProxy(locator, strictRMIException);
jmxBind();
}
protected void jmxBind()
{
Registry.bind(getServiceName(), proxy);
}
/**
* Stops the server invoker.
*
* @throws Exception
*/
public void stopService() throws Exception
{
// JBAS-5590 -- the serverInvoker is a shared resource and shouldn't
// be stopped just because we don't want it any more
// if(serverInvoker != null)
// {
// serverInvoker.stop();
// }
}
/**
* Gives this JMX service a name.
*
* @return The Name value
*/
public String getName()
{
return "Unified-Invoker";
}
/**
* Gets the invoker locator string for this server
*
* @return
*/
public String getInvokerLocator()
{
if(serverInvoker != null)
{
return serverInvoker.getLocator().getLocatorURI();
}
else
{
return null;
}
}
/**
* Implementation of the server invoker handler interface. Will take the invocation request
* and invoke down the interceptor chain.
*
* @param invocationReq
* @return
* @throws Throwable
*/
public Object invoke(InvocationRequest invocationReq) throws Throwable
{
Invocation invocation = (Invocation) invocationReq.getParameter();
Thread currentThread = Thread.currentThread();
ClassLoader oldCl = currentThread.getContextClassLoader();
ObjectName mbean = null;
try
{
mbean = (ObjectName) Registry.lookup(invocation.getObjectName());
// The cl on the thread should be set in another interceptor
Object obj = getServer().invoke(mbean,
"invoke",
new Object[]{invocation},
Invocation.INVOKE_SIGNATURE);
return new MarshalledObject(obj);
}
catch(Exception e)
{
Throwable th = JMXExceptionDecoder.decode(e);
if(log.isTraceEnabled())
{
log.trace("Failed to invoke on mbean: " + mbean, th);
}
if(th instanceof Exception)
{
e = (Exception) th;
}
throw e;
}
finally
{
currentThread.setContextClassLoader(oldCl);
}
}
/**
* set the mbean server that the handler can reference
*
* @param server
*/
public void setMBeanServer(MBeanServer server)
{
mbServer = server;
}
public MBeanServer getServer()
{
return mbServer;
}
/**
* set the invoker that owns this handler
*
* @param invoker
*/
public void setInvoker(ServerInvoker invoker)
{
/**
* This is needed in case we need to make calls on the server invoker (for classloading
* in particular). Will just leave alone for now and come back to this when have
* a use to call on it.
*/
serverInvoker = invoker;
}
protected ServerInvoker getInvoker()
{
return serverInvoker;
}
/**
* Adds a callback handler that will listen for callbacks from
* the server invoker handler.
* This is a no op as don't expect the detached invokers to have callbacks
*
* @param callbackHandler
*/
public void addListener(InvokerCallbackHandler callbackHandler)
{
//NO OP - do not expect the detached invoker to have callbacks
}
/**
* Removes the callback handler that was listening for callbacks
* from the server invoker handler.
* This is a no op as don't expect the detached invokers to have callbacks
*
* @param callbackHandler
*/
public void removeListener(InvokerCallbackHandler callbackHandler)
{
//NO OP - do not expect the detached invoker to have callbacks
}
}