| 1 22 package org.jboss.varia.scheduler; 23 24 import java.lang.reflect.Constructor ; 25 import java.security.InvalidParameterException ; 26 import java.text.SimpleDateFormat ; 27 import java.util.Date ; 28 import java.util.StringTokenizer ; 29 import java.util.Vector ; 30 import java.util.Arrays ; 31 32 import javax.management.InstanceNotFoundException ; 33 import javax.management.MalformedObjectNameException ; 34 import javax.management.Notification ; 35 import javax.management.NotificationEmitter ; 36 import javax.management.NotificationListener ; 37 import javax.management.ObjectName ; 38 import javax.management.MBeanServerInvocationHandler ; 39 import javax.management.timer.Timer ; 40 import javax.management.timer.TimerMBean ; 41 import javax.management.timer.TimerNotification ; 42 43 import org.jboss.logging.Logger; 44 import org.jboss.system.ServiceMBeanSupport; 45 import org.jboss.util.Classes; 46 47 68 public class Scheduler extends ServiceMBeanSupport 69 implements SchedulerMBean 70 { 71 72 76 public static String JNDI_NAME = "scheduler:domain"; 77 public static String JMX_NAME = "scheduler"; 78 public static String DEFAULT_TIMER_NAME = ScheduleManager.DEFAULT_TIMER_NAME; 79 80 private static final int NOTIFICATION = 0; 81 private static final int DATE = 1; 82 private static final int REPETITIONS = 2; 83 private static final int SCHEDULER_NAME = 3; 84 private static final int NULL = 4; 85 86 90 private long mActualSchedulePeriod; 91 private long mRemainingRepetitions = 0; 92 private int mNotificationID = -1; 93 private String mTimerName = DEFAULT_TIMER_NAME; 94 private ObjectName mTimerObjectName; 95 private TimerMBean mTimer; 96 private NotificationEmitter mTimerEmitter; 97 private Schedulable mSchedulable; 98 99 private boolean mScheduleIsStarted = false; 100 private boolean mWaitForNextCallToStop = false; 101 private boolean mStartOnStart = false; 102 private boolean mIsRestartPending = true; 103 104 private boolean mUseMBean = false; 106 107 private Class mSchedulableClass; 108 private String mSchedulableArguments; 109 private String [] mSchedulableArgumentList = new String [0]; 110 private String mSchedulableArgumentTypes; 111 private Class [] mSchedulableArgumentTypeList = new Class [0]; 112 113 private ObjectName mSchedulableMBean; 114 private String mSchedulableMBeanMethod; 115 private String mSchedulableMBeanMethodName; 116 private int[] mSchedulableMBeanArguments = new int[0]; 117 private String [] mSchedulableMBeanArgumentTypes = new String [0]; 118 119 private SimpleDateFormat mDateFormatter; 120 private Date mStartDate; 121 private String mStartDateString; 122 private boolean mStartDateIsNow; 123 private long mSchedulePeriod; 124 private long mInitialRepetitions; 125 private boolean mFixedRate = false; 126 127 private NotificationListener mListener; 128 129 133 136 public Scheduler() 137 { 138 } 139 140 145 public Scheduler(String pSchedulableClass, 146 long pSchedulePeriod) 147 { 148 setStartAtStartup(true); 149 setSchedulableClass(pSchedulableClass); 150 setSchedulePeriod(pSchedulePeriod); 151 } 152 153 162 public Scheduler(String pSchedulableClass, 163 String pInitArguments, 164 String pInitTypes, 165 String pInitialStartDate, 166 long pSchedulePeriod, 167 long pNumberOfRepetitions 168 ) 169 { 170 setStartAtStartup(true); 171 setSchedulableClass(pSchedulableClass); 172 setSchedulableArguments(pInitArguments); 173 setSchedulableArgumentTypes(pInitTypes); 174 setInitialStartDate(pInitialStartDate); 175 setSchedulePeriod(pSchedulePeriod); 176 setInitialRepetitions(pNumberOfRepetitions); 177 } 178 179 189 public Scheduler( 190 String pSchedulableClass, 191 String pInitArguments, 192 String pInitTypes, 193 String pDateFormat, 194 String pInitialStartDate, 195 long pSchedulePeriod, 196 long pNumberOfRepetitions 197 ) 198 { 199 setStartAtStartup(true); 200 setSchedulableClass(pSchedulableClass); 201 setSchedulableArguments(pInitArguments); 202 setSchedulableArgumentTypes(pInitTypes); 203 setDateFormat(pDateFormat); 204 setInitialStartDate(pInitialStartDate); 205 setSchedulePeriod(pSchedulePeriod); 206 setInitialRepetitions(pNumberOfRepetitions); 207 } 208 209 214 private void checkMBean() { 215 if (mSchedulableMBean == null) 216 { 217 log.debug("Schedulable MBean Object Name is not set"); 218 throw new InvalidParameterException ( 219 "Schedulable MBean must be set" 220 ); 221 } 222 if (mSchedulableMBeanMethodName == null) 223 { 224 mSchedulableMBeanMethodName = "perform"; 225 mSchedulableMBeanArguments = new int[]{DATE, REPETITIONS}; 226 mSchedulableMBeanArgumentTypes = new String []{ 227 Date .class.getName(), 228 Integer.TYPE.getName() 229 }; 230 } 231 } 232 233 private void createSchedulable() { 234 if (mSchedulableClass == null) 235 { 236 throw new InvalidParameterException ("Schedulable Class not set"); 237 } 238 if (mSchedulableArgumentList.length != mSchedulableArgumentTypeList.length) 239 { 240 throw new InvalidParameterException ( 241 "Schedulable Class Arguments and Types do not match in length" 242 ); 243 } 244 Object [] lArgumentList = new Object [mSchedulableArgumentTypeList.length]; 246 try 247 { 248 for (int i = 0; i < mSchedulableArgumentTypeList.length; i++) 249 { 250 Class lClass = mSchedulableArgumentTypeList[i]; 251 if (lClass == Boolean.TYPE) 252 { 253 lArgumentList[i] = new Boolean (mSchedulableArgumentList[i]); 254 } 255 else if (lClass == Integer.TYPE) 256 { 257 lArgumentList[i] = new Integer (mSchedulableArgumentList[i]); 258 } 259 else if (lClass == Long.TYPE) 260 { 261 lArgumentList[i] = new Long (mSchedulableArgumentList[i]); 262 } 263 else if (lClass == Short.TYPE) 264 { 265 lArgumentList[i] = new Short (mSchedulableArgumentList[i]); 266 } 267 else if (lClass == Float.TYPE) 268 { 269 lArgumentList[i] = new Float (mSchedulableArgumentList[i]); 270 } 271 else if (lClass == Double.TYPE) 272 { 273 lArgumentList[i] = new Double (mSchedulableArgumentList[i]); 274 } 275 else if (lClass == Byte.TYPE) 276 { 277 lArgumentList[i] = new Byte (mSchedulableArgumentList[i]); 278 } 279 else if (lClass == Character.TYPE) 280 { 281 lArgumentList[i] = new Character (mSchedulableArgumentList[i].charAt(0)); 282 } 283 else 284 { 285 Constructor lConstructor = lClass.getConstructor(new Class []{String .class}); 286 lArgumentList[i] = lConstructor.newInstance(new Object []{mSchedulableArgumentList[i]}); 287 } 288 } 289 } 290 catch (Exception e) 291 { 292 log.error("Could not load or create constructor argument", e); 293 throw new InvalidParameterException ("Could not load or create a constructor argument"); 294 } 295 try 296 { 297 Constructor lSchedulableConstructor = mSchedulableClass.getConstructor(mSchedulableArgumentTypeList); 299 mSchedulable = (Schedulable) lSchedulableConstructor.newInstance(lArgumentList); 301 } 302 catch (Exception e) 303 { 304 log.error("Could not find the constructor or create Schedulable instance", e); 305 throw new InvalidParameterException ("Could not find the constructor or create the Schedulable Instance"); 306 } 307 } 308 309 private Date getNow() { 310 long now = System.currentTimeMillis(); 311 return new Date (now + 1000); 312 } 313 314 private void initStartDate() { 315 if (mStartDateIsNow) 318 { 319 mStartDate = getNow(); 320 } 321 else 322 { 323 if (mStartDate.before(new Date ())) 325 { 326 long lNow = new Date ().getTime() + 100; 328 long lSkipRepeats = ((lNow - mStartDate.getTime()) / mActualSchedulePeriod) + 1; 329 log.debug("Old start date: " + mStartDate + ", now: " + new Date (lNow) + ", Skip repeats: " + lSkipRepeats); 330 if (mRemainingRepetitions > 0) 331 { 332 if (lSkipRepeats >= mRemainingRepetitions) 334 { 335 log.info("No repetitions left because start date is in the past and could " + 337 "not be reached by Initial Repetitions * Schedule Period"); 338 return; 339 } 340 else 341 { 342 mRemainingRepetitions -= lSkipRepeats; 344 } 345 } 346 mStartDate = new Date (mStartDate.getTime() + (lSkipRepeats * mActualSchedulePeriod)); 347 } 348 } 349 } 350 351 362 public void startSchedule() 363 { 364 if (isStarted()) 365 { 366 log.debug("already started"); 367 return; 368 } 369 370 if (mUseMBean) 371 { 372 checkMBean(); 373 } 374 else 375 { 376 createSchedulable(); 377 } 378 379 mRemainingRepetitions = mInitialRepetitions; 380 mActualSchedulePeriod = mSchedulePeriod; 381 initStartDate(); 382 383 log.debug("Schedule initial call to: " + mStartDate + ", remaining repetitions: " + mRemainingRepetitions); 384 mNotificationID = mTimer.addNotification( 385 "Schedule", "Scheduler Notification", 386 null, mStartDate, 388 new Long (mActualSchedulePeriod), 389 mRemainingRepetitions < 0 ? new Long (0) : new Long (mRemainingRepetitions), 390 Boolean.valueOf(mFixedRate) 391 ); 392 mListener = mUseMBean ? new MBeanListener() : new PojoScheduler(); 393 mTimerEmitter.addNotificationListener( 394 mListener, 395 new ScheduleManager.IdNotificationFilter(mNotificationID), 396 null 397 ); 398 mScheduleIsStarted = true; 399 mIsRestartPending = false; 400 } 401 402 406 public void stopSchedule() 407 { 408 stopSchedule(true); 409 } 410 411 421 public void stopSchedule(boolean pDoItNow) 422 { 423 log.debug("stopSchedule(" + pDoItNow + ")"); 424 try 425 { 426 if (mNotificationID < 0) 427 { 428 mScheduleIsStarted = false; 429 mWaitForNextCallToStop = false; 430 return; 431 } 432 if (pDoItNow) 433 { 434 log.debug("stopSchedule(), removing schedule id: " + mNotificationID); 435 mWaitForNextCallToStop = false; 436 if (mListener != null) 437 { 438 mTimerEmitter.removeNotificationListener(mListener); 439 try 440 { 441 mTimer.removeNotification(mNotificationID); 442 } 443 catch (InstanceNotFoundException e) 444 { 445 log.trace(e); 446 } 447 mListener = null; 448 } 449 mNotificationID = -1; 450 mScheduleIsStarted = false; 451 } 452 else 453 { 454 mWaitForNextCallToStop = true; 455 } 456 } 457 catch (Exception e) 458 { 459 log.error("stopSchedule failed", e); 460 } 461 } 462 463 468 public void restartSchedule() 469 { 470 stopSchedule(); 471 startSchedule(); 472 } 473 474 480 public String getSchedulableClass() 481 { 482 if (mSchedulableClass == null) 483 { 484 return null; 485 } 486 return mSchedulableClass.getName(); 487 } 488 489 502 public void setSchedulableClass(String pSchedulableClass) 503 throws InvalidParameterException  504 { 505 if (pSchedulableClass == null || pSchedulableClass.equals("")) 506 { 507 throw new InvalidParameterException ("Schedulable Class cannot be empty or undefined"); 508 } 509 try 510 { 511 ClassLoader loader = TCLActions.getContextClassLoader(); 513 mSchedulableClass = loader.loadClass(pSchedulableClass); 514 if (!isSchedulable(mSchedulableClass)) 516 { 517 String msg = "Given class " + pSchedulableClass + " is not instance of Schedulable"; 518 StringBuffer info = new StringBuffer (msg); 519 info.append("\nThe SchedulableClass info:"); 520 Classes.displayClassInfo(mSchedulableClass, info); 521 info.append("\nSchedulable.class info:"); 522 Classes.displayClassInfo(Schedulable.class, info); 523 log.debug(info.toString()); 524 throw new InvalidParameterException (msg); 525 } 526 } 527 catch (ClassNotFoundException e) 528 { 529 log.info("Failed to find: "+pSchedulableClass, e); 530 throw new InvalidParameterException ( 531 "Given class " + pSchedulableClass + " is not not found" 532 ); 533 } 534 mIsRestartPending = true; 535 mUseMBean = false; 536 } 537 538 545 public String getSchedulableArguments() 546 { 547 return mSchedulableArguments; 548 } 549 550 561 public void setSchedulableArguments(String pArgumentList) 562 { 563 if (pArgumentList == null || pArgumentList.equals("")) 564 { 565 mSchedulableArgumentList = new String [0]; 566 } 567 else 568 { 569 StringTokenizer lTokenizer = new StringTokenizer (pArgumentList, ","); 570 Vector lList = new Vector (); 571 while (lTokenizer.hasMoreTokens()) 572 { 573 String lToken = lTokenizer.nextToken().trim(); 574 if (lToken.equals("")) 575 { 576 lList.add("null"); 577 } 578 else 579 { 580 lList.add(lToken); 581 } 582 } 583 mSchedulableArgumentList = (String []) lList.toArray(new String [0]); 584 } 585 mSchedulableArguments = pArgumentList; 586 mIsRestartPending = true; 587 } 588 589 595 public String getSchedulableArgumentTypes() 596 { 597 return mSchedulableArgumentTypes; 598 } 599 600 614 public void setSchedulableArgumentTypes(String pTypeList) 615 throws InvalidParameterException  616 { 617 if (pTypeList == null || pTypeList.equals("")) 618 { 619 mSchedulableArgumentTypeList = new Class [0]; 620 } 621 else 622 { 623 StringTokenizer lTokenizer = new StringTokenizer (pTypeList, ","); 624 Vector lList = new Vector (); 625 while (lTokenizer.hasMoreTokens()) 626 { 627 String lToken = lTokenizer.nextToken().trim(); 628 Class lClass = null; 630 if (lToken.equals("short")) 631 { 632 lClass = Short.TYPE; 633 } 634 else if (lToken.equals("int")) 635 { 636 lClass = Integer.TYPE; 637 } 638 else if (lToken.equals("long")) 639 { 640 lClass = Long.TYPE; 641 } 642 else if (lToken.equals("byte")) 643 { 644 lClass = Byte.TYPE; 645 } 646 else if (lToken.equals("char")) 647 { 648 lClass = Character.TYPE; 649 } 650 else if (lToken.equals("float")) 651 { 652 lClass = Float.TYPE; 653 } 654 else if (lToken.equals("double")) 655 { 656 lClass = Double.TYPE; 657 } 658 else if (lToken.equals("boolean")) 659 { 660 lClass = Boolean.TYPE; 661 } 662 if (lClass == null) 663 { 664 try 665 { 666 ClassLoader loader = TCLActions.getContextClassLoader(); 668 lClass = loader.loadClass(lToken); 669 } 670 catch (ClassNotFoundException cnfe) 671 { 672 throw new InvalidParameterException ( 673 "The argument type: " + lToken + " is not a valid class or could not be found" 674 ); 675 } 676 } 677 lList.add(lClass); 678 } 679 mSchedulableArgumentTypeList = (Class []) lList.toArray(new Class [0]); 680 } 681 mSchedulableArgumentTypes = pTypeList; 682 mIsRestartPending = true; 683 } 684 685 690 public String getSchedulableMBean() 691 { 692 return mSchedulableMBean == null ? 693 null : 694 mSchedulableMBean.toString(); 695 } 696 697 714 public void setSchedulableMBean(String pSchedulableMBean) 715 throws InvalidParameterException  716 { 717 if (pSchedulableMBean == null) 718 { 719 throw new InvalidParameterException ("Schedulable MBean must be specified"); 720 } 721 try 722 { 723 mSchedulableMBean = new ObjectName (pSchedulableMBean); 724 mUseMBean = true; 725 } 726 catch (MalformedObjectNameException e) 727 { 728 throw new InvalidParameterException ("Schedulable MBean name invalid " + pSchedulableMBean); 729 } 730 } 731 732 735 public String getSchedulableMBeanMethod() 736 { 737 return mSchedulableMBeanMethod; 738 } 739 740 771 public void setSchedulableMBeanMethod(String pSchedulableMBeanMethod) 772 throws InvalidParameterException  773 { 774 if (pSchedulableMBeanMethod == null) 775 { 776 mSchedulableMBeanMethod = null; 777 return; 778 } 779 int lIndex = pSchedulableMBeanMethod.indexOf('('); 780 String lMethodName; 781 if (lIndex == -1) 782 { 783 lMethodName = pSchedulableMBeanMethod.trim(); 784 mSchedulableMBeanArguments = new int[0]; 785 mSchedulableMBeanArgumentTypes = new String [0]; 786 } 787 else 788 { 789 lMethodName = pSchedulableMBeanMethod.substring(0, lIndex).trim(); 790 } 791 if (lMethodName.equals("")) 792 { 793 lMethodName = "perform"; 794 } 795 if (lIndex >= 0) 796 { 797 int lIndex2 = pSchedulableMBeanMethod.indexOf(')'); 798 if (lIndex2 < lIndex) 799 { 800 throw new InvalidParameterException ("Schedulable MBean Method: closing bracket must be after opening bracket"); 801 } 802 if (lIndex2 < pSchedulableMBeanMethod.length() - 1) 803 { 804 String lRest = pSchedulableMBeanMethod.substring(lIndex2 + 1).trim(); 805 if (lRest.length() > 0) 806 { 807 throw new InvalidParameterException ("Schedulable MBean Method: nothing should be after closing bracket"); 808 } 809 } 810 String lArguments = pSchedulableMBeanMethod.substring(lIndex + 1, lIndex2).trim(); 811 if (lArguments.equals("")) 812 { 813 mSchedulableMBeanArguments = new int[0]; 814 mSchedulableMBeanArgumentTypes = new String [0]; 815 } 816 else 817 { 818 StringTokenizer lTokenizer = new StringTokenizer (lArguments, ", "); 819 mSchedulableMBeanArguments = new int[lTokenizer.countTokens()]; 820 mSchedulableMBeanArgumentTypes = new String  |