View Javadoc

1   /*
2    * Copyright (c) 2003 Peter Antman, Teknik i Media  <peter.antman@tim.se>
3    *
4    * $Id: CronEditor.java,v 1.1.1.1 2004/05/19 12:14:31 pra Exp $
5    *
6    * This library is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License as published by the Free Software Foundation; either
9    * version 2 of the License, or (at your option) any later version
10   * 
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General Public License for more details.
15   * 
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this library; if not, write to the Free Software
18   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   */
20  package org.backsource.qcron;
21  import java.util.List;
22  import java.util.Iterator;
23  import java.util.HashSet;
24  import java.util.ArrayList;
25  
26  import javax.management.ObjectName;
27  import javax.management.MBeanServer;
28  import javax.management.MalformedObjectNameException;
29  import org.backsource.jmx.MBeanProxyFactory;
30  import org.backsource.jmx.ServiceMBeanSupport;
31  import org.backsource.jmx.ObjectNameFactory;
32  /***
33   * An editor to handle adding/removal of CronEntrys to CronManager.
34   *
35   *<p>The editor has two primary purposes:</p>
36   * <ul>
37   *  <li>To make its easy to add con entries in jboss-service.xml files.</li>
38   *  <li>To not have to care if one tries to add an already existing Job/Trigger pair. This will be checked by the Editor.</li>
39   * </ul>
40   * <p>The editors normal usage is to use it as a cron spool holder; i.e one sets up a particular CronEditor for a domain and it will manage all CronEntries by giing them to the editor through setCronEntries, wich takes a list of ObjectNames each pointing to a CronEntry. The editor will manage its domain; and ad and remove jobs from quertz depending on the state of the entries list when started. This means that an editor used in this way will remove persisten jobs fron quertz if they are not part a the list any longer; and it will add jobs that has become part of the list but has not yet been added to Quartz.</p>
41   * <p>It is also possible to use the editor to create a single group/job. The name of the group must then be unique.</p>
42   * <p>The editor will allways create a Job/Trigger pair,with the same group/name. The group will be taken from the domain of this MBean if available and the name will be from the rest of the ObjectName. if used programatically the group and name parts must be set manually.</p>
43   <p>See {@link org.backsource.qcron} for an example of how to configure it.</p>
44   <p>For some more info on the attributes that is used to create an edior with a single entry,see {@link org.backsource.qcron.CronEntry}</p>
45   *
46   * @author <a href="mailto:pra@tim.se">Peter Antman</a>
47   * @version $Revision: 1.1.1.1 $
48   * @jmx:mbean name="cron:name=CronEditor" extends="org.backsource.jmx.ServiceMBean"
49  
50   */
51  
52  public class CronEditor extends ServiceMBeanSupport
53     implements CronEditorMBean{
54     public static final javax.management.ObjectName OBJECT_NAME = ObjectNameFactory.create("cron:name=CronEditor");
55     protected ObjectName objectName = OBJECT_NAME;
56     protected MBeanServer server;
57     
58     /***
59      * The CronManager to use when adding/removing jobs.
60      */
61     protected ObjectName cronManagerName;
62     /***
63      * The CronManager to use when adding/removing jobs.
64      */
65     protected CronManager cronManager;
66     /***
67      * The list of cronEntries this editor manages; is a list of ObjectNames.
68      */
69     protected List cronEntries;
70  
71     // Attributes used when editor only handles one CronEntry.
72     protected boolean recover = false;
73     protected boolean volatility = false;
74     protected String cronExp;
75     protected ObjectName jobObjectName;
76     protected CronEntry entry;
77  
78     public CronEditor (){
79        
80     }
81     
82     protected ObjectName getObjectName(MBeanServer server, ObjectName name)
83        throws MalformedObjectNameException
84     {
85        this.server = server;
86        if ( name != null) {
87           this.objectName = name;
88        } // end of if ()
89        
90        return this.objectName;
91     }
92  
93     /***
94      * Set the cron manager to use when editing.
95      * @jmx:managed-attribute
96      */
97     public void setCronManagerName(ObjectName manager) {
98        this.cronManagerName = manager;
99     }
100 
101    /***
102     * @jmx:managed-attribute
103     */
104    public ObjectName getCronManagerName() {
105       return cronManagerName;
106    }
107 
108    /***
109     * Set the list of CronEntries this edior handles.
110     * @para cronEntries a list of ObjectNames each pointing to a {@link CronEntry}  
111     * Set the cron manager to use when editing.
112     * @jmx:managed-attribute
113     */
114    public void setCronEntries(List cronEntries) {
115       this.cronEntries = cronEntries;
116    }
117    /***
118     * @jmx:managed-attribute
119     */
120    public List getCronEntries() {
121       return cronEntries;
122    }
123 
124    // --- Attributes used when editor only handles one CronEntry.
125    /***
126     * Set the cron expression to use for the trigger.
127     *
128     * @see org.quartz.CronTrigger
129     * @jmx:managed-attribute
130     */
131    public void setCronExp(String cronExp) {
132       this.cronExp = cronExp;
133    }
134    /***
135     * @jmx:managed-attribute
136     */
137    public String getCronExp() {
138       return cronExp;
139    }
140 
141    /***
142     * Set the object name of an MBean which should be called when trigger triggers.
143     * @jmx:managed-attribute
144     */
145    public void setJobObjectName(ObjectName jobName) {
146       this.jobObjectName = jobName;
147    }
148    /***
149     * @jmx:managed-attribute
150     */
151    public ObjectName getJobObjectName() {
152       return jobObjectName;
153    }
154    /***
155     * Scheduler should try to recover job if it should have triggered while the
156     * server was down, default to false.
157     *<p>if set to true: volatile must be false.</p>
158     * @jmx:managed-attribute
159     */
160    public void setRequestsRecovery(boolean recover) {
161       this.recover = recover;     
162    }
163    /***
164     * @jmx:managed-attribute
165     */
166    public boolean getRequestsRecovery() {
167       return recover;
168    }
169    /***
170     * Save job to persistent state if false, default is true, i.e no saving to persisten state!
171     *
172     * @jmx:managed-attribute
173     */ 
174    public void setVolatility(boolean volatility) {
175       this.volatility = volatility;
176    }
177    /***
178     * @jmx:managed-attribute
179     */
180    public boolean getVolatility() {
181       return volatility;
182    }
183 
184    /***
185     * Start the editor, calls edit.
186     */
187    protected void startService() throws Exception {
188       cronManager = (CronManager)MBeanProxyFactory.create(CronManager.class,
189                                                           cronManagerName.toString());
190 
191       edit();
192    }
193    
194    /***
195     * Stop the editor, remove al volatile entries from CronManager
196     */
197    protected void stopService() throws Exception {
198       Iterator entries = getEntries().iterator();
199       while (entries.hasNext() ) {
200          Object o = entries.next();
201          CronEntry entry = null;
202          if ( o instanceof CronEntry) {
203             entry = (CronEntry)o;
204          }else if (o instanceof ObjectName) {
205             entry = getCronEntry((ObjectName)o);    
206          } // end of else
207          if ( entry.getVolatility()) {
208             remove(entry);
209          } // end of if ()
210          
211       }
212    }
213 
214    private List getEntries() throws CronException{
215       List check = cronEntries;
216       if ( cronEntries == null && jobObjectName != null) {
217          check = new ArrayList();
218          check.add( getCronEntry() );
219       } // end of if ()
220       if ( check==null) {
221          check = new ArrayList();
222       } // end of if ()
223       
224       log.debug("Returning entries:" + check);
225       return check;
226    }
227 
228    /***
229     * Edit the current list/or single CronEntry(ies), if list is non null any single config is discarded.
230     *
231     * <p>This method will go through al CronEntry objects available in List. Jobbs will be handled under a group name wich is constructed from the ObjectName of the editor and a name from the CronEntry ObjectName. The algoritm is this:</p>
232     * <ul>
233     *   <li>Check if CronEntryv already exist in Quartz.</li>
234     *   <li>if it does, chech if it equals the entry handled, if it does: skip it, otherwise change it.</li>
235     *   <li>if it does not exists: just ad it.</li>
236     *   <li>When al entries have been handled, any job still in Quartz belonging to the domain of the editor that has not been handled will be removed!</li>
237     * </ul>
238     *     
239     * @jmx:managed-attribute
240     */
241    public void edit() throws CronException{
242       String group = objectName.toString();
243      
244       List check = getEntries();
245       HashSet remainingEntryNames = new HashSet(cronManager.getCronEntryNames( group ));
246       log.debug("Old cronentries is: "+remainingEntryNames);
247       Iterator entries = check.iterator();
248       while (entries.hasNext() ) {
249          Object o = entries.next();
250          CronEntry entry = null;
251          if ( o instanceof CronEntry) {
252             entry = (CronEntry)o;
253          }else if (o instanceof ObjectName) {
254             entry = getCronEntry((ObjectName)o);    
255          } // end of else
256          String entryName = entry.getEntryName();
257          entry.setEntryGroup( group );
258          // Check if entry exists.
259          CronEntry current = cronManager.getCronEntry( group, entryName);
260          log.debug("Checking if cronEntry " + group+"."+entryName+ " aleready exists:" + (current !=null));
261          if ( current !=null) {
262             // Remove from remaining so that we know we have handled it
263             remainingEntryNames.remove( entryName );
264             // Check if is has changed
265             if ( !entry.equals(current)) {
266                log.debug("Cron entry exists but has changed");
267                // its has changed; add it again (FIXME: does this work, or do
268                // we have to remove first?
269                cronManager.removeCronEntry( group,entryName);
270                cronManager.addCronEntry(entry);
271             } // end of if ()
272             
273             
274          } else {
275             // Its new           
276             cronManager.addCronEntry(entry);
277          } // end of else
278          
279 
280          
281       } // end of for ()
282       // Remove all entries for group that was not treated
283       Iterator remove = remainingEntryNames.iterator();
284       while ( remove.hasNext()) {
285          cronManager.removeCronEntry( group, (String)remove.next());
286       } // end of while ()
287       
288    }
289    
290    /***
291     * Remove the given CronEntry from manager and quertz, if the manager is configured through a jboss-service.xml file this file has the last say when component is restarted.
292     * @jmx:managed-attribute
293     */
294    public void remove(CronEntry entry) throws CronException{
295       cronManager.removeCronEntry(entry.getEntryGroup(),entry.getEntryName());
296    }
297 
298    /***
299     * Get the single cron entry this editor holds, it will get group and name from this edtitrs ObjectName, will be null if it has cronEntries or if jobObjectName is non null.
300     * @jmx:managed-attribute
301     */
302    public CronEntry getCronEntry() throws CronException{
303       CronEntry e = null;
304       if ( cronEntries == null && jobObjectName != null) {
305                
306          e = new CronEntry(cronExp,jobObjectName);
307          e.setEntryGroup(objectName.getDomain());
308          e.setEntryName(objectName.toString());
309          e.setCronExp(cronExp);
310          e.setVolatility(volatility);
311          e.setRequestsRecovery(recover);
312       }
313       return e;
314    }
315 
316    /***
317     * Get cron entry from ObjectName.     
318     * @jmx:managed-attribute
319     
320    */
321    public CronEntry getCronEntry(ObjectName name) throws CronException{
322       try {
323 
324          return (CronEntry)server.invoke(name,"instance",new Object[]{},new String[]{});
325       } catch (Exception e) {
326          throw new CronException("Could not get entry from mbean "+name+":" +e,e);
327       } // end of try-catch
328       
329    }
330    
331 }// CronEditor