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    @SuppressFBWarnings("EI_EXPOSE_REP2")
89    public AFRMISocketFactory(final AFNaming naming,
90        final RMIClientSocketFactory defaultClientFactory,
91        final RMIServerSocketFactory defaultServerFactory) {
92      super();
93      this.externables = new Externables(naming, defaultClientFactory, defaultServerFactory);
94  
95      closeUponRuntimeShutdown();
96    }
97  
98    
99    private void closeUponRuntimeShutdown() {
100     ShutdownHookSupport.addWeakShutdownHook(new ShutdownHook() {
101 
102       @Override
103       public void onRuntimeShutdown(Thread thread) {
104         try {
105           close();
106         } catch (IOException e) {
107           
108         }
109       }
110     });
111   }
112 
113   
114 
115 
116 
117 
118 
119 
120   protected abstract AFSocketAddress newSocketAddress(int port) throws IOException;
121 
122   
123 
124 
125 
126 
127 
128 
129   protected abstract AFSocket<?> newConnectedSocket(AFSocketAddress addr) throws IOException;
130 
131   private synchronized Externables getExternables() {
132     return externables;
133   }
134 
135   private synchronized void setExternable(Externables externable) {
136     this.externables = externable;
137   }
138 
139   @Override
140   public Socket createSocket(String host, int port) throws IOException {
141     final RMIClientSocketFactory cf = getExternables().defaultClientFactory;
142     if (cf != null && port < RMIPorts.AF_PORT_BASE) {
143       return cf.createSocket(host, port);
144     }
145 
146     final AFSocketAddress addr = newSocketAddress(port);
147     AFSocket<?> socket = newConnectedSocket(addr);
148 
149     synchronized (openSockets) {
150       openSockets.add(socket);
151     }
152     socket.addCloseable(() -> {
153       synchronized (openSockets) {
154         openSockets.remove(socket);
155       }
156     });
157     return socket;
158   }
159 
160   @Override
161   public void close() throws IOException {
162     synchronized (getExternables().naming) {
163       rmiService = null;
164       closeServerSockets();
165       closeSockets();
166     }
167   }
168 
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   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
365     setExternable(new Externables(readNamingInstance(in), 
366         (RMIClientSocketFactory) in.readObject(), 
367         (RMIServerSocketFactory) in.readObject()));
368   }
369 
370   @Override
371   public void writeExternal(ObjectOutput out) throws IOException {
372     Externables ext = getExternables();
373 
374     writeNamingInstance(out, ext.naming);
375     out.writeObject(ext.defaultClientFactory);
376     out.writeObject(ext.defaultServerFactory);
377   }
378 
379   
380 
381 
382 
383 
384 
385 
386   protected abstract AFNaming readNamingInstance(ObjectInput in) throws IOException;
387 
388   
389 
390 
391 
392 
393 
394 
395   protected abstract void writeNamingInstance(ObjectOutput out, AFNaming namingInstance)
396       throws IOException;
397 
398   
399 
400 
401 
402 
403 
404   public boolean isLocalServer(int port) {
405     if (port < RMIPorts.AF_PORT_BASE) {
406       return false;
407     }
408     synchronized (openServerSockets) {
409       return openServerSockets.containsKey(port);
410     }
411   }
412 
413   
414 
415 
416 
417 
418   protected AFNaming getNaming() {
419     return getExternables().naming;
420   }
421 
422   
423 
424 
425 
426 
427 
428   abstract boolean hasRegisteredPort(int port);
429 }