/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt 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.remoting.transport.rmi;
import org.jboss.logging.Logger;
import org.jboss.remoting.AbstractInvoker;
import org.jboss.remoting.CannotConnectException;
import org.jboss.remoting.ConnectionFailedException;
import org.jboss.remoting.InvokerLocator;
import org.jboss.remoting.RemoteClientInvoker;
import org.jboss.remoting.Remoting;
import org.jboss.remoting.Version;
import org.jboss.remoting.marshal.Marshaller;
import org.jboss.remoting.marshal.MarshallerDecorator;
import org.jboss.remoting.marshal.UnMarshaller;
import org.jboss.remoting.marshal.VersionedMarshaller;
import org.jboss.remoting.marshal.rmi.RMIMarshaller;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.HashMap;
import java.util.Map;
/**
* RMIClientInvoker
*
* @author <a href="mailto:jhaynie@vocalocity.net">Jeff Haynie</a>
* @author <a href="mailto:telrod@vocalocity.net">Tom Elrod</a>
* @version $Revision: 2390 $
*/
public class RMIClientInvoker extends RemoteClientInvoker
{
private static final Logger log = Logger.getLogger(RMIClientInvoker.class);
private static final boolean isTraceEnabled = log.isTraceEnabled();
private RMIServerInvokerInf server;
/**
* Need flag to indicate if have been able to lookup registry and set stub.
* Can't do this in the constructor, as need to throw CannotConnectException so
* for clustering capability.
*
* @param locator
*/
private boolean connected = false;
public RMIClientInvoker(InvokerLocator locator)
{
super(locator);
}
public RMIClientInvoker(InvokerLocator locator, Map configuration)
{
super(locator, configuration);
}
private int getRegistryPort(InvokerLocator locator)
{
int port = RMIServerInvoker.DEFAULT_REGISTRY_PORT;
// See if locator contains a specific registry port
Map params = locator.getParameters();
if(params != null)
{
String value = (String) params.get(RMIServerInvoker.REGISTRY_PORT_KEY);
if(value != null)
{
try
{
port = Integer.parseInt(value);
log.debug("Using port " + port + " for rmi registry.");
}
catch(NumberFormatException e)
{
log.error("Can not set the RMIServerInvoker RMI registry to port " + value + ". This is not a valid port number.");
}
}
}
return port;
}
/**
* get the server stub
*
* @param server
*/
public void setServerStub(RMIServerInvokerInf server)
{
this.server = server;
log.trace(this.server);
}
/**
* return the RMI server stub
*
* @return
*/
public RMIServerInvokerInf getServerStub()
{
return this.server;
}
/**
* subclasses must implement this method to provide a hook to connect to the remote server, if this applies
* to the specific transport. However, in some transport implementations, this may not make must difference since
* the connection is not persistent among invocations, such as SOAP. In these cases, the method should
* silently return without any processing.
*
* @throws ConnectionFailedException
*/
protected void handleConnect()
throws ConnectionFailedException
{
//TODO: -TME Need to figure this out a little better as am now dealing with
// with 2 ports, the rmi server and the registry.
try
{
storeLocalConfig(configuration);
String host = locator.getHost();
int port = getRegistryPort(locator);
Registry regsitry = LocateRegistry.getRegistry(host, port);
Remote remoteObj = regsitry.lookup("remoting/RMIServerInvoker/" + locator.getPort());
log.debug("Remote RMI Stub: " + remoteObj);
setServerStub((RMIServerInvokerInf) remoteObj);
connected = true;
}
catch(Exception e)
{
connected = false;
log.debug("Error connecting RMI invoker client.", e);
throw new CannotConnectException("Error connecting RMI invoker client", e);
}
}
/**
* subclasses must implement this method to provide a hook to disconnect from the remote server, if this applies
* to the specific transport. However, in some transport implementations, this may not make must difference since
* the connection is not persistent among invocations, such as SOAP. In these cases, the method should
* silently return without any processing.
*/
protected void handleDisconnect()
{
}
protected String getDefaultDataType()
{
return RMIMarshaller.DATATYPE;
}
protected void storeLocalConfig(Map config)
{
HashMap localConfig = new HashMap(config);
// If a specific SocketFactory was passed in, use it. If a SocketFactory was
// generated from SSL parameters, discard it. It will be recreated later by
// SerializableSSLClientSocketFactory with any additional parameters sent
// from server.
if (socketFactory != null &&
!socketFactoryCreatedFromSSLParameters &&
AbstractInvoker.isCompleteSocketFactory(socketFactory))
localConfig.put(Remoting.CUSTOM_SOCKET_FACTORY, socketFactory);
// Save configuration for SerializableSSLClientSocketFactory.
RemotingRMIClientSocketFactory.addLocalConfiguration(locator, localConfig);
}
protected Object transport(String sessionId, Object invocation, Map metadata, Marshaller marshaller, UnMarshaller unmarshaller)
throws IOException, ConnectionFailedException
{
if(this.server == null)
{
log.error("Server stub has not been set in RMI invoker client. See previous errors for details.");
//throw new IOException("Server stub hasn't been set!");
throw new CannotConnectException("Server stub has not been set.");
}
try
{
Object payload = invocation;
if(marshaller != null && !(marshaller instanceof RMIMarshaller))
{
if(marshaller instanceof MarshallerDecorator)
{
payload = ((MarshallerDecorator) marshaller).addDecoration(payload);
}
else
{
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
if (marshaller instanceof VersionedMarshaller)
((VersionedMarshaller) marshaller).write(payload, byteOut, Version.getDefaultVersion());
else
marshaller.write(payload, byteOut);
ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
ObjectInputStream ois = new ObjectInputStream(byteIn);
try
{
byteOut.close();
payload = ois.readObject();
ois.close();
}
catch(ClassNotFoundException e)
{
log.error("Could not marshall invocation payload object " + payload, e);
throw new IOException(e.getMessage());
}
}
}
Object response = server.transport(payload);
/* Jira Issue: JBREM-167
if(unmarshaller != null && !(unmarshaller instanceof RMIUnMarshaller))
{
PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream();
pos.connect(pis);
ObjectOutputStream oos = new ObjectOutputStream(pos);
oos.writeObject(response);
oos.flush();
oos.reset();
oos.writeObject(Boolean.TRUE);
oos.flush();
oos.reset();
try
{
oos.close();
response = unmarshaller.read(pis, metadata);
pis.close();
}
catch(ClassNotFoundException e)
{
log.error("Could not unmarshall invocation response" + response, e);
throw new IOException(e.getMessage());
}
}
*/
return response;
}
catch(RemoteException e)
{
log.error(e);
throw new CannotConnectException("Error making invocation in RMI client invoker.", e);
}
}
}