%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.jcs.auxiliary.lateral.socket.tcp.LateralTCPListener |
|
|
1 | package org.apache.jcs.auxiliary.lateral.socket.tcp; |
|
2 | ||
3 | /* |
|
4 | * Licensed to the Apache Software Foundation (ASF) under one |
|
5 | * or more contributor license agreements. See the NOTICE file |
|
6 | * distributed with this work for additional information |
|
7 | * regarding copyright ownership. The ASF licenses this file |
|
8 | * to you under the Apache License, Version 2.0 (the |
|
9 | * "License"); you may not use this file except in compliance |
|
10 | * with the License. You may obtain a copy of the License at |
|
11 | * |
|
12 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
13 | * |
|
14 | * Unless required by applicable law or agreed to in writing, |
|
15 | * software distributed under the License is distributed on an |
|
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
|
17 | * KIND, either express or implied. See the License for the |
|
18 | * specific language governing permissions and limitations |
|
19 | * under the License. |
|
20 | */ |
|
21 | ||
22 | import java.io.IOException; |
|
23 | import java.io.ObjectInputStream; |
|
24 | import java.io.ObjectOutputStream; |
|
25 | import java.io.Serializable; |
|
26 | import java.net.InetAddress; |
|
27 | import java.net.ServerSocket; |
|
28 | import java.net.Socket; |
|
29 | import java.util.HashMap; |
|
30 | ||
31 | import org.apache.commons.logging.Log; |
|
32 | import org.apache.commons.logging.LogFactory; |
|
33 | import org.apache.jcs.auxiliary.lateral.LateralCacheInfo; |
|
34 | import org.apache.jcs.auxiliary.lateral.LateralElementDescriptor; |
|
35 | import org.apache.jcs.auxiliary.lateral.behavior.ILateralCacheListener; |
|
36 | import org.apache.jcs.auxiliary.lateral.socket.tcp.behavior.ITCPLateralCacheAttributes; |
|
37 | import org.apache.jcs.engine.behavior.ICacheElement; |
|
38 | import org.apache.jcs.engine.behavior.ICompositeCacheManager; |
|
39 | import org.apache.jcs.engine.control.CompositeCache; |
|
40 | import org.apache.jcs.engine.control.CompositeCacheManager; |
|
41 | ||
42 | import EDU.oswego.cs.dl.util.concurrent.PooledExecutor; |
|
43 | import EDU.oswego.cs.dl.util.concurrent.ThreadFactory; |
|
44 | ||
45 | /** |
|
46 | * Listens for connections from other TCP lateral caches and handles them. The |
|
47 | * initialization method starts a listening thread, which creates a socket |
|
48 | * server. When messages are received they are passed to a pooled executor which |
|
49 | * then calls the appropriate handle method. |
|
50 | */ |
|
51 | 1570 | public class LateralTCPListener |
52 | implements ILateralCacheListener, Serializable |
|
53 | { |
|
54 | private static final long serialVersionUID = -9107062664967131738L; |
|
55 | ||
56 | 26 | private final static Log log = LogFactory.getLog( LateralTCPListener.class ); |
57 | ||
58 | /** How long the server will block on an accept(). 0 is infinte. */ |
|
59 | private final static int acceptTimeOut = 0; |
|
60 | ||
61 | /** The CacheHub this listener is associated with */ |
|
62 | private transient ICompositeCacheManager cacheManager; |
|
63 | ||
64 | /** Map of available instances, keyed by port */ |
|
65 | 13 | protected final static HashMap instances = new HashMap(); |
66 | ||
67 | /** The socket listener */ |
|
68 | private ListenerThread receiver; |
|
69 | ||
70 | private ITCPLateralCacheAttributes tcpLateralCacheAttributes; |
|
71 | ||
72 | private int port; |
|
73 | ||
74 | private PooledExecutor pooledExecutor; |
|
75 | ||
76 | 13 | private int putCnt = 0; |
77 | ||
78 | 13 | private int removeCnt = 0; |
79 | ||
80 | 13 | private int getCnt = 0; |
81 | ||
82 | /** |
|
83 | * Use the vmid by default. This can be set for testing. If we ever need to |
|
84 | * run more than one per vm, then we need a new technique. |
|
85 | */ |
|
86 | 13 | private long listenerId = LateralCacheInfo.listenerId; |
87 | ||
88 | /** |
|
89 | * Gets the instance attribute of the LateralCacheTCPListener class. |
|
90 | * <p> |
|
91 | * @param ilca |
|
92 | * ITCPLateralCacheAttributes |
|
93 | * @param cacheMgr |
|
94 | * @return The instance value |
|
95 | */ |
|
96 | public synchronized static ILateralCacheListener getInstance( ITCPLateralCacheAttributes ilca, |
|
97 | ICompositeCacheManager cacheMgr ) |
|
98 | { |
|
99 | 52 | ILateralCacheListener ins = (ILateralCacheListener) instances.get( String.valueOf( ilca.getTcpListenerPort() ) ); |
100 | ||
101 | 52 | if ( ins == null ) |
102 | { |
|
103 | 13 | ins = new LateralTCPListener( ilca ); |
104 | ||
105 | 13 | ins.init(); |
106 | ||
107 | 13 | ins.setCacheManager( cacheMgr ); |
108 | ||
109 | 13 | instances.put( String.valueOf( ilca.getTcpListenerPort() ), ins ); |
110 | ||
111 | 13 | if ( log.isDebugEnabled() ) |
112 | { |
|
113 | 0 | log.debug( "created new listener " + ilca.getTcpListenerPort() ); |
114 | } |
|
115 | } |
|
116 | ||
117 | 52 | return ins; |
118 | } |
|
119 | ||
120 | /** |
|
121 | * Only need one since it does work for all regions, just reference by |
|
122 | * multiple region names. |
|
123 | * <p> |
|
124 | * @param ilca |
|
125 | */ |
|
126 | protected LateralTCPListener( ITCPLateralCacheAttributes ilca ) |
|
127 | 13 | { |
128 | 13 | this.setTcpLateralCacheAttributes( ilca ); |
129 | 13 | } |
130 | ||
131 | /** |
|
132 | * This starts the ListenerThread on the specified port. |
|
133 | */ |
|
134 | public void init() |
|
135 | { |
|
136 | try |
|
137 | { |
|
138 | 13 | this.port = getTcpLateralCacheAttributes().getTcpListenerPort(); |
139 | ||
140 | 13 | receiver = new ListenerThread(); |
141 | 13 | receiver.setDaemon( true ); |
142 | 13 | receiver.start(); |
143 | ||
144 | 13 | pooledExecutor = new PooledExecutor(); |
145 | 13 | pooledExecutor.setThreadFactory( new MyThreadFactory() ); |
146 | } |
|
147 | 0 | catch ( Exception ex ) |
148 | { |
|
149 | 0 | log.error( ex ); |
150 | ||
151 | 0 | throw new IllegalStateException( ex.getMessage() ); |
152 | 13 | } |
153 | 13 | } |
154 | ||
155 | /** |
|
156 | * Let the lateral cache set a listener_id. Since there is only one |
|
157 | * listerenr for all the regions and every region gets registered? the id |
|
158 | * shouldn't be set if it isn't zero. If it is we assume that it is a |
|
159 | * reconnect. |
|
160 | * <p> |
|
161 | * By default, the listener id is the vmid. |
|
162 | * <p> |
|
163 | * The service should set this value. This value will never be changed by a |
|
164 | * server we connect to. It needs to be non static, for unit tests. |
|
165 | * <p> |
|
166 | * The service will use the value it sets in all send requests to the |
|
167 | * sender. |
|
168 | * <p> |
|
169 | * @param id |
|
170 | * The new listenerId value |
|
171 | * @throws IOException |
|
172 | */ |
|
173 | public void setListenerId( long id ) |
|
174 | throws IOException |
|
175 | { |
|
176 | 0 | this.listenerId = id; |
177 | 0 | if ( log.isDebugEnabled() ) |
178 | { |
|
179 | 0 | log.debug( "set listenerId = " + id ); |
180 | } |
|
181 | 0 | } |
182 | ||
183 | /** |
|
184 | * Gets the listenerId attribute of the LateralCacheTCPListener object |
|
185 | * <p> |
|
186 | * @return The listenerId value |
|
187 | * @throws IOException |
|
188 | */ |
|
189 | public long getListenerId() |
|
190 | throws IOException |
|
191 | { |
|
192 | 1419 | return this.listenerId; |
193 | } |
|
194 | ||
195 | /** |
|
196 | * Increments the put count. Gets the cache that was injected by the lateral |
|
197 | * factory. Calls put on the cache. |
|
198 | * <p> |
|
199 | * @see org.apache.jcs.engine.behavior.ICacheListener#handlePut(org.apache.jcs.engine.behavior.ICacheElement) |
|
200 | */ |
|
201 | public void handlePut( ICacheElement element ) |
|
202 | throws IOException |
|
203 | { |
|
204 | 7 | putCnt++; |
205 | 7 | if ( log.isInfoEnabled() ) |
206 | { |
|
207 | 7 | if ( getPutCnt() % 100 == 0 ) |
208 | { |
|
209 | 0 | log.info( "Put Count (port " + getTcpLateralCacheAttributes().getTcpListenerPort() + ") = " |
210 | + getPutCnt() ); |
|
211 | } |
|
212 | } |
|
213 | ||
214 | 7 | if ( log.isDebugEnabled() ) |
215 | { |
|
216 | 0 | log.debug( "handlePut> cacheName=" + element.getCacheName() + ", key=" + element.getKey() ); |
217 | } |
|
218 | ||
219 | 7 | getCache( element.getCacheName() ).localUpdate( element ); |
220 | 7 | } |
221 | ||
222 | /** |
|
223 | * Increments the remove count. Gets the cache that was injected by the |
|
224 | * lateral factory. Calls remove on the cache. |
|
225 | * <p> |
|
226 | * @see org.apache.jcs.engine.behavior.ICacheListener#handleRemove(java.lang.String, |
|
227 | * java.io.Serializable) |
|
228 | */ |
|
229 | public void handleRemove( String cacheName, Serializable key ) |
|
230 | throws IOException |
|
231 | { |
|
232 | 1406 | removeCnt++; |
233 | 1406 | if ( log.isInfoEnabled() ) |
234 | { |
|
235 | 1406 | if ( getRemoveCnt() % 100 == 0 ) |
236 | { |
|
237 | 14 | log.info( "Remove Count = " + getRemoveCnt() ); |
238 | } |
|
239 | } |
|
240 | ||
241 | 1406 | if ( log.isDebugEnabled() ) |
242 | { |
|
243 | 0 | log.debug( "handleRemove> cacheName=" + cacheName + ", key=" + key ); |
244 | } |
|
245 | ||
246 | 1406 | getCache( cacheName ).localRemove( key ); |
247 | 1406 | } |
248 | ||
249 | /** |
|
250 | * Gets the cache that was injected by the lateral factory. Calls removeAll |
|
251 | * on the cache. |
|
252 | * <p> |
|
253 | * @see org.apache.jcs.engine.behavior.ICacheListener#handleRemoveAll(java.lang.String) |
|
254 | */ |
|
255 | public void handleRemoveAll( String cacheName ) |
|
256 | throws IOException |
|
257 | { |
|
258 | 0 | if ( log.isDebugEnabled() ) |
259 | { |
|
260 | 0 | log.debug( "handleRemoveAll> cacheName=" + cacheName ); |
261 | } |
|
262 | ||
263 | 0 | getCache( cacheName ).localRemoveAll(); |
264 | 0 | } |
265 | ||
266 | /** |
|
267 | * Gets the cache that was injected by the lateral factory. Calls get on the |
|
268 | * cache. |
|
269 | * <p> |
|
270 | * @param cacheName |
|
271 | * @param key |
|
272 | * @return Serializable |
|
273 | * @throws IOException |
|
274 | */ |
|
275 | public Serializable handleGet( String cacheName, Serializable key ) |
|
276 | throws IOException |
|
277 | { |
|
278 | 0 | getCnt++; |
279 | 0 | if ( log.isInfoEnabled() ) |
280 | { |
|
281 | 0 | if ( getGetCnt() % 100 == 0 ) |
282 | { |
|
283 | 0 | log.info( "Get Count (port " + getTcpLateralCacheAttributes().getTcpListenerPort() + ") = " |
284 | + getGetCnt() ); |
|
285 | } |
|
286 | } |
|
287 | ||
288 | 0 | if ( log.isDebugEnabled() ) |
289 | { |
|
290 | 0 | log.debug( "handleGet> cacheName=" + cacheName + ", key = " + key ); |
291 | } |
|
292 | ||
293 | 0 | return getCache( cacheName ).localGet( key ); |
294 | } |
|
295 | ||
296 | /** |
|
297 | * Right now this does nothing. |
|
298 | * <p> |
|
299 | * @see org.apache.jcs.engine.behavior.ICacheListener#handleDispose(java.lang.String) |
|
300 | */ |
|
301 | public void handleDispose( String cacheName ) |
|
302 | throws IOException |
|
303 | { |
|
304 | 0 | if ( log.isInfoEnabled() ) |
305 | { |
|
306 | 0 | log.info( "handleDispose > cacheName=" + cacheName ); |
307 | } |
|
308 | ||
309 | // TODO handle active deregistration, rather than passive detection |
|
310 | // through error |
|
311 | // getCacheManager().freeCache( cacheName, true ); |
|
312 | 0 | } |
313 | ||
314 | /** |
|
315 | * Gets the cacheManager attribute of the LateralCacheTCPListener object. |
|
316 | * <p> |
|
317 | * Normally this is set by the factory. If it wasn't set the listener |
|
318 | * defaults to the expected singleton behavior of the cache amanger. |
|
319 | * <p> |
|
320 | * @param name |
|
321 | * @return CompositeCache |
|
322 | */ |
|
323 | protected CompositeCache getCache( String name ) |
|
324 | { |
|
325 | 2825 | if ( getCacheManager() == null ) |
326 | { |
|
327 | // revert to singleton on failure |
|
328 | 0 | setCacheManager( CompositeCacheManager.getInstance() ); |
329 | ||
330 | 0 | if ( log.isDebugEnabled() ) |
331 | { |
|
332 | 0 | log.debug( "cacheMgr = " + getCacheManager() ); |
333 | } |
|
334 | } |
|
335 | ||
336 | 2825 | return getCacheManager().getCache( name ); |
337 | } |
|
338 | ||
339 | /** |
|
340 | * This is roughly the number of updates the lateral has received. |
|
341 | * <p> |
|
342 | * @return Returns the putCnt. |
|
343 | */ |
|
344 | public int getPutCnt() |
|
345 | { |
|
346 | 7 | return putCnt; |
347 | } |
|
348 | ||
349 | /** |
|
350 | * @return Returns the getCnt. |
|
351 | */ |
|
352 | public int getGetCnt() |
|
353 | { |
|
354 | 0 | return getCnt; |
355 | } |
|
356 | ||
357 | /** |
|
358 | * @return Returns the removeCnt. |
|
359 | */ |
|
360 | public int getRemoveCnt() |
|
361 | { |
|
362 | 1420 | return removeCnt; |
363 | } |
|
364 | ||
365 | /** |
|
366 | * @param cacheMgr |
|
367 | * The cacheMgr to set. |
|
368 | */ |
|
369 | public void setCacheManager( ICompositeCacheManager cacheMgr ) |
|
370 | { |
|
371 | 13 | this.cacheManager = cacheMgr; |
372 | 13 | } |
373 | ||
374 | /** |
|
375 | * @return Returns the cacheMgr. |
|
376 | */ |
|
377 | public ICompositeCacheManager getCacheManager() |
|
378 | { |
|
379 | 5650 | return cacheManager; |
380 | } |
|
381 | ||
382 | /** |
|
383 | * @param tcpLateralCacheAttributes |
|
384 | * The tcpLateralCacheAttributes to set. |
|
385 | */ |
|
386 | public void setTcpLateralCacheAttributes( ITCPLateralCacheAttributes tcpLateralCacheAttributes ) |
|
387 | { |
|
388 | 13 | this.tcpLateralCacheAttributes = tcpLateralCacheAttributes; |
389 | 13 | } |
390 | ||
391 | /** |
|
392 | * @return Returns the tcpLateralCacheAttributes. |
|
393 | */ |
|
394 | public ITCPLateralCacheAttributes getTcpLateralCacheAttributes() |
|
395 | { |
|
396 | 1425 | return tcpLateralCacheAttributes; |
397 | } |
|
398 | ||
399 | /** |
|
400 | * Processes commands from the server socket. There should be one listener |
|
401 | * for each configured TCP lateral. |
|
402 | */ |
|
403 | public class ListenerThread |
|
404 | extends Thread |
|
405 | { |
|
406 | /** Main processing method for the ListenerThread object */ |
|
407 | public void run() |
|
408 | { |
|
409 | try |
|
410 | { |
|
411 | log.info( "Listening on port " + port ); |
|
412 | ||
413 | ServerSocket serverSocket = new ServerSocket( port ); |
|
414 | serverSocket.setSoTimeout( acceptTimeOut ); |
|
415 | ||
416 | ConnectionHandler handler; |
|
417 | ||
418 | while ( true ) |
|
419 | { |
|
420 | if ( log.isDebugEnabled() ) |
|
421 | { |
|
422 | log.debug( "Waiting for clients to connect " ); |
|
423 | } |
|
424 | ||
425 | Socket socket = serverSocket.accept(); |
|
426 | ||
427 | if ( log.isDebugEnabled() ) |
|
428 | { |
|
429 | InetAddress inetAddress = socket.getInetAddress(); |
|
430 | ||
431 | log.debug( "Connected to client at " + inetAddress ); |
|
432 | } |
|
433 | ||
434 | handler = new ConnectionHandler( socket ); |
|
435 | ||
436 | pooledExecutor.execute( handler ); |
|
437 | } |
|
438 | } |
|
439 | catch ( Exception e ) |
|
440 | { |
|
441 | log.error( "Exception caught in TCP listener", e ); |
|
442 | } |
|
443 | } |
|
444 | } |
|
445 | ||
446 | /** |
|
447 | * A Separate thread taht runs when a command comes into the |
|
448 | * LateralTCPReceiver. |
|
449 | */ |
|
450 | public class ConnectionHandler |
|
451 | implements Runnable |
|
452 | { |
|
453 | private Socket socket; |
|
454 | ||
455 | /** |
|
456 | * Construct for a given socket |
|
457 | * @param socket |
|
458 | */ |
|
459 | public ConnectionHandler( Socket socket ) |
|
460 | { |
|
461 | this.socket = socket; |
|
462 | } |
|
463 | ||
464 | /** |
|
465 | * Main processing method for the LateralTCPReceiverConnection object |
|
466 | */ |
|
467 | public void run() |
|
468 | { |
|
469 | ObjectInputStream ois; |
|
470 | ||
471 | try |
|
472 | { |
|
473 | ois = new ObjectInputStream( socket.getInputStream() ); |
|
474 | } |
|
475 | catch ( Exception e ) |
|
476 | { |
|
477 | log.error( "Could not open ObjectInputStream on " + socket, e ); |
|
478 | ||
479 | return; |
|
480 | } |
|
481 | ||
482 | LateralElementDescriptor led; |
|
483 | ||
484 | try |
|
485 | { |
|
486 | while ( true ) |
|
487 | { |
|
488 | led = (LateralElementDescriptor) ois.readObject(); |
|
489 | ||
490 | if ( led == null ) |
|
491 | { |
|
492 | log.debug( "LateralElementDescriptor is null" ); |
|
493 | continue; |
|
494 | } |
|
495 | if ( led.requesterId == getListenerId() ) |
|
496 | { |
|
497 | log.debug( "from self" ); |
|
498 | } |
|
499 | else |
|
500 | { |
|
501 | if ( log.isDebugEnabled() ) |
|
502 | { |
|
503 | log.debug( "receiving LateralElementDescriptor from another" + "led = " + led |
|
504 | + ", led.command = " + led.command + ", led.ce = " + led.ce ); |
|
505 | } |
|
506 | ||
507 | handle( led ); |
|
508 | } |
|
509 | } |
|
510 | } |
|
511 | catch ( java.io.EOFException e ) |
|
512 | { |
|
513 | log.info( "Caught java.io.EOFException closing connection." ); |
|
514 | } |
|
515 | catch ( java.net.SocketException e ) |
|
516 | { |
|
517 | log.info( "Caught java.net.SocketException closing connection." ); |
|
518 | } |
|
519 | catch ( Exception e ) |
|
520 | { |
|
521 | log.error( "Unexpected exception.", e ); |
|
522 | } |
|
523 | ||
524 | try |
|
525 | { |
|
526 | ois.close(); |
|
527 | } |
|
528 | catch ( Exception e ) |
|
529 | { |
|
530 | log.error( "Could not close object input stream.", e ); |
|
531 | } |
|
532 | } |
|
533 | ||
534 | /** |
|
535 | * This calls the appropriate method, based on the command sent in the |
|
536 | * Lateral element descriptor. |
|
537 | * <p> |
|
538 | * @param led |
|
539 | * @throws IOException |
|
540 | */ |
|
541 | private void handle( LateralElementDescriptor led ) |
|
542 | throws IOException |
|
543 | { |
|
544 | String cacheName = led.ce.getCacheName(); |
|
545 | Serializable key = led.ce.getKey(); |
|
546 | ||
547 | if ( led.command == LateralElementDescriptor.UPDATE ) |
|
548 | { |
|
549 | handlePut( led.ce ); |
|
550 | } |
|
551 | else if ( led.command == LateralElementDescriptor.REMOVE ) |
|
552 | { |
|
553 | // if a hashcode was given and filtering is on |
|
554 | // check to see if they are the same |
|
555 | // if so, then don't remvoe, otherwise issue a remove |
|
556 | if ( led.valHashCode != -1 ) |
|
557 | { |
|
558 | if ( getTcpLateralCacheAttributes().isFilterRemoveByHashCode() ) |
|
559 | { |
|
560 | ICacheElement test = getCache( cacheName ).localGet( key ); |
|
561 | if ( test != null ) |
|
562 | { |
|
563 | if ( test.getVal().hashCode() == led.valHashCode ) |
|
564 | { |
|
565 | if ( log.isDebugEnabled() ) |
|
566 | { |
|
567 | log.debug( "Filtering detected identical hashCode [" + led.valHashCode |
|
568 | + "], not issuing a remove for led " + led ); |
|
569 | } |
|
570 | return; |
|
571 | } |
|
572 | else |
|
573 | { |
|
574 | if ( log.isDebugEnabled() ) |
|
575 | { |
|
576 | log.debug( "Different hashcodes, in cache [" + test.getVal().hashCode() |
|
577 | + "] sent [" + led.valHashCode + "]" ); |
|
578 | } |
|
579 | } |
|
580 | } |
|
581 | } |
|
582 | } |
|
583 | handleRemove( cacheName, key ); |
|
584 | } |
|
585 | else if ( led.command == LateralElementDescriptor.REMOVEALL ) |
|
586 | { |
|
587 | handleRemoveAll( cacheName ); |
|
588 | } |
|
589 | else if ( led.command == LateralElementDescriptor.GET ) |
|
590 | { |
|
591 | Serializable obj = handleGet( cacheName, key ); |
|
592 | ||
593 | ObjectOutputStream oos = new ObjectOutputStream( socket.getOutputStream() ); |
|
594 | ||
595 | if ( oos != null ) |
|
596 | { |
|
597 | oos.writeObject( obj ); |
|
598 | oos.flush(); |
|
599 | } |
|
600 | } |
|
601 | } |
|
602 | } |
|
603 | ||
604 | /** |
|
605 | * Allows us to set the daemon status on the executor threads |
|
606 | * <p> |
|
607 | * @author aaronsm |
|
608 | */ |
|
609 | class MyThreadFactory |
|
610 | implements ThreadFactory |
|
611 | { |
|
612 | /* |
|
613 | * (non-Javadoc) |
|
614 | * @see EDU.oswego.cs.dl.util.concurrent.ThreadFactory#newThread(java.lang.Runnable) |
|
615 | */ |
|
616 | public Thread newThread( Runnable runner ) |
|
617 | { |
|
618 | Thread t = new Thread( runner ); |
|
619 | t.setDaemon( true ); |
|
620 | t.setPriority( Thread.MIN_PRIORITY ); |
|
621 | return t; |
|
622 | } |
|
623 | } |
|
624 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |