package ibis.ipl.server;
import ibis.ipl.IbisConfigurationException;
import ibis.ipl.support.Connection;
import ibis.smartsockets.SmartSocketsProperties;
import ibis.smartsockets.direct.DirectSocketAddress;
import ibis.smartsockets.hub.servicelink.ServiceLink;
import ibis.smartsockets.virtual.InitializationException;
import ibis.smartsockets.virtual.VirtualSocketAddress;
import ibis.smartsockets.virtual.VirtualSocketFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Properties;
/**
* Connection to a server in another JVM (possibly on another machine).
* Connection can be established either by connecting stdin/stdout to a server
* started with the "--remote" option, or by passing the address of the server.
*
* @author Niels Drost
*
*/
public class ServerConnection implements ServerInterface {
public static final int TIMEOUT = 10000;
private final ServerPipe pipe;
private final VirtualSocketAddress address;
private final ManagementServiceConnection managementConnection;
private final RegistryServiceConnection registryConnection;
private final VirtualSocketFactory socketFactory;
private static VirtualSocketAddress createServerAddress(
String serverString, int defaultPort) throws IOException {
if (serverString == null) {
throw new IbisConfigurationException("serverString undefined");
}
DirectSocketAddress address = null;
// maybe it is a DirectSocketAddress?
try {
address = DirectSocketAddress.getByAddress(serverString);
} catch (Throwable e) {
// IGNORE
}
if (address == null) {
// or only a host address
try {
address = DirectSocketAddress.getByAddress(serverString,
defaultPort);
} catch (Throwable e) {
throw new IOException(
"could not create server address from given string: "
+ serverString);
}
}
return new VirtualSocketAddress(address,
ServerConnectionProtocol.VIRTUAL_PORT, address, null);
}
private static VirtualSocketFactory createSocketFactory(String hubs)
throws IOException {
VirtualSocketFactory result;
Properties properties = new Properties();
if (hubs != null) {
properties.setProperty(SmartSocketsProperties.HUB_ADDRESSES, hubs);
}
try {
result = VirtualSocketFactory.createSocketFactory(properties, true);
} catch (InitializationException e) {
throw new IOException(e.getMessage());
}
// make this node disappear from the smartsockets viz
try {
ServiceLink sl = result.getServiceLink();
if (sl != null) {
sl.registerProperty("smartsockets.viz", "invisible");
}
} catch (Throwable e) {
// ignored
}
return result;
}
/**
* Creates a connection to the server with the given in and output stream.
* Will parse the address of the server from the standard out of the server process.
* Also forwards the output to the given stream (with an optional prefix to each
* line). When this connection is terminated, the (remote) server terminates
* as well.
*
* @param stdout
* Standard out of server process
* @param stdin
* Standard in of server process
* @param output
* Stream to forward output to
* @param outputPrefix
* Prefix to add to all lines of output
* @param timeout
* Number of milliseconds to wait for the server to become
* available
* @param socketFactory
* Socket factory to use for making connections. if null, a new
* factory will be created
*
* @throws IOException
* if the socket factory cannot be created
*/
public ServerConnection(InputStream stdout, OutputStream stdin,
PrintStream output, String outputPrefix, long timeout,
VirtualSocketFactory socketFactory) throws IOException {
pipe = new ServerPipe(stdout, stdin, output, outputPrefix);
address = createServerAddress(pipe.getAddress(timeout),
ServerProperties.DEFAULT_PORT);
if (socketFactory == null) {
this.socketFactory = createSocketFactory(null);
} else {
this.socketFactory = socketFactory;
}
managementConnection = new ManagementServiceConnection(this.address,
this.socketFactory);
registryConnection = new RegistryServiceConnection(this.address,
this.socketFactory);
}
/**
* Creates a connection to the server at the given address.
*
* @param address
* address of the server
* @param socketFactory
* Socket factory to use for making connections. if null, a new
* factory will be created
* @throws IOException
* in case the socket factory cannot be created
*/
public ServerConnection(String address, VirtualSocketFactory socketFactory)
throws IOException {
pipe = null;
this.address = createServerAddress(address,
ServerProperties.DEFAULT_PORT);
if (socketFactory == null) {
this.socketFactory = createSocketFactory(null);
} else {
this.socketFactory = socketFactory;
}
managementConnection = new ManagementServiceConnection(this.address,
this.socketFactory);
registryConnection = new RegistryServiceConnection(this.address,
this.socketFactory);
}
/**
* Connections to the server at the given address. A list of hubs to use to
* connect to the server must also be provided.
*
* @param address
* address of the server
* @param hubs
* list of hubs to use.
* @throws IOException
* in case the SocketFactory could not be created
*
*/
public ServerConnection(String address, String hubs) throws IOException {
pipe = null;
Properties properties = new Properties();
properties.put(SmartSocketsProperties.HUB_ADDRESSES, hubs);
this.socketFactory = createSocketFactory(hubs);
this.address = createServerAddress(getAddress(),
ServerProperties.DEFAULT_PORT);
managementConnection = new ManagementServiceConnection(this.address,
this.socketFactory);
registryConnection = new RegistryServiceConnection(this.address,
this.socketFactory);
}
// Java 1.5 Does not allow @Override for interface methods
// @Override
public String getAddress() throws IOException {
return address.machine().toString();
}
// @Override
public String[] getServiceNames() throws IOException {
Connection connection = new Connection(address, TIMEOUT, true,
socketFactory);
try {
connection.out().writeByte(ServerConnectionProtocol.MAGIC_BYTE);
connection.out().writeByte(
ServerConnectionProtocol.OPCODE_GET_SERVICE_NAMES);
connection.getAndCheckReply();
int nrOfServices = connection.in().readInt();
if (nrOfServices < 0) {
throw new IOException("Negative number of services");
}
String[] result = new String[nrOfServices];
for (int i = 0; i < nrOfServices; i++) {
result[i] = connection.in().readUTF();
}
return result;
} finally {
connection.close();
}
}
// @Override
public String[] getHubs() throws IOException {
Connection connection = new Connection(address, TIMEOUT, true,
socketFactory);
try {
connection.out().writeByte(ServerConnectionProtocol.MAGIC_BYTE);
connection.out()
.writeByte(ServerConnectionProtocol.OPCODE_GET_HUBS);
connection.getAndCheckReply();
int nrOfHubs = connection.in().readInt();
if (nrOfHubs < 0) {
throw new IOException("Negative number of hubs");
}
String[] result = new String[nrOfHubs];
for (int i = 0; i < nrOfHubs; i++) {
result[i] = connection.in().readUTF();
}
return result;
} finally {
connection.close();
}
}
// @Override
public void addHubs(DirectSocketAddress... hubAddresses) throws IOException {
String[] strings = new String[hubAddresses.length];
for (int i = 0; i < strings.length; i++) {
strings[i] = hubAddresses[i].toString();
}
addHubs(strings);
}
// @Override
public void addHubs(String... hubAddresses) throws IOException {
Connection connection = new Connection(address, TIMEOUT, true,
socketFactory);
try {
connection.out().writeByte(ServerConnectionProtocol.MAGIC_BYTE);
connection.out()
.writeByte(ServerConnectionProtocol.OPCODE_ADD_HUBS);
connection.out().writeInt(hubAddresses.length);
for (String hub : hubAddresses) {
connection.out().writeUTF(hub);
}
connection.getAndCheckReply();
} finally {
connection.close();
}
}
// @Override
public void end(long timeout) throws IOException {
if (pipe != null) {
pipe.end();
} else {
Connection connection = new Connection(address, TIMEOUT, true,
socketFactory);
try {
connection.out().writeByte(ServerConnectionProtocol.MAGIC_BYTE);
connection.out().writeByte(ServerConnectionProtocol.OPCODE_END);
connection.out().writeLong(timeout);
connection.getAndCheckReply();
} finally {
connection.close();
}
}
}
// @Override
public RegistryServiceInterface getRegistryService() {
return registryConnection;
}
// @Override
public ManagementServiceInterface getManagementService() {
return managementConnection;
}
/**
* Closes connection to the server. Will also terminate server if
* stdin/stdout connection is used
*/
public void closeConnection() {
if (pipe != null) {
pipe.end();
}
}
}