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.IOException;
22 import java.lang.ref.WeakReference;
23 import java.rmi.NoSuchObjectException;
24 import java.rmi.RemoteException;
25 import java.rmi.ServerException;
26 import java.util.ArrayList;
27 import java.util.BitSet;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Objects;
31 import java.util.Random;
32 import java.util.concurrent.ExecutorService;
33 import java.util.concurrent.Executors;
34 import java.util.concurrent.ThreadLocalRandom;
35 import java.util.stream.IntStream;
36
37 import org.newsclub.net.unix.StackTraceUtil;
38
39 import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
40
41
42
43
44
45
46 final class AFRMIServiceImpl implements AFRMIService {
47 private final BitSet ports = new BitSet(1000);
48 private final WeakReference<AFNaming> naming;
49 private final List<WeakReference<Closeable>> closeAtShutdown = new ArrayList<>();
50
51 public AFRMIServiceImpl(AFNaming naming) {
52 this.naming = new WeakReference<>(naming);
53 }
54
55 @SuppressFBWarnings("DMI_RANDOM_USED_ONLY_ONCE")
56 privateint randomPort() {
57 int maxRandom = ports.size();
58
59 Random random = ThreadLocalRandom.current();
60
61 int port;
62 for (int i = 0; i < 3; i++) {
63 port = ports.nextClearBit(random.nextInt(maxRandom));
64 if (port < maxRandom) {
65 return port;
66 } else {
67 maxRandom = port;
68 if (maxRandom == 0) {
69 break;
70 }
71 }
72 }
73 return ports.nextClearBit(0);
74 }
75
76 @Override
77 public synchronized int newPort() throws IOException {
78 int port = randomPort();
79 ports.set(port);
80 port += RMIPorts.ANONYMOUS_PORT_BASE;
81 return port;
82 }
83
84 @Override
85 public synchronized void returnPort(int port) throws IOException {
86 ports.clear(port - RMIPorts.ANONYMOUS_PORT_BASE);
87 }
88
89 @Override
90 public IntStream openPorts() throws RemoteException {
91 return ports.stream().map((int v) -> {
92 return v + RMIPorts.ANONYMOUS_PORT_BASE;
93 });
94 }
95
96 @Override
97 public void shutdown() throws RemoteException {
98 AFNaming namingInstance = naming.get();
99 if (namingInstance != null) {
100 if (!namingInstance.isRemoteShutdownAllowed()) {
101 throw new ServerException("Remote shutdown is disabled");
102 }
103 try {
104 namingInstance.shutdownRegistry();
105 } catch (ShutdownException e) {
106
107 }
108 }
109 }
110
111 @Override
112 public boolean isShutdownAllowed() throws RemoteException {
113 AFNaming namingInstance = naming.get();
114 if (namingInstance != null) {
115 return namingInstance.isRemoteShutdownAllowed();
116 } else {
117 return true;
118 }
119 }
120
121 @Override
122 public void registerForShutdown(Closeable closeable) throws RemoteException {
123 synchronized (closeAtShutdown) {
124 unregisterForShutdown(closeable);
125 closeAtShutdown.add(new WeakReference<>(closeable));
126 }
127 }
128
129 @Override
130 public void unregisterForShutdown(Closeable closeable) throws RemoteException {
131 synchronized (closeAtShutdown) {
132 Objects.requireNonNull(closeable);
133 for (Iterator<WeakReference<Closeable>> it = closeAtShutdown.iterator(); it.hasNext();) {
134 if (closeable.equals(it.next().get())) {
135 it.remove();
136 return;
137 }
138 }
139 }
140 }
141
142 void shutdownRegisteredCloseables() {
143 List<WeakReference<Closeable>> list;
144 synchronized (closeAtShutdown) {
145 list = new ArrayList<>(closeAtShutdown);
146 closeAtShutdown.clear();
147 }
148
149 ExecutorService executor = Executors.newCachedThreadPool();
150 for (WeakReference<Closeable> ref : list) {
151 executor.execute(() -> {
152 @SuppressWarnings("resource")
153 Closeable cl = ref.get();
154 if (cl == null) {
155 return;
156 }
157 try {
158 cl.close();
159 } catch (NoSuchObjectException e) {
160
161 } catch (IOException e) {
162 StackTraceUtil.printStackTrace(e);
163 }
164 });
165 }
166 executor.shutdown();
167 }
168 }