View Javadoc

1   package org.apache.jcs.engine.control.event;
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  
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  
27  import org.apache.jcs.engine.control.event.behavior.IElementEventQueue;
28  import org.apache.jcs.engine.control.event.behavior.IElementEventHandler;
29  import org.apache.jcs.engine.control.event.behavior.IElementEvent;
30  
31  /***
32   * An event queue is used to propagate ordered cache events to one and only one
33   * target listener.
34   */
35  public class ElementEventQueue
36      implements IElementEventQueue
37  {
38      private final static Log log = LogFactory.getLog( ElementEventQueue.class );
39  
40      private static int processorInstanceCount = 0;
41  
42      private String cacheName;
43  
44      private boolean destroyed = false;
45  
46      private Thread t;
47  
48      // Internal queue implementation
49  
50      private Object queueLock = new Object();
51  
52      // Dummy node
53  
54      private Node head = new Node();
55  
56      private Node tail = head;
57  
58      /***
59       * Constructor for the ElementEventQueue object
60       *
61       * @param cacheName
62       */
63      public ElementEventQueue( String cacheName )
64      {
65  
66          this.cacheName = cacheName;
67  
68          t = new QProcessor();
69          t.start();
70  
71          if ( log.isDebugEnabled() )
72          {
73              log.debug( "Constructed: " + this );
74          }
75      }
76  
77      /***
78       * Event Q is emtpy.
79       */
80      public synchronized void destroy()
81      {
82          if ( !destroyed )
83          {
84              destroyed = true;
85  
86              // sychronize on queue so the thread will not wait forever,
87              // and then interrupt the QueueProcessor
88  
89              synchronized ( queueLock )
90              {
91                  t.interrupt();
92              }
93  
94              t = null;
95  
96              log.info( "Element event queue destroyed: " + this );
97          }
98      }
99  
100     /***
101      * @return the region name for the event queue
102      */
103     public String toString()
104     {
105         return "cacheName=" + cacheName;
106     }
107 
108     /***
109      * @return The destroyed value
110      */
111     public boolean isAlive()
112     {
113         return ( !destroyed );
114     }
115 
116     /***
117      * Adds an ElementEvent to be handled
118      *
119      * @param hand
120      *            The IElementEventHandler
121      * @param event
122      *            The IElementEventHandler IElementEvent event
123      * @throws IOException
124      */
125     public void addElementEvent( IElementEventHandler hand, IElementEvent event )
126         throws IOException
127     {
128 
129         if ( log.isDebugEnabled() )
130         {
131             log.debug( "Adding Event Handler to QUEUE, !destroyed = " + !destroyed );
132         }
133 
134         if ( !destroyed )
135         {
136             ElementEventRunner runner = new ElementEventRunner( hand, event );
137 
138             if ( log.isDebugEnabled() )
139             {
140                 log.debug( "runner = " + runner );
141             }
142 
143             put( runner );
144         }
145     }
146 
147     /***
148      * Adds an event to the queue.
149      *
150      * @param event
151      */
152     private void put( AbstractElementEventRunner event )
153     {
154         Node newNode = new Node();
155 
156         newNode.event = event;
157 
158         synchronized ( queueLock )
159         {
160             tail.next = newNode;
161             tail = newNode;
162 
163             queueLock.notify();
164         }
165     }
166 
167     private AbstractElementEventRunner take()
168         throws InterruptedException
169     {
170         synchronized ( queueLock )
171         {
172             // wait until there is something to read
173 
174             while ( head == tail )
175             {
176                 if ( log.isDebugEnabled() )
177                 {
178                     log.debug( "Waiting for something to come into the Q" );
179                 }
180 
181                 queueLock.wait();
182 
183                 if ( log.isDebugEnabled() )
184                 {
185                     log.debug( "Something came into the Q" );
186                 }
187             }
188 
189             // we have the lock, and the list is not empty
190 
191             Node node = head.next;
192 
193             AbstractElementEventRunner value = node.event;
194 
195             if ( log.isDebugEnabled() )
196             {
197                 log.debug( "head.event = " + head.event );
198                 log.debug( "node.event = " + node.event );
199             }
200 
201             // Node becomes the new head (head is always empty)
202 
203             node.event = null;
204             head = node;
205 
206             return value;
207         }
208     }
209 
210     ///////////////////////////// Inner classes /////////////////////////////
211 
212     private static class Node
213     {
214         Node next = null;
215 
216         ElementEventQueue.AbstractElementEventRunner event = null;
217     }
218 
219     /***
220      */
221     private class QProcessor
222         extends Thread
223     {
224         /***
225          * Constructor for the QProcessor object
226          */
227         QProcessor()
228         {
229             super( "ElementEventQueue.QProcessor-" + ( ++processorInstanceCount ) );
230 
231             setDaemon( true );
232         }
233 
234         /***
235          * Main processing method for the QProcessor object
236          */
237         public void run()
238         {
239             AbstractElementEventRunner r = null;
240 
241             while ( !destroyed )
242             {
243                 try
244                 {
245                     r = take();
246 
247                     if ( log.isDebugEnabled() )
248                     {
249                         log.debug( "r from take() = " + r );
250                     }
251 
252                 }
253                 catch ( InterruptedException e )
254                 {
255                     // We were interrupted, so terminate gracefully.
256 
257                     this.destroy();
258                 }
259 
260                 if ( !destroyed && r != null )
261                 {
262                     r.run();
263                 }
264             }
265 
266             log.info( "QProcessor exiting for " + ElementEventQueue.this );
267         }
268     }
269 
270     /***
271      * Retries before declaring failure.
272      *
273      */
274     private abstract class AbstractElementEventRunner
275         implements Runnable
276     {
277         /***
278          * Main processing method for the AbstractElementEvent object
279          */
280         public void run()
281         {
282             IOException ex = null;
283 
284             try
285             {
286                 ex = null;
287                 doRun();
288                 return;
289                 // happy and done.
290             }
291             catch ( IOException e )
292             {
293                 ex = e;
294             }
295 
296             // Too bad. The handler has problems.
297             if ( ex != null )
298             {
299                 log.warn( "Giving up element event handling " + ElementEventQueue.this, ex );
300 
301             }
302             return;
303         }
304 
305         /***
306          * Description of the Method
307          *
308          * @exception IOException
309          */
310         protected abstract void doRun()
311             throws IOException;
312     }
313 
314     /***
315      */
316     private class ElementEventRunner
317         extends AbstractElementEventRunner
318     {
319 
320         private IElementEventHandler hand;
321 
322         private IElementEvent event;
323 
324         /***
325          * Constructor for the PutEvent object
326          * @param hand
327          * @param event
328          * @exception IOException
329          */
330         ElementEventRunner( IElementEventHandler hand, IElementEvent event )
331             throws IOException
332         {
333             if ( log.isDebugEnabled() )
334             {
335                 log.debug( "Constructing " + this );
336             }
337             this.hand = hand;
338             this.event = event;
339         }
340 
341         /***
342          * Description of the Method
343          *
344          * @exception IOException
345          */
346         protected void doRun()
347             throws IOException
348         {
349             hand.handleElementEvent( event );
350         }
351     }
352 }