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-2003 (C) Exoffice Technologies Inc. All Rights Reserved.
42 */
43
44 package org.exolab.jms.scheduler;
45
46 import java.util.HashMap;
47 import java.util.LinkedList;
48
49 import org.apache.commons.logging.Log;
50 import org.apache.commons.logging.LogFactory;
51
52 import org.exolab.core.service.BasicService;
53 import org.exolab.core.service.ServiceException;
54 import org.exolab.core.threadPool.ThreadPool;
55 import org.exolab.jms.config.Configuration;
56 import org.exolab.jms.config.ConfigurationManager;
57 import org.exolab.jms.config.SchedulerConfiguration;
58 import org.exolab.jms.threads.ThreadPoolManager;
59
60
61 /***
62 * The scheduler is responsible for executing {@link Runnable} objects
63 * using a thread pool. Clients can add these objects to the scheduler
64 * and the scheduler will, in fifo order, execute them. If there are no
65 * threads currently available, the runnable will wait for one to become
66 * available.
67 * <p>
68 * A client can add or remove {@link Runnable} objects.
69 *
70 * @version $Revision: 1.9 $ $Date: 2003/08/17 01:32:25 $
71 * @author <a href="mailto:mourikis@intalio.com">Jim Mourikis</a>
72 * @author <a href="mailto:tma@netspace.net.au">Tim Anderson</a>
73 * @author <a href="mailto:jima@intalio.com">Jim Alateras</a>
74 * @see java.lang.Runnable
75 * @see org.exolab.core.service.BasicService
76 * @see org.exolab.core.threadPool.ThreadPool
77 */
78 public class Scheduler extends BasicService {
79
80 /***
81 * This is the thread pool used by the scheduler
82 */
83 private ThreadPool _threads = null;
84
85 /***
86 * The queue of Runnable instances
87 */
88 private LinkedList _queue = new LinkedList();;
89
90 /***
91 * HashMap of Runnable->Integer, representing the number of times a
92 * Runnable object exists in the queue
93 */
94 private HashMap _references = new HashMap();
95
96 /***
97 * If true, shuts down the scheduler
98 */
99 private volatile boolean _stop = false;
100
101 /***
102 * This attribute holds the number of threads that the scheduler
103 * will use in its pool. It defaults to 6 and has a minimum value
104 * of 2
105 */
106 private int _threadCount = 6;
107
108 /***
109 * This is the minimum number of threads that can be used to
110 * configure the scheduler. If a lower nmber is specified then
111 * it defaults to this value
112 */
113 private final static int MIN_THREAD_COUNT = 2;
114
115 /***
116 * Unique name identifyting this sevice
117 */
118 private static final String SCHEDULER_NAME = "Scheduler";
119
120 /***
121 * This is the singleton instance of this class
122 */
123 private static Scheduler _instance = null;
124
125 /***
126 * The logger
127 */
128 private static final Log _log = LogFactory.getLog(Scheduler.class);
129
130 /***
131 * Creates the singleton instance
132 *
133 * @throws ServiceException if the scheduler can't be created
134 */
135 public static Scheduler createInstance() throws ServiceException {
136 _instance = new Scheduler();
137 return _instance;
138 }
139
140 /***
141 * Returns the singleton instance
142 *
143 * @return the singleton instance, or <code>null</code> if it hasn't
144 * been initialised
145 */
146 public static Scheduler instance() {
147 return _instance;
148 }
149
150 /***
151 * Construct an instance of the scheduler. This will read the information
152 * from the configuration file and then set up a thread pool.
153 *
154 * @throws ServiceException if the thread pool can't be initialised
155 */
156 private Scheduler() throws ServiceException {
157 super(SCHEDULER_NAME);
158
159 // access the configuration file.
160 Configuration config = ConfigurationManager.getConfig();
161 SchedulerConfiguration sched_config =
162 config.getSchedulerConfiguration();
163
164 int count = sched_config.getMaxThreads();
165 if (count < MIN_THREAD_COUNT) {
166 count = MIN_THREAD_COUNT;
167 }
168 _threadCount = count;
169
170 // create the thread pool
171 _threads = ThreadPoolManager.instance().createThreadPool(
172 SCHEDULER_NAME, _threadCount);
173 }
174
175 /***
176 * Add a Runnable object to the scheduler queue.
177 * When a thread becomes available, it will be executed.
178 *
179 * @param runner the object to execute
180 */
181 public void add(Runnable runner) {
182 synchronized (_queue) {
183 _queue.addLast(runner);
184 addReference(runner);
185 _queue.notify();
186 }
187 }
188
189 /***
190 * Remove a Runnable object from the scheduler queue.
191 *
192 * @param runner the object to remove
193 * @return boolean <tt>true</tt> if the object was
194 * removed, <tt>false</tt> if it is
195 * already running or doesn't exist
196 */
197 public boolean remove(Runnable runner) {
198 boolean result = false;
199 synchronized (_queue) {
200 result = _queue.remove(runner);
201 }
202 return result;
203 }
204
205 /***
206 * Returns if a Runnable object exists in the scheduler queue.
207 *
208 * @param runner the object to remove
209 * @return boolean <tt>true</tt> if the object exists,
210 * <tt>false</tt> if it is already
211 * running or doesn't exist
212 */
213 public boolean contains(Runnable runner) {
214 boolean result = false;
215 synchronized (_queue) {
216 result = (_references.get(runner) != null);
217 }
218 return result;
219 }
220
221 /***
222 * Returns true if the scheduler queue is empty
223 *
224 * @return <tt>true</tt> if the scheduler queue is empty
225 */
226 public boolean isEmpty() {
227 boolean result = false;
228 synchronized (_queue) {
229 result = _queue.isEmpty();
230 }
231 return result;
232 }
233
234 /***
235 * Start the scheduler
236 * This can only be terminated by invoking {@link #stop}
237 */
238 public void run() {
239 while (!_stop) {
240 Runnable runner = next();
241 if (!_stop && runner != null) {
242 try {
243 _threads.execute(runner);
244 } catch (Exception exception) {
245 _log.error(exception);
246 }
247 }
248 }
249 }
250
251 // override BasicService.stop
252 public void stop() throws ServiceException {
253 // TODO - need a safer way of shutting down threads.
254 _threads.stopRequestAllWorkers();
255 _stop = true;
256 super.stop();
257 }
258
259 /***
260 * Return the next object in the queue to execute
261 * This method blocks until an object becomes available.
262 *
263 * @return Runnable the next object to execute
264 */
265 protected Runnable next() {
266 Runnable result = null;
267 synchronized (_queue) {
268 while (!_stop && _queue.isEmpty()) {
269 try {
270 _queue.wait();
271 } catch (InterruptedException ignore) {
272 // do nothing.
273 }
274 }
275 if (!_stop) {
276 result = (Runnable) _queue.removeFirst();
277 removeReference(result);
278 }
279 }
280 return result;
281 }
282
283 /***
284 * Increment the reference count to a queued Runnable object,
285 * to enable contains() to be as efficient as possible.
286 */
287 private void addReference(Runnable runner) {
288 Integer count = (Integer) _references.get(runner);
289 if (count != null) {
290 count = new Integer(count.intValue() + 1);
291 _references.put(runner, count);
292 } else {
293 _references.put(runner, new Integer(1));
294 }
295 }
296
297 /***
298 * Decrement the reference count to a queued Runnable object,
299 * removing it if no more references remain.
300 */
301 private void removeReference(Runnable runner) {
302 // decrement the no. of references to the Runnable object,
303 // removing it when the count reaches 0
304 Integer count = (Integer) _references.get(runner);
305 if (count.intValue() <= 1) {
306 _references.remove(runner);
307 } else {
308 _references.put(runner, new Integer(count.intValue() - 1));
309 }
310 }
311
312 } //-- Scheduler
This page was automatically generated by Maven