1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
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 }
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
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 }
207 if ( entry.getVolatility()) {
208 remove(entry);
209 }
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 }
220 if ( check==null) {
221 check = new ArrayList();
222 }
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 }
256 String entryName = entry.getEntryName();
257 entry.setEntryGroup( group );
258
259 CronEntry current = cronManager.getCronEntry( group, entryName);
260 log.debug("Checking if cronEntry " + group+"."+entryName+ " aleready exists:" + (current !=null));
261 if ( current !=null) {
262
263 remainingEntryNames.remove( entryName );
264
265 if ( !entry.equals(current)) {
266 log.debug("Cron entry exists but has changed");
267
268
269 cronManager.removeCronEntry( group,entryName);
270 cronManager.addCronEntry(entry);
271 }
272
273
274 } else {
275
276 cronManager.addCronEntry(entry);
277 }
278
279
280
281 }
282
283 Iterator remove = remainingEntryNames.iterator();
284 while ( remove.hasNext()) {
285 cronManager.removeCronEntry( group, (String)remove.next());
286 }
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 }
328
329 }
330
331 }