1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package net.sf.ehcache.distribution;
18
19 import net.sf.ehcache.CacheException;
20 import net.sf.ehcache.CacheManager;
21 import net.sf.ehcache.Ehcache;
22 import net.sf.ehcache.Status;
23 import net.sf.ehcache.event.CacheEventListener;
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26
27 import java.io.IOException;
28 import java.net.InetAddress;
29 import java.net.ServerSocket;
30 import java.net.UnknownHostException;
31 import java.rmi.Naming;
32 import java.rmi.NotBoundException;
33 import java.rmi.Remote;
34 import java.rmi.RemoteException;
35 import java.rmi.registry.LocateRegistry;
36 import java.rmi.registry.Registry;
37 import java.rmi.server.ExportException;
38 import java.rmi.server.UnicastRemoteObject;
39 import java.util.ArrayList;
40 import java.util.HashMap;
41 import java.util.Iterator;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Set;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 public class RMICacheManagerPeerListener implements CacheManagerPeerListener {
71
72 private static final Log LOG = LogFactory.getLog(RMICacheManagerPeerListener.class.getName());
73 private static final int MINIMUM_SENSIBLE_TIMEOUT = 200;
74 private static final int NAMING_UNBIND_RETRY_INTERVAL = 400;
75 private static final int NAMING_UNBIND_MAX_RETRIES = 10;
76
77
78
79
80 protected final Map cachePeers = new HashMap();
81
82
83
84
85 protected Status status;
86
87
88
89
90 protected Integer port;
91
92 private Registry registry;
93 private boolean registryCreated;
94 private final String hostName;
95
96 private CacheManager cacheManager;
97 private Integer socketTimeoutMillis;
98
99
100
101
102
103
104
105
106
107
108 public RMICacheManagerPeerListener(String hostName, Integer port, CacheManager cacheManager,
109 Integer socketTimeoutMillis) throws UnknownHostException {
110
111 status = Status.STATUS_UNINITIALISED;
112
113 if (hostName != null && hostName.length() != 0) {
114 this.hostName = hostName;
115 if (hostName.equals("localhost")) {
116 LOG.warn("Explicitly setting the listener hostname to 'localhost' is not recommended. "
117 + "It will only work if all CacheManager peers are on the same machine.");
118 }
119 } else {
120 this.hostName = calculateHostAddress();
121 }
122 if (port == null || port.intValue() == 0) {
123 assignFreePort(false);
124 } else {
125 this.port = port;
126 }
127 this.cacheManager = cacheManager;
128 if (socketTimeoutMillis == null || socketTimeoutMillis.intValue() < MINIMUM_SENSIBLE_TIMEOUT) {
129 throw new IllegalArgumentException("socketTimoutMillis must be a reasonable value greater than 200ms");
130 }
131 this.socketTimeoutMillis = socketTimeoutMillis;
132
133 }
134
135
136
137
138
139
140 protected void assignFreePort(boolean forced) throws IllegalStateException {
141 if (status != Status.STATUS_UNINITIALISED) {
142 throw new IllegalStateException("Cannot change the port of an already started listener.");
143 }
144 this.port = new Integer(this.getFreePort());
145 if (forced) {
146 LOG.warn("Resolving RMI port conflict by automatically using a free TCP/IP port to listen on: " + this.port);
147 } else {
148 LOG.debug("Automatically finding a free TCP/IP port to listen on: " + this.port);
149 }
150 }
151
152
153
154
155
156
157
158 protected String calculateHostAddress() throws UnknownHostException {
159 return InetAddress.getLocalHost().getHostAddress();
160 }
161
162
163
164
165
166
167
168
169 protected int getFreePort() throws IllegalArgumentException {
170 ServerSocket serverSocket = null;
171 try {
172 serverSocket = new ServerSocket(0);
173 return serverSocket.getLocalPort();
174 } catch (IOException e) {
175 throw new IllegalArgumentException("Could not acquire a free port number.");
176 } finally {
177 if (serverSocket != null && !serverSocket.isClosed()) {
178 try {
179 serverSocket.close();
180 } catch (Exception e) {
181 LOG.debug("Error closing ServerSocket: " + e.getMessage());
182 }
183 }
184 }
185 }
186
187
188
189
190
191 public void init() throws CacheException {
192 RMICachePeer rmiCachePeer = null;
193 try {
194 startRegistry();
195 int counter = 0;
196 populateListOfRemoteCachePeers();
197 synchronized (cachePeers) {
198 for (Iterator iterator = cachePeers.values().iterator(); iterator.hasNext();) {
199 rmiCachePeer = (RMICachePeer) iterator.next();
200 bind(rmiCachePeer.getUrl(), rmiCachePeer);
201 counter++;
202 }
203 }
204 LOG.debug(counter + " RMICachePeers bound in registry for RMI listener");
205 status = Status.STATUS_ALIVE;
206 } catch (Exception e) {
207 String url = null;
208 if (rmiCachePeer != null) {
209 url = rmiCachePeer.getUrl();
210 }
211
212 throw new CacheException("Problem starting listener for RMICachePeer "
213 + url + ". Initial cause was " + e.getMessage(), e);
214 }
215 }
216
217
218
219
220
221
222 protected void bind(String peerName, RMICachePeer rmiCachePeer) throws Exception {
223 Naming.rebind(peerName, rmiCachePeer);
224 }
225
226
227
228
229
230
231
232
233 protected String[] listBoundRMICachePeers() throws CacheException {
234 try {
235 return registry.list();
236 } catch (RemoteException e) {
237 throw new CacheException("Unable to list cache peers " + e.getMessage());
238 }
239 }
240
241
242
243
244
245
246 protected Remote lookupPeer(String name) throws CacheException {
247 try {
248 return registry.lookup(name);
249 } catch (Exception e) {
250 throw new CacheException("Unable to lookup peer for replicated cache " + name + " "
251 + e.getMessage());
252 }
253 }
254
255
256
257
258 protected void populateListOfRemoteCachePeers() throws RemoteException {
259 String[] names = cacheManager.getCacheNames();
260 for (int i = 0; i < names.length; i++) {
261 String name = names[i];
262 Ehcache cache = cacheManager.getEhcache(name);
263 synchronized (cachePeers) {
264 if (cachePeers.get(name) == null) {
265 if (isDistributed(cache)) {
266 RMICachePeer peer = new RMICachePeer(cache, hostName, port, socketTimeoutMillis);
267 cachePeers.put(name, peer);
268 }
269 }
270 }
271 }
272
273 }
274
275
276
277
278
279
280
281 protected boolean isDistributed(Ehcache cache) {
282 Set listeners = cache.getCacheEventNotificationService().getCacheEventListeners();
283 for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
284 CacheEventListener cacheEventListener = (CacheEventListener) iterator.next();
285 if (cacheEventListener instanceof CacheReplicator) {
286 return true;
287 }
288 }
289 return false;
290 }
291
292
293
294
295
296
297
298
299
300
301
302
303 protected void startRegistry() throws RemoteException {
304 try {
305 registry = LocateRegistry.getRegistry(port.intValue());
306 try {
307 registry.list();
308 } catch (RemoteException e) {
309
310 registry = LocateRegistry.createRegistry(port.intValue());
311 registryCreated = true;
312 }
313 } catch (ExportException exception) {
314 LOG.fatal("Exception starting RMI registry. Error was " + exception.getMessage(), exception);
315 }
316 }
317
318
319
320
321
322
323 protected void stopRegistry() throws RemoteException {
324 if (registryCreated) {
325
326
327
328 boolean success = UnicastRemoteObject.unexportObject(registry, true);
329 if (success) {
330 LOG.debug("rmiregistry unexported.");
331 } else {
332 LOG.warn("Could not unexport rmiregistry.");
333 }
334 }
335 }
336
337
338
339
340
341
342
343
344 public void dispose() throws CacheException {
345 try {
346 int counter = 0;
347 synchronized (cachePeers) {
348 for (Iterator iterator = cachePeers.values().iterator(); iterator.hasNext();) {
349 RMICachePeer rmiCachePeer = (RMICachePeer) iterator.next();
350 disposeRMICachePeer(rmiCachePeer);
351 counter++;
352 }
353 stopRegistry();
354 }
355 LOG.debug(counter + " RMICachePeers unbound from registry in RMI listener");
356 status = Status.STATUS_SHUTDOWN;
357 } catch (Exception e) {
358 throw new CacheException("Problem unbinding remote cache peers. Initial cause was " + e.getMessage(), e);
359 }
360 }
361
362
363
364
365
366
367
368
369
370
371
372
373 protected void disposeRMICachePeer(RMICachePeer rmiCachePeer) throws Exception {
374 unbind(rmiCachePeer);
375 }
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390 protected void unbind(RMICachePeer rmiCachePeer) throws Exception {
391 String url = rmiCachePeer.getUrl();
392 try {
393 Naming.unbind(url);
394 } catch (NotBoundException e) {
395 LOG.warn(url + " not bound therefore not unbinding.");
396 }
397
398 boolean unexported = UnicastRemoteObject.unexportObject(rmiCachePeer, false);
399 for (int count = 1; (count < NAMING_UNBIND_MAX_RETRIES) && !unexported; count++) {
400 try {
401 Thread.sleep(NAMING_UNBIND_RETRY_INTERVAL);
402 } catch (InterruptedException ie) {
403
404 break;
405 }
406 unexported = UnicastRemoteObject.unexportObject(rmiCachePeer, false);
407 }
408
409
410
411 if (!unexported) {
412 if (!UnicastRemoteObject.unexportObject(rmiCachePeer, true)) {
413 LOG.warn("Unable to unexport rmiCachePeer: " + rmiCachePeer.getUrl() + ". Skipping.");
414 }
415 }
416 }
417
418
419
420
421
422
423 public List getBoundCachePeers() {
424 List cachePeerList = new ArrayList();
425 synchronized (cachePeers) {
426 for (Iterator iterator = cachePeers.values().iterator(); iterator.hasNext();) {
427 RMICachePeer rmiCachePeer = (RMICachePeer) iterator.next();
428 cachePeerList.add(rmiCachePeer);
429 }
430 }
431 return cachePeerList;
432 }
433
434
435
436
437 public Status getStatus() {
438 return status;
439 }
440
441
442
443
444
445
446
447
448 public String getUniqueResourceIdentifier() {
449 return "RMI listener port: " + port;
450 }
451
452
453
454
455
456
457
458 public void attemptResolutionOfUniqueResourceConflict() throws IllegalStateException, CacheException {
459 assignFreePort(true);
460 }
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484 public void notifyCacheAdded(String cacheName) throws CacheException {
485
486 if (LOG.isDebugEnabled()) {
487 LOG.debug("Adding " + cacheName + " to RMI listener");
488 }
489
490
491 synchronized (cachePeers) {
492 if (cachePeers.get(cacheName) != null) {
493 return;
494 }
495 }
496
497 Ehcache cache = cacheManager.getEhcache(cacheName);
498 if (isDistributed(cache)) {
499 RMICachePeer rmiCachePeer = null;
500 String url = null;
501 try {
502 rmiCachePeer = new RMICachePeer(cache, hostName, port, socketTimeoutMillis);
503 url = rmiCachePeer.getUrl();
504 bind(url, rmiCachePeer);
505 } catch (Exception e) {
506 throw new CacheException("Problem starting listener for RMICachePeer "
507 + url + ". Initial cause was " + e.getMessage(), e);
508 }
509
510 synchronized (cachePeers) {
511 cachePeers.put(cacheName, rmiCachePeer);
512 }
513
514 }
515 if (LOG.isDebugEnabled()) {
516 LOG.debug(cachePeers.size() + " RMICachePeers bound in registry for RMI listener");
517 }
518 }
519
520
521
522
523
524
525
526
527
528
529
530
531
532 public void notifyCacheRemoved(String cacheName) {
533
534 if (LOG.isDebugEnabled()) {
535 LOG.debug("Removing " + cacheName + " from RMI listener");
536 }
537
538
539 synchronized (cachePeers) {
540 if (cachePeers.get(cacheName) == null) {
541 return;
542 }
543 }
544
545 RMICachePeer rmiCachePeer;
546 synchronized (cachePeers) {
547 rmiCachePeer = (RMICachePeer) cachePeers.remove(cacheName);
548 }
549 String url = null;
550 try {
551 unbind(rmiCachePeer);
552 } catch (Exception e) {
553 throw new CacheException("Error removing Cache Peer "
554 + url + " from listener. Message was: " + e.getMessage(), e);
555 }
556
557 if (LOG.isDebugEnabled()) {
558 LOG.debug(cachePeers.size() + " RMICachePeers bound in registry for RMI listener");
559 }
560 }
561
562
563
564
565
566 void addCachePeer(String name, RMICachePeer peer) {
567 synchronized (cachePeers) {
568 cachePeers.put(name, peer);
569
570 }
571 }
572 }