1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package twcsckernel.projectbase.rmi.socketfactory;
16
17 import java.io.IOException;
18 import java.net.ServerSocket;
19 import java.net.Socket;
20 import java.rmi.server.RMISocketFactory;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.Map;
24
25 /***
26 * Socket factory for clients who want servers to be able to call them
27 * back even if the client's address/port are not reachable from the
28 * server. This happens with firewalls and dial-up networking, for
29 * example. <p>
30 *
31 * The factory provides two main capabilities: <p>
32 * <ul>
33 * <li>A gateway registry for going through firewalls</li>
34 * <li>A signalling channel for establishing callback sockets</li>
35 * </ul>
36 * <p>
37 * The gateway registry allows a mapping to be made between two
38 * endpoints. For example, the endpoint cssassociates.com:3453 can
39 * be mapped to somewhereElse.com:2356. Client connections made
40 * to the first endpoint from RMI will actually be connected to the
41 * second endpoint. This situation occurs when a port on a firewall
42 * is "opened" and forwarded to a host within the firewall. <p>
43 *
44 * A signalling channel is a socket from the client to the server
45 * and carries the "two-way" protocol (for lack of a better name).
46 * The client establishes a signalling channel to the server,
47 * which must be running the ServerTwoWaySocketFactory. When the server
48 * needs to make a callback to the client, it requests a socket via
49 * the channel instead of trying to directly connect to the client.
50 * The client establishes a socket to the server for the server to
51 * use in the callback.
52 *
53 * @author Tim Taylor -- tttaylor@cssassociates.com
54 */
55 public class ClientTwoWaySocketFactory extends RMISocketFactory {
56 /*** Registered gateways */
57 private Map<String,EndpointInfo> gatewayMap = Collections.synchronizedMap(new HashMap<String,EndpointInfo>());
58
59 /***
60 * Signalling channel used to handshake with server. Currently
61 * supports one server and one channel. I.e., each client
62 * can have only one server (without modifying this code
63 * to support multiple channels). However, the server
64 * can have as many clients as it wants.
65 */
66 private SignallingChannel channel;
67
68 /***
69 * Register a gateway. Client connections requested via
70 * <code>createSocket</code> for <code>host:port</code> will
71 * instead be made to <code>gateHost:gatePort</code>
72 * @param host The destination host.
73 * @param port The destination port.
74 * @param gateHost The gateway host (proxy, firewall, etc.).
75 * @param gatePort The gateway port.
76 */
77 public void registerGateway(String host, int port,
78 String gateHost, int gatePort) {
79 gatewayMap.put(EndpointInfo.getEndpointString(host, port),
80 new EndpointInfo(gateHost, gatePort));
81 }
82
83 /***
84 * Establish a two-way protocol signalling channel to the
85 * server at <code>
86 */
87 public void establishSignallingChannel(String address, int port)
88 throws IOException {
89
90 channel = new SignallingChannel(address, port);
91
92
93
94
95
96 registerGateway(channel.getDestinationAddress(),
97 channel.getDestinationPort(),
98 channel.getDirectAddress(),
99 channel.getDirectPort());
100
101 channel.start();
102 }
103
104 /***
105 * Returns the endpoint associated with the <code>host</code>
106 * and <code>port</code>. The endpoint returned is the endpoint
107 * to be directly contacted. If the <code>host</code> and
108 * <code>port</code> have an associated gateway endpoint, that
109 * endpoint is returned. Otherwise an endpoint for the provided
110 * <code>host</code> and <code>port</code> is returned.
111 *
112 * @return The endpoint to be connected to.
113 */
114 public EndpointInfo getDirectEndpoint(String host, int port) {
115 EndpointInfo endpointInfo = (EndpointInfo)
116 gatewayMap.get(EndpointInfo.getEndpointString(host, port));
117
118 if (endpointInfo != null) {
119 return endpointInfo;
120 }
121 else {
122 return new EndpointInfo(host, port);
123 }
124 }
125
126 /***
127 * Creates a socket to the specified <code>host</code> and <code>
128 * port</code>. If a gateway has been registered using
129 * <code>registerGateway</code> then the socket will be connected
130 * to that gateway. Otherwise, the socket is connected directly
131 * to the specified <code>host</code> and <code>port</code>.
132 *
133 * @see #registerGateway()
134 */
135 public Socket createSocket(String host, int port) throws IOException {
136 EndpointInfo ep = getDirectEndpoint(host, port);
137
138 return new Socket(ep.getHost(), ep.getPort());
139 }
140
141 /***
142 * @return a server socket for the specified port
143 */
144 public ServerSocket createServerSocket(int port) throws IOException {
145 ServerSocket sock = new ServerSocket(port);
146 if (channel != null) {
147
148 channel.getOutputStream().writeInt(sock.getLocalPort());
149 }
150 return sock;
151 }
152 }
153
154
155
156