/*
* 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.stream;
import org.jboss.logging.Logger;
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.Connector;
import org.jboss.remoting.transport.PortUtil;
import org.jboss.remoting.util.SecurityUtility;
import javax.management.MBeanServer;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
/**
* This is a helper class that runs internal to remoting on the
* client side. It contains a reference to a local input stream
* and creates a remoting server to receive calls from a target
* remoting server (via calls from a StreamHandler initiated by a
* server invoker handler).
* <p/>
* NOTE: That once this class receives the close() method called
* from the server, it will also stop and destroy the internal
* remoting server, since is assumed there will be no more callbacks
* (since the stream itself is closed).
*
* @author <a href="mailto:tom.elrod@jboss.com">Tom Elrod</a>
*/
public class StreamServer
{
private InputStream streamSource = null;
private String transport = "socket";
private String host = "localhost";
private int port = 5405;
private Connector connector = null;
private boolean internalConnector = true;
private static final Logger log = Logger.getLogger(StreamServer.class);
public static final String STREAM_TRANSPORT_KEY = "remoting.stream.transport";
public static final String STREAM_HOST_KEY = "remoting.stream.host";
public static final String STREAM_PORT_KEY = "remoting.stream.port";
private static final InetAddress LOCAL_HOST;
static
{
try
{
LOCAL_HOST = (InetAddress) AccessController.doPrivileged( new PrivilegedExceptionAction()
{
public Object run() throws UnknownHostException
{
try
{
return InetAddress.getLocalHost();
}
catch (UnknownHostException e)
{
return InetAddress.getByName("127.0.0.1");
}
}
});
}
catch (PrivilegedActionException e)
{
log.debug(StreamServer.class.getName() + " unable to get local host address", e.getCause());
throw new ExceptionInInitializerError(e.getCause());
}
catch (SecurityException e)
{
log.debug(StreamServer.class.getName() + " unable to get local host address", e);
throw e;
}
}
/**
* Creates the server wrapped around the specified input stream.
* This will create the remoting server as well.
*
* @param stream
* @throws Exception
*/
public StreamServer(InputStream stream) throws Exception
{
this.streamSource = stream;
String locatorURI = getLocatorURI();
setupServer(locatorURI);
}
public StreamServer(InputStream stream, String transport) throws Exception
{
this.transport = transport;
this.streamSource = stream;
String locatorURI = getLocatorURI();
setupServer(locatorURI);
}
public StreamServer(InputStream stream, InvokerLocator locator) throws Exception
{
this.streamSource = stream;
setupServer(locator.getLocatorURI());
}
public StreamServer(InputStream stream, Connector connector) throws Exception
{
this.streamSource = stream;
this.connector = connector;
if(connector != null)
{
if(!connector.isStarted())
{
throw new IllegalStateException("Connector (" + connector + ") passed to act as stream server has not been started.");
}
ServerInvocationHandler invocationHandler = new Handler(connector);
connector.addInvocationHandler("stream", invocationHandler);
internalConnector = false;
}
else
{
throw new NullPointerException("Connector passed to act as stream server can not be null.");
}
}
private String getLocatorURI() throws IOException
{
// check for system properties for locator values
transport = getSystemProperty(STREAM_TRANSPORT_KEY, transport);
try
{
host = getLocalHostName();
}
catch(UnknownHostException e)
{
try
{
InetAddress localAddress = getLocalHost();
host = localAddress.getHostAddress();
}
catch(UnknownHostException e1)
{
log.error("Stream server could not determine local host or address.");
}
}
host = getSystemProperty(STREAM_HOST_KEY, host);
String defaultPort = "" + PortUtil.findFreePort(host);
String sPort = getSystemProperty(STREAM_PORT_KEY, defaultPort);
try
{
port = Integer.parseInt(sPort);
}
catch(NumberFormatException e)
{
log.error("Stream server could not convert specified port " + sPort + " to a number.");
}
return transport + "://" + host + ":" + port;
}
/**
* Gets the locator to call back on this server to get the inputstream data.
*
* @return
* @throws Exception
*/
public String getInvokerLocator() throws Exception
{
String locator = null;
if(connector != null)
{
locator = connector.getInvokerLocator();
}
return locator;
}
public void setupServer(String locatorURI) throws Exception
{
InvokerLocator locator = new InvokerLocator(locatorURI);
connector = new Connector();
connector.setInvokerLocator(locator.getLocatorURI());
connector.create();
ServerInvocationHandler invocationHandler = new Handler(connector);
connector.addInvocationHandler("stream", invocationHandler);
connector.start();
}
/**
* Handler for accepting method calls on the input stream and perform the coresponding
* method call on the original input stream and returning the data.
*/
public class Handler implements ServerInvocationHandler
{
private Connector connector = null;
public Handler(Connector connector)
{
this.connector = connector;
}
public Object invoke(InvocationRequest invocation) throws Throwable
{
Object obj = invocation.getParameter();
// will expect the parameter to ALWAYS be of type StreamCallPaylod
if(obj instanceof StreamCallPayload)
{
StreamCallPayload payload = (StreamCallPayload) obj;
String method = payload.getMethod();
if(StreamHandler.READ.equals(method))
{
int i = streamSource.read();
return new Integer(i);
}
else if(StreamHandler.AVAILABLE.equals(method))
{
int i = streamSource.available();
return new Integer(i);
}
else if(StreamHandler.CLOSE.equals(method))
{
streamSource.close();
if(connector != null && internalConnector)
{
connector.stop();
connector.destroy();
}
return null;
}
else if(StreamHandler.RESET.equals(method))
{
streamSource.reset();
}
else if(StreamHandler.MARKSUPPORTED.equals(method))
{
boolean b = streamSource.markSupported();
return new Boolean(b);
}
else if(StreamHandler.MARKREADLIMIT.equals(method))
{
Object[] param = payload.getParams();
Integer intr = (Integer) param[0];
int readLimit = intr.intValue();
streamSource.mark(readLimit);
}
else if(StreamHandler.SKIP.equals(method))
{
Object[] param = payload.getParams();
Long lg = (Long) param[0];
long n = lg.longValue();
long ret = streamSource.skip(n);
return new Long(ret);
}
else if(StreamHandler.READBYTEARRAY.equals(method))
{
Object[] param = payload.getParams();
byte[] byteParam = (byte[]) param[0];
int i = streamSource.read(byteParam);
StreamCallPayload ret = new StreamCallPayload(StreamHandler.READBYTEARRAY);
ret.setParams(new Object[]{byteParam, new Integer(i)});
return ret;
}
else
{
throw new Exception("Unsupported method call - " + method);
}
}
else
{
log.error("Can not process invocation request because is not of type StreamCallPayload.");
throw new Exception("Invalid payload type. Must be of type StreamCallPayload.");
}
return null;
}
/**
* Adds a callback handler that will listen for callbacks from
* the server invoker handler.
*
* @param callbackHandler
*/
public void addListener(InvokerCallbackHandler callbackHandler)
{
// NO OP as do not handling callback listeners in this example
}
/**
* Removes the callback handler that was listening for callbacks
* from the server invoker handler.
*
* @param callbackHandler
*/
public void removeListener(InvokerCallbackHandler callbackHandler)
{
// NO OP as do not handling callback listeners in this example
}
/**
* set the mbean server that the handler can reference
*
* @param server
*/
public void setMBeanServer(MBeanServer server)
{
// NO OP as do not need reference to MBeanServer for this handler
}
/**
* set the invoker that owns this handler
*
* @param invoker
*/
public void setInvoker(ServerInvoker invoker)
{
// NO OP as do not need reference back to the server invoker
}
}
static private String getSystemProperty(final String name, final String defaultValue)
{
if (SecurityUtility.skipAccessControl())
return System.getProperty(name, defaultValue);
String value = null;
try
{
value = (String)AccessController.doPrivileged( new PrivilegedExceptionAction()
{
public Object run() throws Exception
{
return System.getProperty(name, defaultValue);
}
});
}
catch (PrivilegedActionException e)
{
throw (RuntimeException) e.getCause();
}
return value;
}
static private InetAddress getLocalHost() throws UnknownHostException
{
if (SecurityUtility.skipAccessControl())
{
return doGetLocalHost();
}
try
{
return (InetAddress) AccessController.doPrivileged( new PrivilegedExceptionAction()
{
public Object run() throws UnknownHostException
{
return doGetLocalHost();
}
});
}
catch (PrivilegedActionException e)
{
throw (UnknownHostException) e.getCause();
}
}
static private InetAddress doGetLocalHost() throws UnknownHostException
{
if (LOCAL_HOST != null)
{
return LOCAL_HOST;
}
try
{
return InetAddress.getLocalHost();
}
catch (UnknownHostException e)
{
return InetAddress.getByName("127.0.0.1");
}
}
static private String getLocalHostName() throws UnknownHostException
{
if (SecurityUtility.skipAccessControl())
{
return doGetLocalHost().getHostName();
}
try
{
return (String) AccessController.doPrivileged( new PrivilegedExceptionAction()
{
public Object run() throws UnknownHostException
{
return doGetLocalHost().getHostName();
}
});
}
catch (PrivilegedActionException e)
{
throw (UnknownHostException) e.getCause();
}
}
}