KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > scalagent > scheduler > Scheduler


1 /*
2  * Copyright (C) 2001 - 2005 ScalAgent Distributed Technologies
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17  * USA.
18  *
19  * Initial developer(s): ScalAgent Distributed Technologies
20  * Contributor(s):
21  */

22 package com.scalagent.scheduler;
23
24 import java.io.*;
25 import java.util.*;
26
27 import org.objectweb.util.monolog.api.BasicLevel;
28 import org.objectweb.util.monolog.api.Logger;
29
30 import fr.dyade.aaa.agent.*;
31
32 /**
33  * A <code>Scheduler</code> agent sends <code>Condition</code> notifications
34  * to agents registered for specific conditions, at dates and times specified
35  * by <code>ScheduleEvent</code> objects.
36  * <p>
37  * Events in the scheduler are described by <code>ScheduleEvent</code> objects,
38  * which hold a start date, a possibly not null duration, a condition name, and
39  * a class specific function to get the next scheduling date of the event. The
40  * <code>Scheduler</code> agent sends a <code>true</code> <code>Condition</code>
41  * notification at the event start date, a <code>false</code> one at the event
42  * end date if duration is not null, and then reschedules the event if the next
43  * date, as provided by the event class specific function, is not
44  * <code>null</code>. Events are stored in a double linked list of
45  * <code>ScheduleItem</code> objects. Events are deleted by the scheduler as
46  * soon as their next scheduling date is <code>null</code>.
47  * <p>
48  * The <code>Condition</code> notifications are sent to agents previously
49  * registered. An agent registers in the scheduler for all the events with a
50  * given condition name, by sending the scheduler a
51  * <code>AddConditionListener</code> notification. The agent must register
52  * itself.
53  * <p>
54  * Agents registering and events definition are two separate processes. There
55  * may exist many events signaling the same condition. Or an agent may register
56  * for a condition which no event yet signals at that time. An registered agent
57  * must eventually unregister.
58  * <p>
59  * The <code>Scheduler</code> agent may be programmed by
60  * <code>ScheduleEvent</code> notifications, or by a command line interface
61  * through an associated <code>SchedulerProxy</code> agent.
62  * The <code>SchedulerProxy</code> agent must be created and deployed
63  * explicitely.
64  * <p>
65  * The scheduler blocks waiting for the next event to be started. This is
66  * implemented using a <code>SchedulerAlarm</code> object as input stream of
67  * the <code>ProxyAgent</code> base class. The stream is responsible for waking
68  * up its scheduler by sending it a <code>ScheduleNotification</code>.
69  * The scheduler controls its stream by synchronized function calls.
70  * <p>
71  * Note: there misses functions for administering the scheduled events.
72  *
73  * @see Condition
74  * @see ScheduleEvent
75  * @see AddConditionListener
76  * @see RemoveConditionListener
77  * @see SchedulerAlarm
78  * @see SchedulerProxy
79  */

80 public class Scheduler extends ProxyAgent {
81   
82   public static Logger logger = Debug.getLogger(Scheduler.class.getName());
83   
84   /** initializes service only once */
85   private static boolean initialized = false;
86
87   /** Name of default scheduler, if any. */
88   public static String JavaDoc defaultName = "DefaultScheduler";
89
90   /**
91    * Initializes the package as a well known service.
92    * <p>
93    * Creates a <code>Scheduler</code> agent with the well known stamp
94    * <code>AgentId.SchedulerServiceStamp</code>.
95    *
96    * @param args parameters from the configuration file
97    * @param firstTime <code>true</code> when agent server starts anew
98    */

99   public static void init(String JavaDoc args, boolean firstTime) throws Exception JavaDoc {
100     if (initialized) return;
101     initialized = true;
102
103     if (! firstTime) return;
104
105     Scheduler scheduler = new Scheduler();
106     scheduler.deploy();
107   }
108
109   public static void stopService() {
110     // Do nothing
111
}
112
113   /**
114    * Gets the identifier of the default scheduler in an agent server.
115    *
116    * @param serverId id of agent server
117    * @return id of default scheduler agent
118    */

119   public static AgentId getDefault(short serverId) {
120     return new AgentId(serverId, serverId, AgentId.SchedulerServiceStamp);
121   }
122
123   /**
124    * Gets the identifier of the default scheduler in this agent server.
125    *
126    * @param serverId id of agent server
127    * @return id of default scheduler agent
128    */

129   public static AgentId getDefault() {
130     return getDefault(AgentServer.getServerId());
131   }
132
133   /** events list */
134   ScheduleItem items;
135   /** registered listeners for each condition */
136   ConditionItem conditions;
137
138   /** object in charge of waking up this agent */
139   transient SchedulerAlarm alarm;
140
141   /**
142    * Creates a local agent with unknown port.
143    *
144    * @param schedulerName symbolic name of this agent
145    */

146   public Scheduler(String JavaDoc schedulerName) {
147     super(schedulerName);
148     blockingCnx = false;
149     items = null;
150     conditions = null;
151     alarm = null;
152   }
153
154   /**
155    * Creates the default scheduler agent with well known stamp
156    * <code>AgentId.SchedulerServiceStamp</code>.
157    */

158   private Scheduler() throws IOException {
159     super(defaultName, AgentId.SchedulerServiceStamp);
160     blockingCnx = false;
161     items = null;
162     conditions = null;
163     alarm = null;
164   }
165
166   /**
167     * Creates an agent to be configured.
168     *
169     * @param to target agent server
170     * @param name symbolic name of this agent
171     */

172   public Scheduler(short to, String JavaDoc name) {
173     super(to, name);
174     blockingCnx = false;
175     items = null;
176     conditions = null;
177     alarm = null;
178   }
179
180
181   /**
182    * Provides a string image for this object.
183    *
184    * @return a string image for this object
185    */

186   public String JavaDoc toString() {
187     return "(" + super.toString() +
188       ",items=" + items +
189       ",conditions=" + conditions + ")";
190   }
191
192
193   /**
194    * Initializes the connection with the outside, up to creating
195    * the input and output streams <code>ois</code> and <code>oos</code>.
196    */

197   public void connect() throws Exception JavaDoc {
198     alarm = new SchedulerAlarm();
199     ois = alarm;
200   }
201
202   /**
203    * Closes the connection with the outside.
204    */

205   public void disconnect() throws IOException {
206     if (alarm != null) {
207       // stops alarm thread
208
alarm.close();
209       alarm = null;
210       ois = null;
211     }
212   }
213
214
215   /**
216    * Inserts an item in the list, if it does not exist.
217    *
218    * @param name name of condition item to insert.
219    * @return inserted or existing item
220    */

221   ConditionItem addCondition(String JavaDoc name) {
222     if (conditions == null) {
223       conditions = new ConditionItem(name);
224       return conditions;
225     }
226     ConditionItem prev = null;
227     for (ConditionItem item = conditions; item != null; item = item.next) {
228       int cmp = name.compareTo(item.name);
229       if (cmp == 0)
230     return item;
231       if (cmp < 0)
232     break;
233       prev = item;
234     }
235     
236     ConditionItem newItem = new ConditionItem(name);
237     if (prev == null) {
238       newItem.next = conditions;
239       conditions = newItem;
240     } else {
241       newItem.next = prev.next;
242       prev.next = newItem;
243     }
244
245     return newItem;
246   }
247
248   /**
249    * Finds an item in the list.
250    *
251    * @param name name of condition item to find.
252    * @return existing item, or null
253    */

254   ConditionItem findCondition(String JavaDoc name) {
255     if (conditions == null)
256       return null;
257     for (ConditionItem item = conditions; item != null; item = item.next) {
258       int cmp = name.compareTo(item.name);
259       if (cmp == 0)
260     return item;
261       if (cmp < 0)
262     break;
263     }
264     
265     return null;
266   }
267
268   /**
269    * Removes an item from the list.
270    *
271    * @param name name of condition item to remove.
272    */

273   void removeCondition(String JavaDoc name) {
274     if (conditions == null)
275       return;
276     ConditionItem prev = null;
277     for (ConditionItem item = conditions; item != null; item = item.next) {
278       int cmp = name.compareTo(item.name);
279       if (cmp == 0)
280     break;
281       if (cmp < 0)
282     return;
283       prev = item;
284     }
285     
286     if (prev == null) {
287       conditions = conditions.next;
288     } else {
289       prev.next = prev.next.next;
290     }
291   }
292
293   /**
294    * Initializes the transient members of this agent.
295    * This function is first called by the factory agent,
296    * then by the system each time the agent server is restarted.
297    *
298    * @param firstTime true when first called by the factory
299    */

300   protected void agentInitialize(boolean firstTime) throws Exception JavaDoc {
301     // initializes the alarm stream
302
super.agentInitialize(firstTime);
303
304     // reschedules all events
305
checkItems(true);
306   }
307
308   /**
309    * Reacts to <code>Scheduler</code> specific notifications.
310    * Analyzes the notification type, then calls the appropriate
311    * <code>doReact</code> function. By default calls <code>react</code>
312    * from base class.
313    * Handled notification types are :
314    * <code>ScheduleEvent</code> and derived types,
315    * <code>ScheduleNotification</code>,
316    * <code>AddConditionListener</code>,
317    * <code>RemoveConditionListener</code>,
318    * <code>AgentDeleteRequest</code>.
319    * <p>
320    * Note: management of <code>AgentDeleteRequest</code> notifications has been
321    * removed to conform to the behaviour expected by the configurator agent.
322    * This should be fixed.
323    *
324    * @param from agent sending notification
325    * @param not notification to react to
326    */

327   public void react(AgentId from, Notification not) throws Exception JavaDoc {
328     if (logger.isLoggable(BasicLevel.DEBUG))
329       logger.log(BasicLevel.DEBUG, "Scheduler.react(" + from + ',' + not + ')');
330     if (not instanceof ScheduleEvent) {
331       doReact(from, (ScheduleEvent) not);
332     } else if (not instanceof ScheduleNotification) {
333       doReact(from, (ScheduleNotification) not);
334     } else if (not instanceof AddConditionListener) {
335       doReact(from, (AddConditionListener) not);
336     } else if (not instanceof RemoveConditionListener) {
337       doReact(from, (RemoveConditionListener) not);
338       // } else if (not instanceof AgentDeleteRequest) {
339
// doReact(from, (AgentDeleteRequest) not);
340
} else {
341       super.react(from, not);
342     }
343   }
344
345   /**
346    * Reacts to <code>ScheduleEvent</code> notifications.
347    * Calls <code>insertItem</code>.
348    *
349    * @param from agent sending notification
350    * @param not notification to react to
351    */

352   protected void doReact(AgentId from, ScheduleEvent not) throws Exception JavaDoc {
353     // inserts event in list
354
insertItem(not);
355
356     // checks for ripe items
357
checkItems(false);
358   }
359
360   /**
361    * Reacts to <code>ScheduleNotification</code> notifications.
362    * Calls <code>checkItems</code>.
363    *
364    * @param from agent sending notification
365    * @param not notification to react to
366    */

367   protected void doReact(AgentId from, ScheduleNotification not) throws Exception JavaDoc {
368     // checks for ripe items
369
checkItems(false);
370   }
371
372   /**
373    * Reacts to <code>AddConditionListener</code> notifications.
374    * Calls <code>addConditionListener</code>.
375    *
376    * @param from agent sending notification
377    * @param not notification to react to
378    */

379   protected void doReact(AgentId from, AddConditionListener not) throws Exception JavaDoc {
380     addConditionListener(not.name, from);
381   }
382
383   /**
384    * Reacts to <code>RemoveConditionListener</code> notifications.
385    * Calls <code>removeConditionListener</code>.
386    *
387    * @param from agent sending notification
388    * @param not notification to react to
389    */

390   protected void doReact(AgentId from, RemoveConditionListener not) throws Exception JavaDoc {
391     removeConditionListener(not.name, from);
392   }
393
394   /**
395    * Reacts to <code>AgentDeleteRequest</code> notifications.
396    * Calls <code>delete</code>.
397    *
398    * @param from agent sending notification
399    * @param not notification to react to
400    */

401   // protected void doReact(AgentId from, AgentDeleteRequest not) throws Exception {
402
// delete();
403
// }
404

405   /**
406    * Reacts to <code>ScheduleEvent</code> notifications.
407    *
408    * @param not notification to react to
409    */

410   protected void insertItem(ScheduleEvent not) throws Exception JavaDoc {
411     Date now = new Date();
412     ScheduleItem newItem = new ScheduleItem(not);
413
414     // finds next scheduling date of event
415
newItem.date = not.nextDate(now);
416
417     // checks for an outdated scheduling
418
if (newItem.date == null) {
419       if (! not.outdatedRestart) return;
420       newItem.date = now;
421     }
422
423     insertItem(newItem);
424   }
425
426   /**
427    * Inserts an item in the list ordered by date.
428    * Inserts at list head a null dated item.
429    *
430    * @param newItem item to insert
431    */

432   protected void insertItem(ScheduleItem newItem) throws Exception JavaDoc {
433     if (newItem.date == null) return;
434
435     if (items == null) {
436       items = newItem;
437     } else {
438       ScheduleItem prev = null;
439
440       for (ScheduleItem item = items; item != null; item = item.next) {
441         if (!newItem.date.after(item.date))
442           break;
443         prev = item;
444       }
445
446       if (prev == null) {
447     if (items != null) {
448       newItem.next = items;
449       items.prev = newItem;
450     }
451     items = newItem;
452       } else {
453     newItem.next = prev.next;
454     newItem.prev = prev;
455     prev.next = newItem;
456     if (newItem.next != null)
457       newItem.next.prev = newItem;
458       }
459     }
460   }
461
462   /**
463    * Checks for ripe events.
464    * Sends <code>Condition</code> notification and reschedule event.
465    *
466    * @param restart <code>true</code> if called on restart
467    */

468   protected void checkItems(boolean restart) throws Exception JavaDoc {
469     if (logger.isLoggable(BasicLevel.DEBUG))
470       logger.log(BasicLevel.DEBUG, "Scheduler.checkItems(" + restart + ')');
471     Date now = new Date();
472
473     checkLoop:
474     for (ScheduleItem item = items; item != null;) {
475       if (item.date != null &&
476       item.date.after(now))
477     break checkLoop;
478
479       ScheduleItem nextItem = item.next;
480
481       if (! restart ||
482       item.event.outdatedRestart ||
483       item.status || // closes event
484
(item.event.duration > 0 &&
485        new Date(item.date.getTime() +
486             (item.event.duration * 1000)).after(now))
487     ) {
488     // signals event
489
item.status = ! item.status;
490     signalEvent(item);
491       }
492
493       // reschedules event
494
if (item.status == true) {
495     if (item.event.duration > 0) {
496       // finds event end date
497
item.date.setTime(item.date.getTime() + (item.event.duration * 1000));
498     } else {
499       // sends no false condition when duration is null
500
item.status = ! item.status;
501     }
502       }
503       if (item.status == false) {
504     item.date = item.event.nextDate(now);
505         // this call must return a date later than now
506
// for fear of an infinite loop
507
if ((item.date != null) && ! item.date.after(now))
508           item.date = null;
509       }
510
511       if (item.date == null) {
512     // removes event from list
513
removeItem(item);
514     item = nextItem;
515     continue checkLoop;
516       }
517
518       // checks if event needs to be changed in the list
519
if (nextItem == null ||
520       ! item.date.after(nextItem.date)) {
521     continue checkLoop;
522       }
523
524       // removes event from list
525
removeItem(item);
526       // and inserts it at its new place
527
insertItem(item);
528       
529       item = nextItem;
530     }
531
532     // sets next wake-up time
533
if (items != null) {
534       alarm.setTime(items.date.getTime() - now.getTime());
535     }
536   }
537
538   /**
539    * Removes an item from the list
540    *
541    * @param item item to remove
542    */

543   void removeItem(ScheduleItem item) {
544     if (item.next != null)
545       item.next.prev = item.prev;
546     if (item.prev == null)
547       items = item.next;
548     else
549       item.prev.next = item.next;
550     item.prev = item.next = null;
551
552     // checks if condition may be deleted
553
ConditionItem citem = findCondition(item.event.name);
554     if (citem == null)
555       return;
556     Enumeration listeners = citem.listeners.getListeners();
557     if (listeners != null &&
558     listeners.hasMoreElements())
559       return;
560     for (ScheduleItem sitem = items; sitem != null; sitem = sitem.next) {
561       if (sitem.event.name.equals(item.event.name))
562     return;
563     }
564     removeCondition(item.event.name);
565   }
566
567   /**
568    * Adds an agent to the list of listeners for an event.
569    *
570    * @param condition condition name of events to register to
571    * @param listener registering agent
572    */

573   protected void addConditionListener(String JavaDoc condition, AgentId listener) throws Exception JavaDoc {
574     ConditionItem citem = addCondition(condition);
575     citem.listeners.addListener(listener);
576
577     for (ScheduleItem item = items; item != null; item = item.next) {
578       if (! item.event.name.equals(condition))
579     continue;
580       if (item.status)
581     sendTo(listener, new Condition(item.event.name, item.status));
582     }
583   }
584
585   /**
586    * Removes an agent from the list of listeners for an event.
587    *
588    * @param condition condition name of events to unregister to
589    * @param listener unregistering agent
590    */

591   protected void removeConditionListener(String JavaDoc condition, AgentId listener) throws Exception JavaDoc {
592     ConditionItem citem = findCondition(condition);
593     if (citem == null)
594       return;
595     citem.listeners.removeListener(listener);
596
597     // checks if condition may be deleted
598
Enumeration listeners = citem.listeners.getListeners();
599     if (listeners != null &&
600     listeners.hasMoreElements())
601       return;
602     for (ScheduleItem item = items; item != null; item = item.next) {
603       if (item.event.name.equals(condition))
604     return;
605     }
606     removeCondition(condition);
607   }
608
609   /**
610    * Signals a condition to registered agents.
611    *
612    * @param item event to signal
613    */

614   protected void signalEvent(ScheduleItem item) {
615     ConditionItem condition = findCondition(item.event.name);
616     if (condition == null)
617       return;
618     sendTo(condition.listeners, new Condition(item.event.name, item.status));
619   }
620   
621   /**
622    * Overrides the ProxyAgent behavior in order not
623    * to react to DriverDone notifications.
624    * Useless for the Scheduler.
625    * Moreover it may stop the scheduler when restarting
626    * the agent server after a stop.
627    */

628   protected void driverDone(DriverDone not) throws IOException {
629
630   }
631 }
632
Popular Tags