PageRenderTime 43ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/jboss-5.1.0/varia/src/main/org/jboss/varia/scheduler/Scheduler.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 1330 lines | 852 code | 75 blank | 403 comment | 130 complexity | d15fcdefc32cb25abef87b9b93b858d6 MD5 | raw file
  1. /*
  2. * JBoss, Home of Professional Open Source.
  3. * Copyright 2008, Red Hat Middleware LLC, and individual contributors
  4. * as indicated by the @author tags. See the copyright.txt file in the
  5. * distribution for a full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.jboss.varia.scheduler;
  23. import java.lang.reflect.Constructor;
  24. import java.security.InvalidParameterException;
  25. import java.text.SimpleDateFormat;
  26. import java.util.Date;
  27. import java.util.StringTokenizer;
  28. import java.util.Vector;
  29. import java.util.Arrays;
  30. import javax.management.InstanceNotFoundException;
  31. import javax.management.MalformedObjectNameException;
  32. import javax.management.Notification;
  33. import javax.management.NotificationEmitter;
  34. import javax.management.NotificationListener;
  35. import javax.management.ObjectName;
  36. import javax.management.MBeanServerInvocationHandler;
  37. import javax.management.timer.Timer;
  38. import javax.management.timer.TimerMBean;
  39. import javax.management.timer.TimerNotification;
  40. import org.jboss.logging.Logger;
  41. import org.jboss.system.ServiceMBeanSupport;
  42. import org.jboss.util.Classes;
  43. /**
  44. * Schedules a timer task that calls an MBean or Object instance.
  45. * Any MBean operation can be called. Object instances must implement the
  46. * {@link Schedulable} interface.
  47. * <p />
  48. * Create a separate Scheduler MBean for every MBean or Object you wish to call.
  49. * One example naming strategy for calling an MBean named:
  50. <code>example:type=HelloWorld</code>
  51. * is to create a similarly named:
  52. <code>example:type=Scheduler,call=HelloWorld</code> MBean.
  53. * This way you should not run into a name conflict.
  54. * <p>
  55. * This MBean registers a notification listener with an
  56. * javax.management.timer.Timer MBean. If the Timer does not exist, this MBean
  57. * will create it. Each Timer can handle multiple Scheduler instances.
  58. * </p>
  59. *
  60. * @author <a href="mailto:andreas@jboss.org">Andreas Schaefer</a>
  61. * @author Cameron (camtabor)
  62. * @version $Revision: 81038 $
  63. */
  64. public class Scheduler extends ServiceMBeanSupport
  65. implements SchedulerMBean
  66. {
  67. // -------------------------------------------------------------------------
  68. // Constants
  69. // -------------------------------------------------------------------------
  70. public static String JNDI_NAME = "scheduler:domain";
  71. public static String JMX_NAME = "scheduler";
  72. public static String DEFAULT_TIMER_NAME = ScheduleManager.DEFAULT_TIMER_NAME;
  73. private static final int NOTIFICATION = 0;
  74. private static final int DATE = 1;
  75. private static final int REPETITIONS = 2;
  76. private static final int SCHEDULER_NAME = 3;
  77. private static final int NULL = 4;
  78. // -------------------------------------------------------------------------
  79. // Members
  80. // -------------------------------------------------------------------------
  81. private long mActualSchedulePeriod;
  82. private long mRemainingRepetitions = 0;
  83. private int mNotificationID = -1;
  84. private String mTimerName = DEFAULT_TIMER_NAME;
  85. private ObjectName mTimerObjectName;
  86. private TimerMBean mTimer;
  87. private NotificationEmitter mTimerEmitter;
  88. private Schedulable mSchedulable;
  89. private boolean mScheduleIsStarted = false;
  90. private boolean mWaitForNextCallToStop = false;
  91. private boolean mStartOnStart = false;
  92. private boolean mIsRestartPending = true;
  93. // Pending values which can be different to the actual ones
  94. private boolean mUseMBean = false;
  95. private Class mSchedulableClass;
  96. private String mSchedulableArguments;
  97. private String[] mSchedulableArgumentList = new String[0];
  98. private String mSchedulableArgumentTypes;
  99. private Class[] mSchedulableArgumentTypeList = new Class[0];
  100. private ObjectName mSchedulableMBean;
  101. private String mSchedulableMBeanMethod;
  102. private String mSchedulableMBeanMethodName;
  103. private int[] mSchedulableMBeanArguments = new int[0];
  104. private String[] mSchedulableMBeanArgumentTypes = new String[0];
  105. private SimpleDateFormat mDateFormatter;
  106. private Date mStartDate;
  107. private String mStartDateString;
  108. private boolean mStartDateIsNow;
  109. private long mSchedulePeriod;
  110. private long mInitialRepetitions;
  111. private boolean mFixedRate = false;
  112. private NotificationListener mListener;
  113. // -------------------------------------------------------------------------
  114. // Constructors
  115. // -------------------------------------------------------------------------
  116. /**
  117. * Constructs a new Scheduler instance.
  118. **/
  119. public Scheduler()
  120. {
  121. }
  122. /**
  123. * Constructs a new Scheduler instance.
  124. * @param pSchedulableClass
  125. * @param pSchedulePeriod
  126. */
  127. public Scheduler(String pSchedulableClass,
  128. long pSchedulePeriod)
  129. {
  130. setStartAtStartup(true);
  131. setSchedulableClass(pSchedulableClass);
  132. setSchedulePeriod(pSchedulePeriod);
  133. }
  134. /**
  135. * Constructs a new Scheduler instance.
  136. * @param pSchedulableClass
  137. * @param pInitArguments
  138. * @param pInitTypes
  139. * @param pInitialStartDate
  140. * @param pSchedulePeriod
  141. * @param pNumberOfRepetitions
  142. */
  143. public Scheduler(String pSchedulableClass,
  144. String pInitArguments,
  145. String pInitTypes,
  146. String pInitialStartDate,
  147. long pSchedulePeriod,
  148. long pNumberOfRepetitions
  149. )
  150. {
  151. setStartAtStartup(true);
  152. setSchedulableClass(pSchedulableClass);
  153. setSchedulableArguments(pInitArguments);
  154. setSchedulableArgumentTypes(pInitTypes);
  155. setInitialStartDate(pInitialStartDate);
  156. setSchedulePeriod(pSchedulePeriod);
  157. setInitialRepetitions(pNumberOfRepetitions);
  158. }
  159. /**
  160. * Constructs a new Scheduler instance.
  161. * @param pSchedulableClass
  162. * @param pInitArguments
  163. * @param pInitTypes
  164. * @param pDateFormat
  165. * @param pInitialStartDate
  166. * @param pSchedulePeriod
  167. * @param pNumberOfRepetitions
  168. */
  169. public Scheduler(
  170. String pSchedulableClass,
  171. String pInitArguments,
  172. String pInitTypes,
  173. String pDateFormat,
  174. String pInitialStartDate,
  175. long pSchedulePeriod,
  176. long pNumberOfRepetitions
  177. )
  178. {
  179. setStartAtStartup(true);
  180. setSchedulableClass(pSchedulableClass);
  181. setSchedulableArguments(pInitArguments);
  182. setSchedulableArgumentTypes(pInitTypes);
  183. setDateFormat(pDateFormat);
  184. setInitialStartDate(pInitialStartDate);
  185. setSchedulePeriod(pSchedulePeriod);
  186. setInitialRepetitions(pNumberOfRepetitions);
  187. }
  188. // -------------------------------------------------------------------------
  189. // SchedulerMBean Methods
  190. // -------------------------------------------------------------------------
  191. //
  192. private void checkMBean() {
  193. if (mSchedulableMBean == null)
  194. {
  195. log.debug("Schedulable MBean Object Name is not set");
  196. throw new InvalidParameterException(
  197. "Schedulable MBean must be set"
  198. );
  199. }
  200. if (mSchedulableMBeanMethodName == null)
  201. {
  202. mSchedulableMBeanMethodName = "perform";
  203. mSchedulableMBeanArguments = new int[]{DATE, REPETITIONS};
  204. mSchedulableMBeanArgumentTypes = new String[]{
  205. Date.class.getName(),
  206. Integer.TYPE.getName()
  207. };
  208. }
  209. }
  210. private void createSchedulable() {
  211. if (mSchedulableClass == null)
  212. {
  213. throw new InvalidParameterException("Schedulable Class not set");
  214. }
  215. if (mSchedulableArgumentList.length != mSchedulableArgumentTypeList.length)
  216. {
  217. throw new InvalidParameterException(
  218. "Schedulable Class Arguments and Types do not match in length"
  219. );
  220. }
  221. // Create all the Objects for the Constructor to be called
  222. Object[] lArgumentList = new Object[mSchedulableArgumentTypeList.length];
  223. try
  224. {
  225. for (int i = 0; i < mSchedulableArgumentTypeList.length; i++)
  226. {
  227. Class lClass = mSchedulableArgumentTypeList[i];
  228. if (lClass == Boolean.TYPE)
  229. {
  230. lArgumentList[i] = new Boolean(mSchedulableArgumentList[i]);
  231. }
  232. else if (lClass == Integer.TYPE)
  233. {
  234. lArgumentList[i] = new Integer(mSchedulableArgumentList[i]);
  235. }
  236. else if (lClass == Long.TYPE)
  237. {
  238. lArgumentList[i] = new Long(mSchedulableArgumentList[i]);
  239. }
  240. else if (lClass == Short.TYPE)
  241. {
  242. lArgumentList[i] = new Short(mSchedulableArgumentList[i]);
  243. }
  244. else if (lClass == Float.TYPE)
  245. {
  246. lArgumentList[i] = new Float(mSchedulableArgumentList[i]);
  247. }
  248. else if (lClass == Double.TYPE)
  249. {
  250. lArgumentList[i] = new Double(mSchedulableArgumentList[i]);
  251. }
  252. else if (lClass == Byte.TYPE)
  253. {
  254. lArgumentList[i] = new Byte(mSchedulableArgumentList[i]);
  255. }
  256. else if (lClass == Character.TYPE)
  257. {
  258. lArgumentList[i] = new Character(mSchedulableArgumentList[i].charAt(0));
  259. }
  260. else
  261. {
  262. Constructor lConstructor = lClass.getConstructor(new Class[]{String.class});
  263. lArgumentList[i] = lConstructor.newInstance(new Object[]{mSchedulableArgumentList[i]});
  264. }
  265. }
  266. }
  267. catch (Exception e)
  268. {
  269. log.error("Could not load or create constructor argument", e);
  270. throw new InvalidParameterException("Could not load or create a constructor argument");
  271. }
  272. try
  273. {
  274. // Check if constructor is found
  275. Constructor lSchedulableConstructor = mSchedulableClass.getConstructor(mSchedulableArgumentTypeList);
  276. // Create an instance of it
  277. mSchedulable = (Schedulable) lSchedulableConstructor.newInstance(lArgumentList);
  278. }
  279. catch (Exception e)
  280. {
  281. log.error("Could not find the constructor or create Schedulable instance", e);
  282. throw new InvalidParameterException("Could not find the constructor or create the Schedulable Instance");
  283. }
  284. }
  285. private Date getNow() {
  286. long now = System.currentTimeMillis();
  287. return new Date(now + 1000);
  288. }
  289. private void initStartDate() {
  290. // Register the Schedule at the Timer
  291. // If start date is NOW then take the current date
  292. if (mStartDateIsNow)
  293. {
  294. mStartDate = getNow();
  295. }
  296. else
  297. {
  298. // Check if initial start date is in the past
  299. if (mStartDate.before(new Date()))
  300. {
  301. // If then first check if a repetition is in the future
  302. long lNow = new Date().getTime() + 100;
  303. long lSkipRepeats = ((lNow - mStartDate.getTime()) / mActualSchedulePeriod) + 1;
  304. log.debug("Old start date: " + mStartDate + ", now: " + new Date(lNow) + ", Skip repeats: " + lSkipRepeats);
  305. if (mRemainingRepetitions > 0)
  306. {
  307. // If not infinit loop
  308. if (lSkipRepeats >= mRemainingRepetitions)
  309. {
  310. // No repetition left -> exit
  311. log.info("No repetitions left because start date is in the past and could " +
  312. "not be reached by Initial Repetitions * Schedule Period");
  313. return;
  314. }
  315. else
  316. {
  317. // Reduce the missed hits
  318. mRemainingRepetitions -= lSkipRepeats;
  319. }
  320. }
  321. mStartDate = new Date(mStartDate.getTime() + (lSkipRepeats * mActualSchedulePeriod));
  322. }
  323. }
  324. }
  325. /**
  326. * Starts the schedule if the schedule is stopped otherwise nothing will happen.
  327. * The Schedule is immediately set to started even the first call is in the
  328. * future.
  329. *
  330. * @jmx:managed-operation
  331. *
  332. * @throws InvalidParameterException If any of the necessary values are not set
  333. * or invalid (especially for the Schedulable
  334. * class attributes).
  335. */
  336. public void startSchedule()
  337. {
  338. if (isStarted())
  339. {
  340. log.debug("already started");
  341. return;
  342. }
  343. if (mUseMBean)
  344. {
  345. checkMBean();
  346. }
  347. else
  348. {
  349. createSchedulable();
  350. }
  351. mRemainingRepetitions = mInitialRepetitions;
  352. mActualSchedulePeriod = mSchedulePeriod;
  353. initStartDate();
  354. log.debug("Schedule initial call to: " + mStartDate + ", remaining repetitions: " + mRemainingRepetitions);
  355. mNotificationID = mTimer.addNotification(
  356. "Schedule", "Scheduler Notification",
  357. null, // new Integer(getID()), // User Object
  358. mStartDate,
  359. new Long(mActualSchedulePeriod),
  360. mRemainingRepetitions < 0 ? new Long(0) : new Long(mRemainingRepetitions),
  361. Boolean.valueOf(mFixedRate)
  362. );
  363. mListener = mUseMBean ? new MBeanListener() : new PojoScheduler();
  364. mTimerEmitter.addNotificationListener(
  365. mListener,
  366. new ScheduleManager.IdNotificationFilter(mNotificationID),
  367. null
  368. );
  369. mScheduleIsStarted = true;
  370. mIsRestartPending = false;
  371. }
  372. /**
  373. * Stops the schedule immediately.
  374. * @jmx:managed-operation
  375. */
  376. public void stopSchedule()
  377. {
  378. stopSchedule(true);
  379. }
  380. /**
  381. * Stops the schedule because it is either not used anymore or to restart it with
  382. * new values.
  383. *
  384. * @jmx:managed-operation
  385. *
  386. * @param pDoItNow If true the schedule will be stopped without waiting for the next
  387. * scheduled call otherwise the next call will be performed before
  388. * the schedule is stopped.
  389. */
  390. public void stopSchedule(boolean pDoItNow)
  391. {
  392. log.debug("stopSchedule(" + pDoItNow + ")");
  393. try
  394. {
  395. if (mNotificationID < 0)
  396. {
  397. mScheduleIsStarted = false;
  398. mWaitForNextCallToStop = false;
  399. return;
  400. }
  401. if (pDoItNow)
  402. {
  403. log.debug("stopSchedule(), removing schedule id: " + mNotificationID);
  404. mWaitForNextCallToStop = false;
  405. if (mListener != null)
  406. {
  407. mTimerEmitter.removeNotificationListener(mListener);
  408. try
  409. {
  410. mTimer.removeNotification(mNotificationID);
  411. }
  412. catch (InstanceNotFoundException e)
  413. {
  414. log.trace(e);
  415. }
  416. mListener = null;
  417. }
  418. mNotificationID = -1;
  419. mScheduleIsStarted = false;
  420. }
  421. else
  422. {
  423. mWaitForNextCallToStop = true;
  424. }
  425. }
  426. catch (Exception e)
  427. {
  428. log.error("stopSchedule failed", e);
  429. }
  430. }
  431. /**
  432. * Stops the server right now and starts it right now.
  433. *
  434. * @jmx:managed-operation
  435. */
  436. public void restartSchedule()
  437. {
  438. stopSchedule();
  439. startSchedule();
  440. }
  441. /**
  442. * @jmx:managed-attribute
  443. *
  444. * @return Full qualified Class name of the schedulable class called by the schedule or
  445. * null if not set.
  446. */
  447. public String getSchedulableClass()
  448. {
  449. if (mSchedulableClass == null)
  450. {
  451. return null;
  452. }
  453. return mSchedulableClass.getName();
  454. }
  455. /**
  456. * Sets the fully qualified Class name of the Schedulable Class being called by the
  457. * Scheduler. Must be set before the Schedule is started. Please also set the
  458. * {@link #setSchedulableArguments} and {@link #setSchedulableArgumentTypes}.
  459. *
  460. * @jmx:managed-attribute
  461. *
  462. * @param pSchedulableClass Fully Qualified Schedulable Class.
  463. *
  464. * @throws InvalidParameterException If the given value is not a valid class or cannot
  465. * be loaded by the Scheduler or is not of instance
  466. * Schedulable.
  467. */
  468. public void setSchedulableClass(String pSchedulableClass)
  469. throws InvalidParameterException
  470. {
  471. if (pSchedulableClass == null || pSchedulableClass.equals(""))
  472. {
  473. throw new InvalidParameterException("Schedulable Class cannot be empty or undefined");
  474. }
  475. try
  476. {
  477. // Try to load the Schedulable Class
  478. ClassLoader loader = TCLActions.getContextClassLoader();
  479. mSchedulableClass = loader.loadClass(pSchedulableClass);
  480. // Check if instance of Schedulable
  481. if (!isSchedulable(mSchedulableClass))
  482. {
  483. String msg = "Given class " + pSchedulableClass + " is not instance of Schedulable";
  484. StringBuffer info = new StringBuffer(msg);
  485. info.append("\nThe SchedulableClass info:");
  486. Classes.displayClassInfo(mSchedulableClass, info);
  487. info.append("\nSchedulable.class info:");
  488. Classes.displayClassInfo(Schedulable.class, info);
  489. log.debug(info.toString());
  490. throw new InvalidParameterException(msg);
  491. }
  492. }
  493. catch (ClassNotFoundException e)
  494. {
  495. log.info("Failed to find: "+pSchedulableClass, e);
  496. throw new InvalidParameterException(
  497. "Given class " + pSchedulableClass + " is not not found"
  498. );
  499. }
  500. mIsRestartPending = true;
  501. mUseMBean = false;
  502. }
  503. /**
  504. * @jmx:managed-attribute
  505. *
  506. * @return Comma seperated list of Constructor Arguments used to instantiate the
  507. * Schedulable class instance. Right now only basic data types, String and
  508. * Classes with a Constructor with a String as only argument are supported.
  509. */
  510. public String getSchedulableArguments()
  511. {
  512. return mSchedulableArguments;
  513. }
  514. /**
  515. * @jmx:managed-attribute
  516. *
  517. * Sets the comma seperated list of arguments for the Schedulable class. Note that
  518. * this list must have as many elements as the Schedulable Argument Type list otherwise
  519. * the start of the Scheduler will fail. Right now only basic data types, String and
  520. * Classes with a Constructor with a String as only argument are supported.
  521. *
  522. * @param pArgumentList List of arguments used to create the Schedulable intance. If
  523. * the list is null or empty then the no-args constructor is used.
  524. */
  525. public void setSchedulableArguments(String pArgumentList)
  526. {
  527. if (pArgumentList == null || pArgumentList.equals(""))
  528. {
  529. mSchedulableArgumentList = new String[0];
  530. }
  531. else
  532. {
  533. StringTokenizer lTokenizer = new StringTokenizer(pArgumentList, ",");
  534. Vector lList = new Vector();
  535. while (lTokenizer.hasMoreTokens())
  536. {
  537. String lToken = lTokenizer.nextToken().trim();
  538. if (lToken.equals(""))
  539. {
  540. lList.add("null");
  541. }
  542. else
  543. {
  544. lList.add(lToken);
  545. }
  546. }
  547. mSchedulableArgumentList = (String[]) lList.toArray(new String[0]);
  548. }
  549. mSchedulableArguments = pArgumentList;
  550. mIsRestartPending = true;
  551. }
  552. /**
  553. * @jmx:managed-attribute
  554. *
  555. * @return A comma seperated list of Argument Types which should match the list of
  556. * arguments.
  557. */
  558. public String getSchedulableArgumentTypes()
  559. {
  560. return mSchedulableArgumentTypes;
  561. }
  562. /**
  563. * Sets the comma seperated list of argument types for the Schedulable class. This will
  564. * be used to find the right constructor and to created the right instances to call the
  565. * constructor with. This list must have as many elements as the Schedulable Arguments
  566. * list otherwise the start of the Scheduler will fail. Right now only basic data types,
  567. * String and Classes with a Constructor with a String as only argument are supported.
  568. *
  569. * @jmx:managed-attribute
  570. *
  571. * @param pTypeList List of arguments used to create the Schedulable intance. If
  572. * the list is null or empty then the no-args constructor is used.
  573. *
  574. * @throws InvalidParameterException If the given list contains a unknow datat type.
  575. */
  576. public void setSchedulableArgumentTypes(String pTypeList)
  577. throws InvalidParameterException
  578. {
  579. if (pTypeList == null || pTypeList.equals(""))
  580. {
  581. mSchedulableArgumentTypeList = new Class[0];
  582. }
  583. else
  584. {
  585. StringTokenizer lTokenizer = new StringTokenizer(pTypeList, ",");
  586. Vector lList = new Vector();
  587. while (lTokenizer.hasMoreTokens())
  588. {
  589. String lToken = lTokenizer.nextToken().trim();
  590. // Get the class
  591. Class lClass = null;
  592. if (lToken.equals("short"))
  593. {
  594. lClass = Short.TYPE;
  595. }
  596. else if (lToken.equals("int"))
  597. {
  598. lClass = Integer.TYPE;
  599. }
  600. else if (lToken.equals("long"))
  601. {
  602. lClass = Long.TYPE;
  603. }
  604. else if (lToken.equals("byte"))
  605. {
  606. lClass = Byte.TYPE;
  607. }
  608. else if (lToken.equals("char"))
  609. {
  610. lClass = Character.TYPE;
  611. }
  612. else if (lToken.equals("float"))
  613. {
  614. lClass = Float.TYPE;
  615. }
  616. else if (lToken.equals("double"))
  617. {
  618. lClass = Double.TYPE;
  619. }
  620. else if (lToken.equals("boolean"))
  621. {
  622. lClass = Boolean.TYPE;
  623. }
  624. if (lClass == null)
  625. {
  626. try
  627. {
  628. // Load class to check if available
  629. ClassLoader loader = TCLActions.getContextClassLoader();
  630. lClass = loader.loadClass(lToken);
  631. }
  632. catch (ClassNotFoundException cnfe)
  633. {
  634. throw new InvalidParameterException(
  635. "The argument type: " + lToken + " is not a valid class or could not be found"
  636. );
  637. }
  638. }
  639. lList.add(lClass);
  640. }
  641. mSchedulableArgumentTypeList = (Class[]) lList.toArray(new Class[0]);
  642. }
  643. mSchedulableArgumentTypes = pTypeList;
  644. mIsRestartPending = true;
  645. }
  646. /**
  647. * @jmx:managed-attribute
  648. *
  649. * @return Object Name if a Schedulable MBean is set
  650. */
  651. public String getSchedulableMBean()
  652. {
  653. return mSchedulableMBean == null ?
  654. null :
  655. mSchedulableMBean.toString();
  656. }
  657. /**
  658. * Sets the fully qualified JMX MBean name of the Schedulable MBean to be called.
  659. * <b>Attention: </b>if set the all values set by {@link #setSchedulableClass},
  660. * {@link #setSchedulableArguments} and {@link #setSchedulableArgumentTypes} are
  661. * cleared and not used anymore. Therefore only use either Schedulable Class or
  662. * Schedulable MBean. If {@link #setSchedulableMBeanMethod} is not set then the
  663. * schedule method as in the {@link Schedulable#perform} will be called with the
  664. * same arguments. Also note that the Object Name will not be checked if the
  665. * MBean is available. If the MBean is not available it will not be called but
  666. * the remaining repetitions will be decreased.
  667. *
  668. * @jmx:managed-attribute
  669. *
  670. * @param pSchedulableMBean JMX MBean Object Name which should be called.
  671. *
  672. * @throws InvalidParameterException If the given value is an valid Object Name.
  673. */
  674. public void setSchedulableMBean(String pSchedulableMBean)
  675. throws InvalidParameterException
  676. {
  677. if (pSchedulableMBean == null)
  678. {
  679. throw new InvalidParameterException("Schedulable MBean must be specified");
  680. }
  681. try
  682. {
  683. mSchedulableMBean = new ObjectName(pSchedulableMBean);
  684. mUseMBean = true;
  685. }
  686. catch (MalformedObjectNameException e)
  687. {
  688. throw new InvalidParameterException("Schedulable MBean name invalid " + pSchedulableMBean);
  689. }
  690. }
  691. /**
  692. * @return Schedulable MBean Method description if set
  693. **/
  694. public String getSchedulableMBeanMethod()
  695. {
  696. return mSchedulableMBeanMethod;
  697. }
  698. /**
  699. * Sets the method name to be called on the Schedulable MBean. It can optionally be
  700. * followed by an opening bracket, list of attributes (see below) and a closing bracket.
  701. * The list of attributes can contain:
  702. * <ul>
  703. * <li>NOTIFICATION which will be replaced by the timers notification instance
  704. * (javax.management.Notification)</li>
  705. * <li>DATE which will be replaced by the date of the notification call
  706. * (java.util.Date)</li>
  707. * <li>REPETITIONS which will be replaced by the number of remaining repetitions
  708. * (long)</li>
  709. * <li>SCHEDULER_NAME which will be replaced by the Object Name of the Scheduler
  710. * (javax.management.ObjectName)</li>
  711. * <li>any full qualified Class name which the Scheduler will be set a "null" value
  712. * for it</li>
  713. * </ul>
  714. * <br>
  715. * An example could be: "doSomething( NOTIFICATION, REPETITIONS, java.lang.String )"
  716. * where the Scheduler will pass the timer's notification instance, the remaining
  717. * repetitions as int and a null to the MBean's doSomething() method which must
  718. * have the following signature: doSomething( javax.management.Notification, long,
  719. * java.lang.String ).
  720. *
  721. * @jmx:managed-attribute
  722. *
  723. * @param pSchedulableMBeanMethod Name of the method to be called optional followed
  724. * by method arguments (see above).
  725. *
  726. * @throws InvalidParameterException If the given value is not of the right
  727. * format
  728. */
  729. public void setSchedulableMBeanMethod(String pSchedulableMBeanMethod)
  730. throws InvalidParameterException
  731. {
  732. if (pSchedulableMBeanMethod == null)
  733. {
  734. mSchedulableMBeanMethod = null;
  735. return;
  736. }
  737. int lIndex = pSchedulableMBeanMethod.indexOf('(');
  738. String lMethodName;
  739. if (lIndex == -1)
  740. {
  741. lMethodName = pSchedulableMBeanMethod.trim();
  742. mSchedulableMBeanArguments = new int[0];
  743. mSchedulableMBeanArgumentTypes = new String[0];
  744. }
  745. else
  746. {
  747. lMethodName = pSchedulableMBeanMethod.substring(0, lIndex).trim();
  748. }
  749. if (lMethodName.equals(""))
  750. {
  751. lMethodName = "perform";
  752. }
  753. if (lIndex >= 0)
  754. {
  755. int lIndex2 = pSchedulableMBeanMethod.indexOf(')');
  756. if (lIndex2 < lIndex)
  757. {
  758. throw new InvalidParameterException("Schedulable MBean Method: closing bracket must be after opening bracket");
  759. }
  760. if (lIndex2 < pSchedulableMBeanMethod.length() - 1)
  761. {
  762. String lRest = pSchedulableMBeanMethod.substring(lIndex2 + 1).trim();
  763. if (lRest.length() > 0)
  764. {
  765. throw new InvalidParameterException("Schedulable MBean Method: nothing should be after closing bracket");
  766. }
  767. }
  768. String lArguments = pSchedulableMBeanMethod.substring(lIndex + 1, lIndex2).trim();
  769. if (lArguments.equals(""))
  770. {
  771. mSchedulableMBeanArguments = new int[0];
  772. mSchedulableMBeanArgumentTypes = new String[0];
  773. }
  774. else
  775. {
  776. StringTokenizer lTokenizer = new StringTokenizer(lArguments, ", ");
  777. mSchedulableMBeanArguments = new int[lTokenizer.countTokens()];
  778. mSchedulableMBeanArgumentTypes = new String[lTokenizer.countTokens()];
  779. for (int i = 0; lTokenizer.hasMoreTokens(); i++)
  780. {
  781. String lToken = lTokenizer.nextToken().trim();
  782. if (lToken.equals("NOTIFICATION"))
  783. {
  784. mSchedulableMBeanArguments[i] = NOTIFICATION;
  785. mSchedulableMBeanArgumentTypes[i] = Notification.class.getName();
  786. }
  787. else if (lToken.equals("DATE"))
  788. {
  789. mSchedulableMBeanArguments[i] = DATE;
  790. mSchedulableMBeanArgumentTypes[i] = Date.class.getName();
  791. }
  792. else if (lToken.equals("REPETITIONS"))
  793. {
  794. mSchedulableMBeanArguments[i] = REPETITIONS;
  795. mSchedulableMBeanArgumentTypes[i] = Long.TYPE.getName();
  796. }
  797. else if (lToken.equals("SCHEDULER_NAME"))
  798. {
  799. mSchedulableMBeanArguments[i] = SCHEDULER_NAME;
  800. mSchedulableMBeanArgumentTypes[i] = ObjectName.class.getName();
  801. }
  802. else
  803. {
  804. mSchedulableMBeanArguments[i] = NULL;
  805. //AS ToDo: maybe later to check if this class exists !
  806. mSchedulableMBeanArgumentTypes[i] = lToken;
  807. }
  808. }
  809. }
  810. }
  811. mSchedulableMBeanMethodName = lMethodName;
  812. mSchedulableMBeanMethod = pSchedulableMBeanMethod;
  813. }
  814. /**
  815. * @jmx:managed-attribute
  816. *
  817. * @return True if the Scheduler uses a Schedulable MBean, false if it uses a
  818. * Schedulable class
  819. */
  820. public boolean isUsingMBean()
  821. {
  822. return mUseMBean;
  823. }
  824. /**
  825. * @jmx:managed-attribute
  826. *
  827. * @return Schedule Period between two scheduled calls in Milliseconds. It will always
  828. * be bigger than 0 except it returns -1 then the schedule is stopped.
  829. */
  830. public long getSchedulePeriod()
  831. {
  832. return mSchedulePeriod;
  833. }
  834. /**
  835. * Sets the Schedule Period between two scheduled call.
  836. *
  837. * @jmx:managed-attribute
  838. *
  839. * @param pPeriod Time between to scheduled calls (after the initial call) in Milliseconds.
  840. * This value must be bigger than 0.
  841. *
  842. * @throws InvalidParameterException If the given value is less or equal than 0
  843. */
  844. public void setSchedulePeriod(long pPeriod)
  845. {
  846. if (pPeriod <= 0)
  847. {
  848. throw new InvalidParameterException("Schedulable Period may be not less or equals than 0");
  849. }
  850. mSchedulePeriod = pPeriod;
  851. mIsRestartPending = true;
  852. }
  853. /**
  854. * @jmx:managed-attribute
  855. *
  856. * @return the date format
  857. */
  858. public String getDateFormat()
  859. {
  860. if (mDateFormatter == null)
  861. mDateFormatter = new SimpleDateFormat();
  862. return mDateFormatter.toPattern();
  863. }
  864. /**
  865. * Sets the date format used to parse date/times
  866. *
  867. * @jmx:managed-attribute
  868. *
  869. * @param dateFormat The date format when empty or null the locale is used to parse dates
  870. */
  871. public void setDateFormat(String dateFormat)
  872. {
  873. if (dateFormat == null || dateFormat.trim().length() == 0)
  874. mDateFormatter = new SimpleDateFormat();
  875. else
  876. mDateFormatter = new SimpleDateFormat(dateFormat);
  877. }
  878. /**
  879. * @jmx:managed-attribute
  880. *
  881. * @return Date (and time) of the first scheduled. For value see {@link #setInitialStartDate}
  882. * method.
  883. */
  884. public String getInitialStartDate()
  885. {
  886. return mStartDateString;
  887. }
  888. /**
  889. * Sets the first scheduled call. If the date is in the past the scheduler tries to find the
  890. * next available start date.
  891. *
  892. * @jmx:managed-attribute
  893. *
  894. * @param pStartDate Date when the initial call is scheduled. It can be either:
  895. * <ul>
  896. * <li>
  897. * NOW: date will be the current date (new Date()) plus 1 seconds
  898. * </li><li>
  899. * Date as String able to be parsed by SimpleDateFormat with default format
  900. * </li><li>
  901. * Date as String parsed using the date format attribute
  902. * </li><li>
  903. * Milliseconds since 1/1/1970
  904. * </li>
  905. * </ul>
  906. * If the date is in the past the Scheduler
  907. * will search a start date in the future with respect to the initial repe-
  908. * titions and the period between calls. This means that when you restart
  909. * the MBean (restarting JBoss etc.) it will start at the next scheduled
  910. * time. When no start date is available in the future the Scheduler will
  911. * not start.<br>
  912. * Example: if you start your Schedulable everyday at Noon and you restart
  913. * your JBoss server then it will start at the next Noon (the same if started
  914. * before Noon or the next day if start after Noon).
  915. */
  916. public void setInitialStartDate(String pStartDate)
  917. {
  918. mStartDateString = pStartDate == null ? "" : pStartDate.trim();
  919. if (mStartDateString.equals(""))
  920. {
  921. mStartDate = new Date(0);
  922. }
  923. else if (mStartDateString.equals("NOW"))
  924. {
  925. mStartDate = getNow();
  926. mStartDateIsNow = true;
  927. }
  928. else
  929. {
  930. try
  931. {
  932. long lDate = new Long(pStartDate).longValue();
  933. mStartDate = new Date(lDate);
  934. mStartDateIsNow = false;
  935. }
  936. catch (NumberFormatException e)
  937. {
  938. try
  939. {
  940. if (mDateFormatter == null)
  941. {
  942. mDateFormatter = new SimpleDateFormat();
  943. }
  944. mStartDate = mDateFormatter.parse(mStartDateString);
  945. mStartDateIsNow = false;
  946. }
  947. catch (Exception e2)
  948. {
  949. log.error("Could not parse given date string: " + mStartDateString, e2);
  950. throw new InvalidParameterException("Schedulable Date is not of correct format: " + mStartDateString);
  951. }
  952. }
  953. }
  954. log.debug("Initial Start Date is set to: " + mStartDate);
  955. }
  956. /**
  957. * @jmx:managed-attribute
  958. *
  959. * @return Number of scheduled calls initially. If -1 then there is not limit.
  960. */
  961. public long getInitialRepetitions()
  962. {
  963. return mInitialRepetitions;
  964. }
  965. /**
  966. * Sets the initial number of scheduled calls.
  967. *
  968. * @jmx:managed-attribute
  969. *
  970. * @param pNumberOfCalls Initial Number of scheduled calls. If -1 then the number
  971. * is infinite
  972. *
  973. * @throws InvalidParameterException If the given value is less or equal than 0
  974. */
  975. public void setInitialRepetitions(long pNumberOfCalls)
  976. {
  977. if (pNumberOfCalls <= 0)
  978. {
  979. pNumberOfCalls = -1;
  980. }
  981. mInitialRepetitions = pNumberOfCalls;
  982. mIsRestartPending = true;
  983. }
  984. /**
  985. * @jmx:managed-attribute
  986. *
  987. * @return Number of remaining repetitions. If -1 then there is no limit.
  988. */
  989. public long getRemainingRepetitions()
  990. {
  991. return mRemainingRepetitions;
  992. }
  993. /**
  994. * @jmx:managed-attribute
  995. *
  996. * @return True if the schedule is up and running. If you want to start the schedule
  997. * with another values by using {@ #startSchedule} you have to stop the schedule
  998. * first with {@ #stopSchedule} and wait until this method returns false.
  999. */
  1000. public boolean isStarted()
  1001. {
  1002. return mScheduleIsStarted;
  1003. }
  1004. /**
  1005. * @jmx:managed-attribute
  1006. *
  1007. * @return True if any attributes are changed but the Schedule is not restarted yet.
  1008. */
  1009. public boolean isRestartPending()
  1010. {
  1011. return mIsRestartPending;
  1012. }
  1013. /**
  1014. * @jmx:managed-attribute
  1015. *
  1016. * @return True if the Schedule when the Scheduler is started
  1017. */
  1018. public boolean isStartAtStartup()
  1019. {
  1020. return mStartOnStart;
  1021. }
  1022. /**
  1023. * Set the scheduler to start when MBean started or not. Note that this method only
  1024. * affects when the {@link #startService startService()} gets called (normally at
  1025. * startup time.
  1026. *
  1027. * @jmx:managed-attribute
  1028. *
  1029. * @param pStartAtStartup True if Schedule has to be started at startup time
  1030. */
  1031. public void setStartAtStartup(boolean pStartAtStartup)
  1032. {
  1033. mStartOnStart = pStartAtStartup;
  1034. }
  1035. /**
  1036. * @jmx:managed-attribute
  1037. *
  1038. * @return True if this Scheduler is active and will send notifications in the future
  1039. */
  1040. public boolean isActive()
  1041. {
  1042. return isStarted() && mRemainingRepetitions != 0;
  1043. }
  1044. /**
  1045. * @jmx:managed-attribute
  1046. *
  1047. * @return Name of the Timer MBean used in here
  1048. */
  1049. public String getTimerName()
  1050. {
  1051. return mTimerName;
  1052. }
  1053. /**
  1054. * @jmx:managed-attribute
  1055. *
  1056. * @param pTimerName Object Name of the Timer MBean to
  1057. * be used. If null or not a valid ObjectName
  1058. * the default will be used
  1059. */
  1060. public void setTimerName(String pTimerName)
  1061. {
  1062. mTimerName = pTimerName;
  1063. }
  1064. /**
  1065. * @jmx:managed-attribute
  1066. *
  1067. * @param fixedRate the default scheduling to use, fixed-rate or fixed-delay (false, default)
  1068. */
  1069. public void setFixedRate(boolean fixedRate)
  1070. {
  1071. mFixedRate = fixedRate;
  1072. }
  1073. /**
  1074. * @jmx:managed-attribute
  1075. *
  1076. * @return the default scheduling to use
  1077. */
  1078. public boolean getFixedRate()
  1079. {
  1080. return mFixedRate;
  1081. }
  1082. // -------------------------------------------------------------------------
  1083. // Methods
  1084. // -------------------------------------------------------------------------
  1085. // -------------------------------------------------------------------------
  1086. // ServiceMBean - Methods
  1087. // -------------------------------------------------------------------------
  1088. protected void startService()
  1089. throws Exception
  1090. {
  1091. mTimerObjectName = new ObjectName(mTimerName);
  1092. if (!getServer().isRegistered(mTimerObjectName))
  1093. {
  1094. getServer().createMBean(Timer.class.getName(), mTimerObjectName);
  1095. }
  1096. mTimer = (TimerMBean)MBeanServerInvocationHandler.newProxyInstance(getServer(),
  1097. mTimerObjectName, TimerMBean.class, true);
  1098. mTimerEmitter = (NotificationEmitter)mTimer;
  1099. if (!mTimer.isActive())
  1100. {
  1101. mTimer.start();
  1102. }
  1103. if (mStartOnStart)
  1104. {
  1105. log.debug("Start Scheduler on start up time");
  1106. startSchedule();
  1107. }
  1108. }
  1109. protected void stopService()
  1110. {
  1111. stopSchedule();
  1112. }
  1113. private static boolean isSchedulable(Class c)
  1114. {
  1115. boolean lFound = false;
  1116. do
  1117. {
  1118. Class[] lInterfaces = c.getInterfaces();
  1119. for (int i = 0; i < lInterfaces.length; i++)
  1120. {
  1121. if (lInterfaces[i] == Schedulable.class)
  1122. {
  1123. lFound = true;
  1124. break;
  1125. }
  1126. }
  1127. c = c.getSuperclass();
  1128. }
  1129. while (c != null && !lFound);
  1130. return lFound;
  1131. }
  1132. /**
  1133. * Base class for listeners.
  1134. */
  1135. public abstract class BaseListener
  1136. implements NotificationListener
  1137. {
  1138. final Logger log = Logger.getLogger(BaseListener.class);
  1139. public void handleNotification(
  1140. Notification notification,
  1141. Object handback
  1142. )
  1143. {
  1144. boolean trace = log.isTraceEnabled();
  1145. if (trace)
  1146. {
  1147. log.trace("handleNotification: " + notification);
  1148. }
  1149. if (!isStarted())
  1150. {
  1151. log.trace("Scheduler not started");
  1152. stopSchedule();
  1153. return;
  1154. }
  1155. if (mRemainingRepetitions == 0)
  1156. {
  1157. log.trace("No more repetitions");
  1158. stopSchedule();
  1159. return;
  1160. }
  1161. if (mRemainingRepetitions > 0)
  1162. {
  1163. mRemainingRepetitions--;
  1164. if (trace)
  1165. log.trace("Remaining repetitions: " + mRemainingRepetitions);
  1166. }
  1167. invoke(notification);
  1168. if (mWaitForNextCallToStop)
  1169. {
  1170. stopSchedule();
  1171. }
  1172. }
  1173. /**
  1174. * Invokes the scheduler method.
  1175. */
  1176. protected abstract void invoke(Notification notification);
  1177. }
  1178. // -------------------------------------------------------------------------
  1179. // Inner Classes
  1180. // -------------------------------------------------------------------------
  1181. /**
  1182. * Calls {@link Schedulable#perform} on a plain Java Object.
  1183. */
  1184. public class PojoScheduler extends BaseListener
  1185. {
  1186. protected void invoke(Notification notification)
  1187. {
  1188. ClassLoader currentTCL = TCLActions.getContextClassLoader();
  1189. try
  1190. {
  1191. ClassLoader loader = TCLActions.getClassLoader(mSchedulable.getClass());
  1192. TCLActions.setContextClassLoader(loader);
  1193. Date lTimeStamp = new Date(notification.getTimeStamp());
  1194. mSchedulable.perform(lTimeStamp, getRemainingRepetitions());
  1195. }
  1196. catch (Exception e)
  1197. {
  1198. log.error("Scheduler.perform call failed", e);
  1199. }
  1200. finally
  1201. {
  1202. TCLActions.setContextClassLoader(currentTCL);
  1203. }
  1204. }
  1205. }
  1206. /**
  1207. * Invokes an operation on an MBean.
  1208. */
  1209. public class MBeanListener extends BaseListener
  1210. {
  1211. protected void invoke(Notification notification)
  1212. {
  1213. Object[] lArguments = new Object[mSchedulableMBeanArguments.length];
  1214. for (int i = 0; i < lArguments.length; i++)
  1215. {
  1216. switch (mSchedulableMBeanArguments[i])
  1217. {
  1218. case NOTIFICATION:
  1219. lArguments[i] = notification;
  1220. break;
  1221. case DATE:
  1222. lArguments[i] = new Date(notification.getTimeStamp());
  1223. break;
  1224. case REPETITIONS:
  1225. lArguments[i] = new Long(mRemainingRepetitions);
  1226. break;
  1227. case SCHEDULER_NAME:
  1228. lArguments[i] = getServiceName();
  1229. break;
  1230. default:
  1231. lArguments[i] = null;
  1232. }
  1233. }
  1234. if (log.isTraceEnabled())
  1235. {
  1236. log.debug("invoke " + mSchedulableMBean + " " + mSchedulableMBeanMethodName);
  1237. log.debug("arguments: " + Arrays.asList(lArguments));
  1238. log.debug("argument types: " + Arrays.asList(mSchedulableMBeanArgumentTypes));
  1239. }
  1240. try
  1241. {
  1242. getServer().invoke(
  1243. mSchedulableMBean,
  1244. mSchedulableMBeanMethodName,
  1245. lArguments,
  1246. mSchedulableMBeanArgumentTypes
  1247. );
  1248. }
  1249. catch (Exception e)
  1250. {
  1251. log.error("Invoke failed for " + mSchedulableMBean + " " + mSchedulableMBeanMethodName, e);
  1252. }
  1253. }
  1254. }
  1255. }