1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.newsclub.net.unix.rmi;
19
20 import java.io.Closeable;
21 import java.io.Externalizable;
22 import java.io.IOException;
23 import java.io.ObjectInput;
24 import java.io.ObjectOutput;
25 import java.net.ServerSocket;
26 import java.net.Socket;
27 import java.rmi.NotBoundException;
28 import java.rmi.server.RMIClientSocketFactory;
29 import java.rmi.server.RMIServerSocketFactory;
30 import java.rmi.server.RMISocketFactory;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.Map;
34 import java.util.Set;
35
36 import org.newsclub.net.unix.AFServerSocket;
37 import org.newsclub.net.unix.AFSocket;
38 import org.newsclub.net.unix.AFSocketAddress;
39 import org.newsclub.net.unix.StackTraceUtil;
40 import org.newsclub.net.unix.rmi.ShutdownHookSupport.ShutdownHook;
41
42 import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
43
44
45
46
47
48
49 public abstract class AFRMISocketFactory extends RMISocketFactory implements Externalizable,
50 Closeable {
51 private static final long serialVersionUID = 1L;
52
53 private transient AFRMIService rmiService = null;
54
55 private transient Externables externables;
56 private final transient Map<Integer, AFServerSocket<?>> openServerSockets = new HashMap<>();
57 private final transient Set<AFSocket<?>> openSockets = new HashSet<>();
58
59 private static final class Externables {
60 private final AFNaming naming;
61 private final RMIClientSocketFactory defaultClientFactory;
62 private final RMIServerSocketFactory defaultServerFactory;
63
64 private Externables(AFNaming naming, RMIClientSocketFactory defaultClientFactory,
65 RMIServerSocketFactory defaultServerFactory) {
66 this.naming = naming;
67 this.defaultClientFactory = defaultClientFactory;
68 this.defaultServerFactory = defaultServerFactory;
69 }
70 }
71
72
73
74
75
76
77 public AFRMISocketFactory() {
78 this(null, null, null);
79 }
80
81
82
83
84
85
86
87
88 public AFRMISocketFactory(final AFNaming naming,
89 final RMIClientSocketFactory defaultClientFactory,
90 final RMIServerSocketFactory defaultServerFactory) {
91 super();
92 this.externables = new Externables(naming, defaultClientFactory, defaultServerFactory);
93
94 closeUponRuntimeShutdown();
95 }
96
97
98 private void closeUponRuntimeShutdown() {
99 ShutdownHookSupport.addWeakShutdownHook(new ShutdownHook() {
100
101 @Override
102 public void onRuntimeShutdown(Thread thread) {
103 try {
104 close();
105 } catch (IOException e) {
106
107 }
108 }
109 });
110 }
111
112
113
114
115
116
117
118
119 protected abstract AFSocketAddress newSocketAddress(int port) throws IOException;
120
121
122
123
124
125
126
127
128 protected abstract AFSocket<?> newConnectedSocket(AFSocketAddress addr) throws IOException;
129
130 private synchronized Externables getExternables() {
131 return externables;
132 }
133
134 private synchronized void setExternable(Externables externable) {
135 this.externables = externable;
136 }
137
138 @Override
139 public Socket createSocket(String host, int port) throws IOException {
140 final RMIClientSocketFactory cf = getExternables().defaultClientFactory;
141 if (cf != null && port < RMIPorts.AF_PORT_BASE) {
142 return cf.createSocket(host, port);
143 }
144
145 final AFSocketAddress addr = newSocketAddress(port);
146 AFSocket<?> socket = newConnectedSocket(addr);
147
148 synchronized (openSockets) {
149 openSockets.add(socket);
150 }
151 socket.addCloseable(() -> {
152 synchronized (openSockets) {
153 openSockets.remove(socket);
154 }
155 });
156 return socket;
157 }
158
159 @Override
160 public void close() throws IOException {
161 synchronized (getExternables().naming) {
162 rmiService = null;
163 closeServerSockets();
164 closeSockets();
165 }
166 }
167
168 @SuppressFBWarnings("USO_UNSAFE_ACCESSIBLE_OBJECT_SYNCHRONIZATION")
169 private AFRMIService getRmiService() throws IOException {
170 AFNaming naming = getExternables().naming;
171 synchronized (naming) {
172 if (rmiService == null) {
173 try {
174 rmiService = naming.getRMIService();
175 } catch (NotBoundException e) {
176 throw (IOException) new IOException(e.getMessage()).initCause(e);
177 }
178 }
179 return rmiService;
180 }
181 }
182
183
184
185
186
187
188
189
190
191 @Deprecated
192 protected int newPort() throws IOException {
193 return getRmiService().newPort();
194 }
195
196
197
198
199
200
201
202 protected PortLease newPortLease() throws IOException {
203 AFRMIService service = getRmiService();
204 int port = service.newPort();
205 return new PortLease(port, service);
206 }
207
208
209
210
211
212
213
214
215
216
217 @Deprecated
218 protected void returnPort(int port) throws IOException {
219 try {
220 getRmiService().returnPort(port);
221 } catch (ShutdownException e) {
222
223 } catch (IOException e) {
224 StackTraceUtil.printStackTrace(e);
225 }
226 }
227
228
229
230
231
232
233 protected static final class PortLease implements Closeable {
234 private final int port;
235 private final AFRMIService rmiService;
236
237 private PortLease(int port, AFRMIService rmiService) {
238 this.port = port;
239 this.rmiService = rmiService;
240 }
241
242
243
244
245 @Override
246 public void close() throws IOException {
247 rmiService.returnPort(getPort());
248 }
249
250
251
252
253
254
255 public int getPort() {
256 return port;
257 }
258
259
260
261
262
263
264 public AFRMIService getRmiService() {
265 return rmiService;
266 }
267 }
268
269 @Override
270 public ServerSocket createServerSocket(int port) throws IOException {
271 if (port == 0) {
272 PortLease portLease = newPortLease();
273 port = portLease.getPort();
274 final AFSocketAddress addr = newSocketAddress(port);
275 AFServerSocket<?> ass = addr.getAddressFamily().newServerSocket();
276 ass.addCloseable(portLease);
277 ass.setReuseAddress(true);
278 ass.setDeleteOnClose(true);
279 ass.bind(addr);
280
281 if (port >= RMIPorts.AF_PORT_BASE) {
282 ass.addCloseable(new ServerSocketCloseable(ass, port));
283 }
284 return ass;
285 }
286
287 final RMIServerSocketFactory sf = getExternables().defaultServerFactory;
288 if (sf != null && port < RMIPorts.AF_PORT_BASE) {
289 return sf.createServerSocket(port);
290 }
291
292 final AFSocketAddress addr = newSocketAddress(port);
293 AFServerSocket<?> socket = addr.getAddressFamily().newServerSocket();
294 socket.setDeleteOnClose(true);
295 socket.setReuseAddress(true);
296 socket.bind(addr);
297 socket.addCloseable(new ServerSocketCloseable(socket, port));
298 return socket;
299 }
300
301 private void closeServerSockets() throws IOException {
302 Map<Integer, AFServerSocket<?>> map;
303 synchronized (openServerSockets) {
304 map = new HashMap<>(openServerSockets);
305 }
306 IOException ex = null;
307 for (Map.Entry<Integer, AFServerSocket<?>> en : map.entrySet()) {
308 try {
309 en.getValue().close();
310 } catch (ShutdownException e) {
311
312 } catch (IOException e) {
313 if (ex == null) {
314 ex = e;
315 } else {
316 ex.addSuppressed(e);
317 }
318 }
319 }
320 synchronized (openServerSockets) {
321 openServerSockets.clear();
322 }
323 if (ex != null) {
324 throw ex;
325 }
326 }
327
328 private void closeSockets() {
329 Set<AFSocket<?>> set;
330 synchronized (openSockets) {
331 set = new HashSet<>(openSockets);
332 }
333 for (AFSocket<?> socket : set) {
334 try {
335 socket.close();
336 } catch (IOException e) {
337
338 }
339 }
340 synchronized (openSockets) {
341 openSockets.clear();
342 }
343 }
344
345 private final class ServerSocketCloseable implements Closeable {
346 private final int port;
347
348 ServerSocketCloseable(AFServerSocket<?> socket, int port) {
349 this.port = port;
350 synchronized (openServerSockets) {
351 openServerSockets.put(port, socket);
352 }
353 }
354
355 @Override
356 public void close() throws IOException {
357 synchronized (openServerSockets) {
358 openServerSockets.remove(port);
359 }
360 }
361 }
362
363 @Override
364 @SuppressFBWarnings("OBJECT_DESERIALIZATION")
365 public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
366 setExternable(new Externables(readNamingInstance(in),
367 (RMIClientSocketFactory) in.readObject(),
368 (RMIServerSocketFactory) in.readObject()));
369 }
370
371 @Override
372 public void writeExternal(ObjectOutput out) throws IOException {
373 Externables ext = getExternables();
374
375 writeNamingInstance(out, ext.naming);
376 out.writeObject(ext.defaultClientFactory);
377 out.writeObject(ext.defaultServerFactory);
378 }
379
380
381
382
383
384
385
386
387 protected abstract AFNaming readNamingInstance(ObjectInput in) throws IOException;
388
389
390
391
392
393
394
395
396 protected abstract void writeNamingInstance(ObjectOutput out, AFNaming namingInstance)
397 throws IOException;
398
399
400
401
402
403
404
405 public boolean isLocalServer(int port) {
406 if (port < RMIPorts.AF_PORT_BASE) {
407 return false;
408 }
409 synchronized (openServerSockets) {
410 return openServerSockets.containsKey(port);
411 }
412 }
413
414
415
416
417
418
419 protected AFNaming getNaming() {
420 return getExternables().naming;
421 }
422
423
424
425
426
427
428
429 abstract boolean hasRegisteredPort(int port);
430 }