View Javadoc

1   /***
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Exoffice Technologies.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Exoffice Technologies. Exolab is a registered
23   *    trademark of Exoffice Technologies.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 2000-2001,2003 (C) Exoffice Technologies Inc. All Rights Reserved.
42   *
43   * $Id: LeaseManager.java,v 1.22 2003/10/29 04:58:32 tanderson Exp $
44   *
45   * Date         Author  Changes
46   * 3/01/2000    jima    Created
47   */
48  package org.exolab.jms.lease;
49  
50  
51  import org.apache.commons.logging.Log;
52  import org.apache.commons.logging.LogFactory;
53  
54  import org.exolab.core.service.BasicService;
55  import org.exolab.core.service.ServiceException;
56  import org.exolab.core.service.ServiceState;
57  import org.exolab.core.util.OrderedQueue;
58  import org.exolab.jms.message.MessageHandle;
59  
60  
61  /***
62   * The LeaseManager is responsible for creating and managing the lease objects.
63   * The Leasemanager is a singleton. When a BaseLease object is created it is
64   * added to the queue according to the duration (i.e. leases with shorter
65   * durations are placed at the top of the queue.
66   * <p>
67   * When the lease expires the LeeaseManager calls the leasee's associated
68   * listener(s).
69   * <p>
70   * We need to cater for persistence and non-persistent leases
71   *
72   * @version     $Revision: 1.22 $ $Date: 2003/10/29 04:58:32 $
73   * @author      <a href="mailto:jima@intalio.com">Jim Alateras</a>
74   */
75  public class LeaseManager extends BasicService {
76  
77      /***
78       * The name of the LeaseManagerThread that scans and removes expired leases
79       */
80      private static final String LEASE_MANAGER_THREAD_NAME =
81          "LeaseManagerReaper";
82  
83      /***
84       * An ordered list of leases
85       */
86      private OrderedQueue _queue = null;
87  
88      /***
89       * The singleton instance of the LeaseManager
90       */
91      private static LeaseManager _instance = new LeaseManager();
92  
93      /***
94       * Helper for waiting for leases to expire
95       */
96      private final Object _waiter = new Object();
97  
98      /***
99       * The logger
100      */
101     private static final Log _log = LogFactory.getLog(LeaseManager.class);
102 
103 
104     /***
105      * Create a new sorted tree set using the lease comparator as the
106      * sorting functor.
107      */
108     protected LeaseManager() {
109         super(LEASE_MANAGER_THREAD_NAME);
110         _queue = new OrderedQueue(new LeaseComparator());
111     }
112 
113     /***
114      * Return the singleton instance of the LeaseManager
115      *
116      * @return LeaseManager
117      */
118     public static LeaseManager instance() {
119         return _instance;
120     }
121 
122     /***
123      * Create a message lease with the specified duration and the nominated
124      * listener. The listener does not to be specified but is merely a
125      * convenience argument.
126      * <p>
127      * The object must be non-null and the duration must be greater than zero.
128      *
129      * @param       handle          message handle
130      * @param       duration        lease duration
131      * @param       listener        object that implements the LeaseEvent-
132      *                              ListenerIfc
133      * @return      MessageLease    creared lease object or null if no lease
134      *                              is created
135      */
136     public MessageLease createMessageLease(MessageHandle handle,
137                                            long duration,
138                                            LeaseEventListenerIfc listener) {
139         MessageLease result = null;
140 
141         if ((handle != null) && (duration > 0)) {
142             result = new MessageLease(handle, duration, listener);
143             addLease(result);
144         }
145         
146         return result;
147     }
148 
149     /***
150      * Create a lease on any object for the specified duration. If a listener
151      * is also provided then add it to the lease. Insert the lease in the queue
152      * and return an instance of the created lease.
153      * <p>
154      * The object must be non-null and the duration must be greater than zero.
155      *
156      * @param       object          leased object
157      * @param       duration        lease duration
158      * @param       listener        object that implements the LeaseEvent-
159      *                              ListenerIfc
160      * @return      BaseLease       creared lease object or null if no lease
161      *                              is created
162      */
163     public BaseLease createLease(Object object, long duration,
164                                  LeaseEventListenerIfc listener) {
165         BaseLease result = null;
166 
167         if ((object != null) && (duration > 0)) {
168             result = new BaseLease(object, duration, listener);
169             addLease(result);
170         }
171 
172         return result;
173     }
174 
175     /***
176      * Remove the specified lease.
177      *
178      * @param       lease           lease to remove
179      * @return      boolean         true if successful; false otherwise
180      */
181     public boolean removeLease(BaseLease lease) {
182         boolean result = false;
183 
184         synchronized (_queue) {
185             result = _queue.remove(lease);
186         }
187         if (result) {
188             synchronized (_waiter) {
189                 _waiter.notify();
190             }
191         }
192 
193         return result;
194     }
195 
196     /***
197      * Renew the lease on the specified object
198      *
199      * @param object                object's whose lease we want to renew
200      * @param duration              the new duration of the lease in ms
201      */
202     public BaseLease renewLease(BaseLease lease, long duration) {
203         BaseLease newlease = null;
204 
205         if ((lease != null) && (duration > 0)) {
206             synchronized (_queue) {
207                 // check that the lease hasn't expired yet.
208                 if (_queue.remove(lease)) {
209                     lease.setDuration(duration);
210                     _queue.add(lease);
211                     newlease = lease;
212                     synchronized (_waiter) {
213                         _waiter.notify();
214                     }
215                 }
216             }
217         }
218 
219         return newlease;
220     }
221 
222     /***
223      * Remove all the leases from the queue. Do not expire any of them
224      */
225     public void removeAll() {
226         synchronized (_queue) {
227             _queue.clear();
228         }
229     }
230 
231     /***
232      * Returns the number of active leases
233      *
234      * @return the number of leases
235      */
236     public int getLeaseCount() {
237         return _queue.size();
238     }
239 
240     /***
241      * The run method will search for expired leases, remove them from the
242      * list and notify listeners
243      */
244     public void run() {
245         while (getState() != ServiceState.STOPPED) {
246             expire();
247 
248             // wait until a lease is available, or the service is terminated
249             synchronized (_waiter) {
250                 try {
251                     _waiter.wait();
252                 } catch (InterruptedException ignore) {
253                 }
254             }
255         }
256     }
257 
258     /***
259      * Stop the service.
260      *
261      * @throws ServiceException if the service fails to stop
262      */
263     public void stop() throws ServiceException {
264         super.stop();
265         synchronized (_waiter) {
266             _waiter.notifyAll();
267         }
268     }
269 
270     /***
271      * Expires active leases
272      */
273     protected void expire() {
274         while (_queue.size() > 0) {
275             BaseLease lease = null;
276             boolean expired = false;
277             synchronized (_queue) {
278                 lease = (BaseLease) _queue.firstElement();
279                 if (lease == null) {
280                     continue;
281                 }
282                 
283                 if (lease.getExpiryTime() <= System.currentTimeMillis()) {
284                     // remove from the list and notify listeners
285                     _queue.removeFirstElement();
286                     expired = true;
287                 }
288             }
289 
290             if (expired) {
291                 lease.notifyLeaseExpired();
292             } else {
293                 // wait until the first element in the list is
294                 // ready to expire
295                 long time = lease.getExpiryTime() - 
296                     System.currentTimeMillis();
297                 
298                 if (time > 0) {
299                     try {
300                         synchronized (_waiter) {
301                             _waiter.wait(time);
302                         }
303                     } catch (InterruptedException ignore) {
304                     }
305                 }
306             }
307         }
308     }
309 
310     /***
311      * Add a lease
312      *
313      * @param lease the lease to add
314      */
315     protected void addLease(BaseLease lease) {
316         synchronized (_queue) {
317             _queue.add(lease);
318             if (_queue.firstElement() == lease) {
319                 // inserted before the first element, so reset scan
320                 synchronized (_waiter) {
321                     _waiter.notify();
322                 }
323             }
324         }
325     }
326 
327 
328 } //-- LeaseManager