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.IOException;
21 import java.rmi.AccessException;
22 import java.rmi.AlreadyBoundException;
23 import java.rmi.ConnectIOException;
24 import java.rmi.NoSuchObjectException;
25 import java.rmi.NotBoundException;
26 import java.rmi.Remote;
27 import java.rmi.RemoteException;
28 import java.rmi.registry.Registry;
29 import java.rmi.server.RemoteObject;
30 import java.rmi.server.RemoteServer;
31 import java.util.HashMap;
32 import java.util.Map;
33 import java.util.concurrent.TimeUnit;
34 import java.util.concurrent.atomic.AtomicBoolean;
35
36 import org.newsclub.net.unix.rmi.ShutdownHookSupport.ShutdownHook;
37
38 import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
39
40
41
42
43
44
45
46 public abstract class AFRegistry implements Registry {
47 final RemoteCloseable<?> boundCloser;
48
49 private final Registry impl;
50 private final Map<String, Remote> bound = new HashMap<>();
51 private final AFNaming naming;
52 private final AtomicBoolean boundCloserExported = new AtomicBoolean(false);
53
54 private AFRMIService rmiService = null;
55
56 AFRegistry(AFNaming naming, Registry impl) {
57 this.naming = naming;
58 this.impl = impl;
59 this.boundCloser = new RemoteCloseable<Void>() {
60 @Override
61 public Void get() throws IOException {
62 return null;
63 }
64
65 @Override
66 public void close() throws IOException {
67 AFRegistry.this.forceUnexportBound();
68 }
69 };
70
71 if (isLocal()) {
72 ShutdownHookSupport.addWeakShutdownHook(new ShutdownHook() {
73 @Override
74 public void onRuntimeShutdown(Thread thread) {
75 forceUnexportBound();
76 }
77 });
78 }
79 }
80
81
82
83
84
85
86
87
88 @Deprecated
89 public boolean isRemoteServer() {
90 return isLocal();
91 }
92
93
94
95
96
97
98 public final boolean isLocal() {
99 return (impl instanceof RemoteServer);
100 }
101
102
103
104
105
106
107 @SuppressFBWarnings("EI_EXPOSE_REP")
108 public AFNaming getNaming() {
109 return naming;
110 }
111
112 @Override
113 public Remote lookup(String name) throws RemoteException, NotBoundException, AccessException {
114 return impl.lookup(name);
115 }
116
117 @Override
118 public void bind(String name, Remote obj) throws RemoteException, AlreadyBoundException,
119 AccessException {
120 impl.bind(name, RemoteObject.toStub(obj));
121 synchronized (bound) {
122 bound.put(name, obj);
123 }
124 checkBound();
125 }
126
127 @Override
128 public void unbind(String name) throws RemoteException, NotBoundException, AccessException {
129 impl.unbind(name);
130 synchronized (bound) {
131 bound.remove(name);
132 }
133 checkBound();
134 }
135
136 @Override
137 public void rebind(String name, Remote obj) throws RemoteException, AccessException {
138 impl.rebind(name, RemoteObject.toStub(obj));
139 synchronized (bound) {
140 bound.put(name, obj);
141 }
142 checkBound();
143 }
144
145 @Override
146 public String[] list() throws RemoteException, AccessException {
147 return impl == null ? new String[0] : impl.list();
148 }
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173 public Remote lookup(String name, long timeout, TimeUnit unit) throws NotBoundException,
174 RemoteException {
175 long timeWait = unit.toMillis(timeout);
176
177 Exception exFirst = null;
178 do {
179 try {
180 return impl.lookup(name);
181 } catch (NotBoundException | ConnectIOException | NoSuchObjectException e) {
182 if (exFirst == null) {
183 exFirst = e;
184 }
185 }
186
187 try {
188 Thread.sleep(Math.min(timeWait, 50));
189 } catch (InterruptedException e1) {
190 exFirst.addSuppressed(e1);
191 break;
192 }
193 timeWait -= 50;
194 } while (timeWait > 0);
195
196 if (exFirst instanceof NotBoundException) {
197 throw (NotBoundException) exFirst;
198 } else if (exFirst instanceof RemoteException) {
199 throw (RemoteException) exFirst;
200 } else {
201 throw new RemoteException("Lookup timed out");
202 }
203 }
204
205 void forceUnexportBound() {
206 final Map<String, Remote> map;
207 synchronized (bound) {
208 map = new HashMap<>(bound);
209 bound.clear();
210 }
211 try {
212 checkBound();
213 } catch (RemoteException e1) {
214
215 }
216 for (Map.Entry<String, Remote> en : map.entrySet()) {
217 String name = en.getKey();
218 Remote obj = en.getValue();
219 if (obj == null) {
220 continue;
221 }
222 AFNaming.unexportObject(obj);
223 try {
224 unbind(name);
225 } catch (RemoteException | NotBoundException e) {
226
227 }
228
229 }
230 try {
231 for (String list : list()) {
232 try {
233 unbind(list);
234 } catch (RemoteException | NotBoundException e) {
235
236 }
237 }
238 } catch (RemoteException e) {
239
240 }
241
242 AFNaming.unexportObject(this.impl);
243 AFNaming.unexportObject(this);
244 }
245
246 private void checkBound() throws RemoteException {
247 boolean empty;
248 synchronized (bound) {
249 empty = bound.isEmpty();
250 }
251 if (empty) {
252 if (boundCloserExported.compareAndSet(true, false)) {
253 AFRMIService service;
254 try {
255 service = getRMIService();
256 service.unregisterForShutdown(boundCloser);
257 } catch (NoSuchObjectException | NotBoundException e) {
258 return;
259 } finally {
260 AFNaming.unexportObject(boundCloser);
261 }
262 }
263 } else if (boundCloserExported.compareAndSet(false, true)) {
264 AFNaming.exportObject(boundCloser, naming.getSocketFactory());
265
266 AFRMIService service;
267 try {
268 service = getRMIService();
269 } catch (NotBoundException e) {
270 return;
271 }
272 service.registerForShutdown(boundCloser);
273 }
274 }
275
276 private AFRMIService getRMIService() throws RemoteException, NotBoundException {
277 if (rmiService == null) {
278 rmiService = naming.getRMIService(this);
279 }
280 return rmiService;
281 }
282 }