View Javadoc

1   /*
2   Copyright (C) 2000 - 2007 Grid Systems, S.A.
3   
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License, version 2, as
6   published by the Free Software Foundation.
7   
8   This program is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12  
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  */
17  package com.gridsystems.innergrid.kernel.genericutils;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.concurrent.atomic.AtomicLong;
26  
27  import org.apache.commons.lang.NullArgumentException;
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  
31  import com.gridsystems.exceptions.PagedRequestException;
32  import com.gridsystems.exceptions.PagedRequestNotAllowedException;
33  import com.gridsystems.exceptions.PagedRequestNotFoundException;
34  import com.gridsystems.innergrid.kernel.server.KernelContext;
35  
36  /**
37   * Centralized management for asynchronous requests being performed.
38   *
39   * @author lgaspart
40   * @author Xmas (generics)
41   * @author Rodrigo Ruiz (bug fixes)
42   * @version 1.0
43   * @param <E> Specific PagedRequest to manage
44   */
45  public final class PagedRequestManager<E extends PagedRequest<?>> {
46    /**
47     * Default timeout value in milliseconds.
48     */
49    private static final long DEFAULT_TIMEOUT = 200000;
50  
51    /**
52     * Default clean sleep time in milliseconds.
53     */
54    private static final long DEFAULT_SLEEP = 60000;
55  
56    /**
57     * All logs are done through this instance.
58     */
59    private static final Log LOG = LogFactory.getLog(PagedRequestManager.class);
60  
61    /**
62     * Used to build thread names.
63     */
64    private static final AtomicLong CLEAN_THREAD_ID = new AtomicLong(0);
65  
66    /**
67     * Reference to the cleaning thread.
68     */
69    private CleaningThread cleanThread = null;
70  
71    /**
72     * Time interval in milliseconds for a not accessed request to be considered
73     * timed out.
74     */
75    private long timeoutThreshold = DEFAULT_TIMEOUT;
76  
77    /**
78     * Time to sleep (in milliseconds) between successive cleaning thread loops.
79     */
80    private long cleanSleep = DEFAULT_SLEEP;
81  
82    /**
83     * Collection where all the requests are stored.
84     */
85    private Map<Long, E> pagedRequests
86      = Collections.synchronizedMap(new HashMap<Long, E>());
87  
88    /**
89     * Private constructor.
90     */
91    public PagedRequestManager() {
92    }
93  
94    /**
95     * {@inheritDoc}
96     */
97    @Override protected void finalize() throws Throwable {
98      removeAllPagedRequest();
99      super.finalize();
100   }
101 
102   /**
103    * Makes sure the cleaner thread is up and running.
104    */
105   private void initCleaner() {
106     if (this.cleanThread == null) {
107       this.cleanThread = new CleaningThread(CLEAN_THREAD_ID.addAndGet(1L));
108       this.cleanThread.start();
109     }
110   }
111 
112   /**
113    * Shutdowns the cleaner thread.
114    */
115   private void disposeCleaner() {
116     if (this.cleanThread != null) {
117       this.cleanThread.interrupt();
118       this.cleanThread = null;
119     }
120   }
121 
122   /**
123    * Adds a PagedRequest to the collection of the manager.
124    *
125    * @param req PagedRequest to be added
126    * @throws NullArgumentException if req is null
127    */
128   public void addPagedRequest(E req) throws NullArgumentException {
129     if (req == null) {
130       throw new NullArgumentException("req");
131     }
132 
133     initCleaner();
134     pagedRequests.put(Long.valueOf(req.getId()), req);
135   }
136 
137   /**
138    * Obtains a PagedRequest from the managed pool.
139    *
140    * @param id Identifier of the asked for PagedRequest
141    * @return PagedRequest whose identifier matches 'id' parameter
142    * @throws PagedRequestNotFoundException if there is no PagedRequest with that id
143    * @throws PagedRequestNotAllowedException if the current user did not create the
144    *                                         request
145    */
146   public E getPagedRequest(long id)
147     throws PagedRequestNotFoundException, PagedRequestNotAllowedException {
148     return getPagedRequest(id, true);
149   }
150 
151   /**
152    * Obtains a PagedRequest from the managed pool.
153    *
154    * @param id identifier of the PagedRequest
155    * @param secure true if security checking must be done
156    * @return a PagedRequest object identified by the 'id' parameter
157    * @throws PagedRequestNotFoundException if there is no PagedRequest with that id
158    * @throws PagedRequestNotAllowedException if the current user did not create the
159    *                                         request
160    */
161   private E getPagedRequest(long id, boolean secure)
162     throws PagedRequestNotFoundException, PagedRequestNotAllowedException {
163     E req = pagedRequests.get(id);
164 
165     if (req == null) {
166       throw new PagedRequestNotFoundException();
167     }
168 
169     String currentUserName = KernelContext.getContext().getUserName();
170     if (secure && (currentUserName != null) && !currentUserName.equals(req.getUser())) {
171       throw new PagedRequestNotAllowedException();
172     }
173 
174     return req;
175   }
176   /**
177    * Gets all paged requests that are instances of the specified class.
178    *
179    * @return A collection containing all selected paged requests
180    */
181   public List<E> getAllPagedRequests() {
182     List<E> list = new ArrayList<E>(pagedRequests.values());
183     return list;
184   }
185 
186   /**
187    * Gets the size of the collection of managed paged requests.
188    *
189    * @return size of the collection of managed paged requests
190    */
191   public int getPagedRequestsSize() {
192     return pagedRequests.size();
193   }
194 
195   /**
196    * Cancels and Removes all paged request from the collection of the manager.
197    */
198   public void removeAllPagedRequest() {
199     final int count = this.pagedRequests.keySet().size();
200     Long[] ids = this.pagedRequests.keySet().toArray(new Long[count]);
201     for (Long id : ids) {
202       try {
203         removePagedRequest(id, false);
204       } catch (PagedRequestException e) {
205         LOG.error("Exception thrown when removing paged request " + id, e);
206       }
207     }
208   }
209 
210   /**
211    * Removes a paged request from the collection of the manager, given its 'id'.
212    *
213    * @param id identifier of the paged request to be removed
214    * @throws PagedRequestNotFoundException if there is no request matching the id
215    * @throws PagedRequestNotAllowedException if the current user did not create
216    *                                         the request
217    */
218   public void removePagedRequest(long id)
219     throws PagedRequestNotFoundException, PagedRequestNotAllowedException {
220     removePagedRequest(id, true);
221   }
222 
223   /**
224    * Removes a paged request from the collection of the manager, given its 'id'.
225    * Before removing, it calls to the clear method of the PagedRequest
226    * @param id identifier of the paged request to be removed
227    * @param secure true if security checking must be done
228    * @throws PagedRequestNotFoundException if there is no request matching the id
229    * @throws PagedRequestNotAllowedException if the current user did not create
230    *                                         the request
231    */
232   private void removePagedRequest(long id, boolean secure)
233     throws PagedRequestNotFoundException, PagedRequestNotAllowedException {
234 
235     E pr = getPagedRequest(id, secure);
236 
237     try {
238       pr.clear();
239     } catch (Exception e) {
240       LOG.error("Unable to clear Paged Request number " + id);
241     }
242 
243     //Removes request from list
244     pagedRequests.remove(id);
245 
246     if (pagedRequests.isEmpty()) {
247       disposeCleaner();
248     }
249   }
250 
251 
252   /**
253    * Gets a String representation of this object.
254    *
255    * @return a string representation of this object
256    */
257   @Override public String toString() {
258     return "PagedRequestManager { requests number = " + pagedRequests.size()
259       + ", timeout limit = " + timeoutThreshold + " }";
260   }
261 
262   /**
263    * Thread in charge to clean obsolete requests.
264    */
265   class CleaningThread extends Thread {
266 
267     /**
268      * Creates a new instance.
269      */
270     public CleaningThread(long id) {
271       setName("CleaningThread-" + id);
272       setDaemon(true);
273     }
274 
275     /**
276      * Removes from the collection of requests those not accessed during
277      * timeoutThreshold milliseconds.
278      */
279     @Override public void run() {
280       while (!isInterrupted()) {
281         // Builds a list with the requests to remove
282         long now = System.currentTimeMillis();
283         ArrayList<E> toRemove = new ArrayList<E>();
284         synchronized (pagedRequests) {
285           Iterator<E> it = pagedRequests.values().iterator();
286           while (it.hasNext()) {
287             E pr =  it.next();
288             if (now - pr.getLastAccessTime() > timeoutThreshold) {
289               toRemove.add(pr);
290             }
291           }
292         }
293 
294         // Removes all requests in the list
295         Iterator<E> it = toRemove.iterator();
296         while (it.hasNext()) {
297           E req = it.next();
298           long id = req.getId();
299           try {
300             removePagedRequest(id, false);
301           } catch (PagedRequestNotFoundException e) {
302             LOG.warn("Paged Request " + id + " could not be removed: not found");
303           } catch (PagedRequestNotAllowedException e) {
304             LOG.warn("Paged Request " + id + " could not be removed: not allowed");
305           }
306         }
307 
308         // Hibernates until the next loop
309         try {
310           Thread.sleep(cleanSleep);
311         } catch (InterruptedException e) { }
312       }
313     }
314   }
315 }