| /* | |
| Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath 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 | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | |
| 02111-1307 USA. | |
| As a special exception, if you link this library with other files to | |
| produce an executable, this library does not by itself cause the | |
| resulting executable to be covered by the GNU General Public License. | |
| This exception does not however invalidate any other reasons why the | |
| executable file might be covered by the GNU General Public License. | |
| */ | |
| package gnu.java.rmi.server; | |
| import java.rmi.server.RMISocketFactory; | |
| import java.rmi.server.RMIServerSocketFactory; | |
| import java.rmi.server.RMIClientSocketFactory; | |
| import java.rmi.RemoteException; | |
| import gnu.java.rmi.server.UnicastConnection; | |
| import java.util.Hashtable; | |
| import java.net.Socket; | |
| import java.net.ServerSocket; | |
| import java.io.IOException; | |
| import java.io.ObjectOutput; | |
| import java.io.ObjectInput; | |
| import java.lang.Thread; | |
| import java.lang.Runnable; | |
| import java.net.InetAddress; | |
| import java.net.UnknownHostException; | |
| public class UnicastConnectionManager | |
| implements Runnable, ProtocolConstants { | |
| private static String localhost; | |
| private static Hashtable servers = new Hashtable(); | |
| private Thread serverThread; | |
| private ServerSocket ssock; | |
| String serverName; | |
| int serverPort; | |
| private RMIServerSocketFactory serverFactory; | |
| private RMIClientSocketFactory clientFactory; | |
| static { | |
| try { | |
| localhost = InetAddress.getLocalHost().getHostName(); | |
| } | |
| catch (UnknownHostException _) { | |
| localhost = "localhost"; | |
| } | |
| } | |
| private UnicastConnectionManager(String host, int port, RMIClientSocketFactory csf) { | |
| ssock = null; | |
| serverName = host; | |
| serverPort = port; | |
| serverFactory = null; | |
| clientFactory = csf; | |
| } | |
| private UnicastConnectionManager(int port, RMIServerSocketFactory ssf) { | |
| try { | |
| ssock = ssf.createServerSocket(port); | |
| serverPort = ssock.getLocalPort(); | |
| } | |
| catch (IOException _) { | |
| try { | |
| ssock = ssf.createServerSocket(0); | |
| serverPort = ssock.getLocalPort(); | |
| } | |
| catch (IOException __) { | |
| ssock = null; | |
| serverPort = 0; | |
| } | |
| } | |
| serverName = localhost; | |
| serverFactory = ssf; | |
| clientFactory = null; | |
| } | |
| /** | |
| * Return a client connection manager which will connect to the given | |
| * host/port. | |
| */ | |
| public static synchronized UnicastConnectionManager getInstance(String host, int port, RMIClientSocketFactory csf) { | |
| //System.out.println("getInstance: " + host + "," + port + "," + csf); | |
| if (csf == null) { | |
| csf = RMISocketFactory.getSocketFactory(); | |
| } | |
| TripleKey key = new TripleKey(host, port, csf); | |
| UnicastConnectionManager man = (UnicastConnectionManager)servers.get(key); | |
| if (man == null) { | |
| man = new UnicastConnectionManager(host, port, csf); | |
| servers.put(key, man); | |
| } | |
| return (man); | |
| } | |
| /** | |
| * Return a server connection manager which will accept connection on the | |
| * given port. | |
| */ | |
| public static synchronized UnicastConnectionManager getInstance(int port, RMIServerSocketFactory ssf) { | |
| //System.out.println("getInstance: " + port + "," + ssf); | |
| if (ssf == null) { | |
| ssf = RMISocketFactory.getSocketFactory(); | |
| } | |
| TripleKey key = new TripleKey(localhost, port, ssf); | |
| UnicastConnectionManager man = (UnicastConnectionManager)servers.get(key); | |
| if (man == null) { | |
| man = new UnicastConnectionManager(port, ssf); | |
| // The provided port might not be the set port. | |
| key.port = man.serverPort; | |
| servers.put(key, man); | |
| } | |
| return (man); | |
| } | |
| /** | |
| * Get a connection from this manager. | |
| */ | |
| public UnicastConnection getConnection() throws IOException { | |
| if (ssock == null) { | |
| return (getClientConnection()); | |
| } | |
| else { | |
| return (getServerConnection()); | |
| } | |
| } | |
| /** | |
| * Accept a connection to this server. | |
| */ | |
| private UnicastConnection getServerConnection() throws IOException { | |
| Socket sock = ssock.accept(); | |
| UnicastConnection conn = new UnicastConnection(this, sock); | |
| conn.acceptConnection(); | |
| //System.out.println("Server connection " + conn); | |
| return (conn); | |
| } | |
| /** | |
| * Make a conection from this client to the server. | |
| */ | |
| private UnicastConnection getClientConnection() throws IOException { | |
| Socket sock = clientFactory.createSocket(serverName, serverPort); | |
| UnicastConnection conn = new UnicastConnection(this, sock); | |
| conn.makeConnection(DEFAULT_PROTOCOL); | |
| //System.out.println("Client connection " + conn); | |
| return (conn); | |
| } | |
| /** | |
| * Discard a connection when we're done with it - maybe it can be | |
| * recycled. | |
| */ | |
| public void discardConnection(UnicastConnection conn) { | |
| //System.out.println("Discarding connection " + conn); | |
| conn.disconnect(); | |
| } | |
| /** | |
| * Start a server on this manager if it's a server socket and we've not | |
| * already got one running. | |
| */ | |
| public void startServer() { | |
| synchronized(this) { | |
| if (ssock == null || serverThread != null) { | |
| return; | |
| } | |
| serverThread = new Thread(this); | |
| } | |
| serverThread.start(); | |
| } | |
| /** | |
| * Server thread for connection manager. | |
| */ | |
| public void run() { | |
| for (;;) { | |
| try { | |
| //System.out.println("Waiting for connection on " + serverPort); | |
| UnicastConnection conn = getServerConnection(); | |
| (new Thread(conn)).start(); | |
| } | |
| catch (Exception e) { | |
| e.printStackTrace(); | |
| } | |
| } | |
| } | |
| /** | |
| * Serialization routine. | |
| */ | |
| void write(ObjectOutput out) throws IOException { | |
| out.writeUTF(serverName); | |
| out.writeInt(serverPort); | |
| } | |
| /** | |
| * Serialization routine. | |
| */ | |
| static UnicastConnectionManager read(ObjectInput in) throws IOException { | |
| String host = in.readUTF(); | |
| int port = in.readInt(); | |
| RMIClientSocketFactory csf = ((RMIObjectInputStream)in).manager.clientFactory; | |
| return (getInstance(host, port, csf)); | |
| } | |
| } | |
| /** | |
| * This is use as the hashkey for the client/server connections. | |
| */ | |
| class TripleKey { | |
| String host; | |
| int port; | |
| Object other; | |
| TripleKey(String host, int port, Object other) { | |
| this.host = host; | |
| this.port = port; | |
| this.other = other; | |
| } | |
| /** | |
| * Hash code just include the host and other - we ignore the port since | |
| * this has unusual matching behaviour. | |
| */ | |
| public int hashCode() { | |
| return (host.hashCode() ^ other.hashCode()); | |
| } | |
| public boolean equals(Object obj) { | |
| if (obj instanceof TripleKey) { | |
| TripleKey other = (TripleKey)obj; | |
| if (this.host.equals(other.host) && | |
| this.other == other.other && | |
| (this.port == other.port || this.port == 0 || other.port == 0)) { | |
| return (true); | |
| } | |
| } | |
| return (false); | |
| } | |
| } |