1 23 package com.sun.ejb.containers; 24 25 import java.util.Date ; 26 import java.util.Collection ; 27 import java.util.Set ; 28 import java.util.HashSet ; 29 import java.util.ArrayList ; 30 import java.util.Iterator ; 31 import java.util.HashMap ; 32 import java.util.Map ; 33 import java.util.Map.Entry; 34 import java.util.TimerTask ; 35 import java.util.Properties ; 36 import java.util.logging.Logger ; 37 import java.util.logging.Level ; 38 39 import java.text.DateFormat ; 40 import java.text.SimpleDateFormat ; 41 42 import java.io.File ; 43 import java.io.FileWriter ; 44 import java.io.FileReader ; 45 import java.io.BufferedWriter ; 46 import java.io.BufferedReader ; 47 import java.io.PrintWriter ; 48 import java.io.File ; 49 import java.io.Serializable ; 50 51 import com.sun.logging.LogDomains; 52 import com.sun.ejb.ContainerFactory; 53 54 import javax.ejb.Timer ; 55 import javax.ejb.EJBException ; 56 import javax.ejb.FinderException ; 57 import javax.ejb.CreateException ; 58 import javax.ejb.RemoveException ; 59 60 import com.sun.enterprise.admin.monitor.callflow.Agent; 61 import com.sun.enterprise.admin.monitor.callflow.RequestType; 62 import com.sun.enterprise.Switch; 63 import com.sun.enterprise.deployment.*; 64 65 import com.sun.enterprise.server.ApplicationServer; 66 import com.sun.enterprise.instance.InstanceEnvironment; 67 import com.sun.enterprise.instance.AppsManager; 68 import com.sun.enterprise.instance.ServerManager; 69 70 import com.sun.enterprise.config.serverbeans.ServerBeansFactory; 71 import com.sun.enterprise.server.ServerContext; 72 import com.sun.enterprise.config.serverbeans.EjbContainer; 73 import com.sun.enterprise.config.serverbeans.EjbTimerService; 74 75 import javax.transaction.TransactionManager ; 76 77 86 public class EJBTimerService 87 implements com.sun.ejb.spi.distributed.DistributedEJBTimerService 88 { 89 private long nextTimerIdMillis_ = 0; 90 private long nextTimerIdCounter_ = 0; 91 private String serverName_; 92 private String domainName_; 93 94 98 private static final String TIMER_ID_SEP = "@@"; 100 101 private String ownerIdOfThisServer_; 103 104 private TimerCache timerCache_; 106 107 private TimerLocalHome timerLocalHome_; 108 private TimerMigrationLocalHome timerMigrationLocalHome_; 109 private boolean shutdown_; 110 111 private long totalTimedObjectsInitialized_ = 0; 119 120 private static Logger logger = LogDomains.getLogger(LogDomains.EJB_LOGGER); 121 122 124 private static final long MINIMUM_DELIVERY_INTERVAL = 7000; 125 private static final int MAX_REDELIVERIES = 1; 126 private static final long REDELIVERY_INTERVAL = 5000; 127 128 private String appID; 129 130 private long minimumDeliveryInterval_ = MINIMUM_DELIVERY_INTERVAL; 133 134 private long maxRedeliveries_ = MAX_REDELIVERIES; 137 138 private long redeliveryInterval_ = REDELIVERY_INTERVAL; 140 141 private static final String TIMER_SERVICE_FILE = 142 "__timer_service_shutdown__.dat"; 143 private static final String TIMER_SERVICE_DOWNTIME_FORMAT = 144 "yyyy/MM/dd HH:mm:ss"; 145 146 private boolean performDBReadBeforeTimeout = false; 150 151 private static final String strDBReadBeforeTimeout = 152 "com.sun.ejb.timer.ReadDBBeforeTimeout"; 153 private boolean foundSysPropDBReadBeforeTimeout = false; 154 155 public EJBTimerService(String appID, TimerLocalHome timerLocalHome, 156 TimerMigrationLocalHome timerMigrationLocalHome) 157 { 158 timerLocalHome_ = timerLocalHome; 159 timerMigrationLocalHome_ = timerMigrationLocalHome; 160 timerCache_ = new TimerCache(); 161 shutdown_ = false; 162 this.appID = appID; 163 164 domainName_ = ServerManager.instance().getDomainName(); 165 InstanceEnvironment server = 166 ApplicationServer.getServerContext().getInstanceEnvironment(); 167 serverName_ = server.getName(); 168 169 initProperties(); 170 } 171 172 private void initProperties() { 173 174 try { 175 176 ServerContext sc = ApplicationServer.getServerContext(); 178 EjbContainer ejbc = ServerBeansFactory. 179 getConfigBean(sc.getConfigContext()).getEjbContainer(); 180 EjbTimerService ejbt = ejbc.getEjbTimerService(); 181 182 if( ejbt != null ) { 183 184 String valString = ejbt.getMinimumDeliveryIntervalInMillis(); 185 long val = (valString != null) ? 186 Long.parseLong(valString) : -1; 187 188 if( val > 0 ) { 189 minimumDeliveryInterval_ = val; 190 } 191 192 valString = ejbt.getMaxRedeliveries(); 193 val = (valString != null) ? Long.parseLong(valString) : -1; 194 if( val > 0 ) { 196 maxRedeliveries_ = val; 197 } 198 199 valString = ejbt.getRedeliveryIntervalInternalInMillis(); 200 val = (valString != null) ? Long.parseLong(valString) : -1; 201 if( val > 0 ) { 202 redeliveryInterval_ = val; 203 } 204 205 foundSysPropDBReadBeforeTimeout = 209 getDBReadBeforeTimeoutProperty(); 210 211 setPerformDBReadBeforeTimeout( false ); 215 } 216 217 InstanceEnvironment server = sc.getInstanceEnvironment(); 220 String serverName = server.getName(); 221 ownerIdOfThisServer_ = serverName; 222 223 } catch(Exception e) { 224 logger.log(Level.FINE, "Exception converting timer service " + 225 "domain.xml properties. Defaults will be used instead.", e); 226 } 227 228 logger.log(Level.FINE, "EJB Timer Service properties : " + 229 "min delivery interval = " + minimumDeliveryInterval_ + 230 "\nmax redeliveries = " + maxRedeliveries_ + 231 "\nredelivery interval = " + redeliveryInterval_); 232 } 233 234 synchronized void timedObjectCount() { 235 totalTimedObjectsInitialized_++; 236 } 237 238 242 String getOwnerIdOfThisServer() { 243 return ownerIdOfThisServer_; 244 } 245 246 251 252 255 private EJBException createEJBException( Exception ex ) { 256 EJBException ejbEx = new EJBException (); 257 ejbEx.initCause(ex); 258 return ejbEx; 259 } 260 261 264 public String [] listTimers( String [] serverIds ) { 265 String [] totalTimers = new String [ serverIds.length ]; 266 try { 267 for ( int i = 0; i < serverIds.length; i++ ) { 268 totalTimers[i] = new String ( 269 new Integer ( 270 timerLocalHome_.selectCountAllTimersOwnedBy( 271 serverIds[i] )).toString()); 272 } 273 } catch( Exception ex ) { 274 logger.log( Level.SEVERE, "Exception in listTimers() : " , ex ); 275 276 EJBException ejbEx = createEJBException( ex ); 278 throw ejbEx; 279 } 280 return totalTimers; 281 } 282 283 286 public int migrateTimers(String fromOwnerId) { 287 288 String ownerIdOfThisServer = getOwnerIdOfThisServer(); 289 290 if( fromOwnerId.equals(ownerIdOfThisServer) ) { 291 logger.log(Level.WARNING, "Attempt to migrate timers from " + 295 "an active server instance " + ownerIdOfThisServer); 296 throw new IllegalStateException ("Attempt to migrate timers from " + 297 " an active server instance " + 298 ownerIdOfThisServer); 299 } 300 301 logger.log(Level.INFO, "Beginning timer migration process from " + 302 "owner " + fromOwnerId + " to " + ownerIdOfThisServer); 303 304 TransactionManager tm = Switch.getSwitch().getTransactionManager(); 305 306 Set toRestore = new HashSet (); 307 308 try { 309 310 tm.begin(); 311 312 Set toMigrate = timerMigrationLocalHome_. 315 selectAllTimersOwnedBy(fromOwnerId); 316 317 toRestore = timerLocalHome_. 320 selectAllTimersOwnedBy(fromOwnerId); 321 322 for(Iterator iter = toMigrate.iterator(); iter.hasNext();) { 323 TimerMigrationLocal next = (TimerMigrationLocal) iter.next(); 324 next.setOwnerId(ownerIdOfThisServer); 325 } 326 327 tm.commit(); 328 329 } catch(Exception e) { 330 toRestore = new HashSet (); 335 336 logger.log(Level.FINE, "timer migration error", e); 337 338 try { 339 tm.rollback(); 340 } catch(Exception re) { 341 logger.log(Level.FINE, "timer migration rollback error", re); 342 } 343 344 EJBException ejbEx = createEJBException( e ); 346 throw ejbEx; 347 } 348 349 int totalTimersMigrated = toRestore.size(); 350 351 if( toRestore.size() > 0 ) { 352 353 boolean success = false; 354 try { 355 356 logger.log(Level.INFO, "Timer migration phase 1 complete. " + 357 "Changed ownership of " + toRestore.size() + 358 " timers. Now reactivating timers..."); 359 360 tm.begin(); 361 362 _restoreTimers(toRestore); 363 success = true; 364 365 } catch(Exception e) { 366 367 logger.log(Level.FINE, "timer restoration error", e); 368 369 EJBException ejbEx = createEJBException( e ); 371 throw ejbEx; 372 373 } finally { 374 try { 377 tm.commit(); 378 } catch(Exception re) { 379 logger.log(Level.FINE, "timer migration error", re); 380 381 if( success ) { 382 EJBException ejbEx = createEJBException( re ); 385 throw ejbEx; 386 } 387 } 388 } 389 } else { 390 logger.log(Level.INFO, fromOwnerId + " has 0 timers in need " + 391 "of migration"); 392 } 393 394 return totalTimersMigrated; 395 396 } 398 public void setPerformDBReadBeforeTimeout( boolean defaultDBReadValue ) { 399 400 if ( !foundSysPropDBReadBeforeTimeout ) { 403 performDBReadBeforeTimeout = defaultDBReadValue; 404 405 if( logger.isLoggable(Level.FINE) ) { 406 logger.log(Level.FINE, "EJB Timer Service property : " + 407 "\nread DB before timeout delivery = " + 408 performDBReadBeforeTimeout); 409 } 410 411 } 412 } 413 414 425 private boolean getDBReadBeforeTimeoutProperty() { 426 427 boolean result = false; 428 try{ 429 Properties props = System.getProperties(); 430 String str=props.getProperty( strDBReadBeforeTimeout ); 431 if( null != str) { 432 str = str.toLowerCase(); 433 performDBReadBeforeTimeout = Boolean.valueOf(str).booleanValue(); 434 435 if( logger.isLoggable(Level.FINE) ) { 436 logger.log(Level.FINE, "EJB Timer Service property : " + 437 "\nread DB before timeout delivery = " + 438 performDBReadBeforeTimeout); 439 } 440 441 result = true; 442 } 443 } catch(Exception e) { 444 logger.log(Level.INFO, 445 "ContainerFactoryImpl.getDebugMonitoringDetails(), " + 446 " Exception when trying to " + 447 "get the System properties - ", e); 448 } 449 return result; 450 } 451 452 456 void restoreTimers() throws Exception { 457 458 if( totalTimedObjectsInitialized_ == 0 ) { 461 return; 462 } 463 464 TransactionManager tm = Switch.getSwitch().getTransactionManager(); 465 Set allActiveTimers = new HashSet (); 466 try { 467 tm.begin(); 471 472 allActiveTimers = 478 timerLocalHome_.selectAllActiveTimersOwnedByThisServer(); 479 480 _restoreTimers(allActiveTimers); 481 } catch(Exception e) { 482 483 ContainerFactoryImpl cf = (ContainerFactoryImpl) 485 Switch.getSwitch().getContainerFactory(); 486 cf.setEJBTimerService(null); 487 488 logger.log(Level.WARNING, "ejb.timer_service_init_error", e); 489 490 return; 493 494 } finally { 495 try { 497 tm.commit(); 498 } catch(Exception e) { 499 logger.log(Level.WARNING, "ejb.timer_service_init_error", e); 500 } 501 } 502 } 503 504 508 private void _restoreTimers(Set timersEligibleForRestoration) { 509 510 514 Map timersToRestore = new HashMap (); 515 516 for(Iterator iter = timersEligibleForRestoration.iterator(); 517 iter.hasNext();) { 518 519 TimerLocal next = (TimerLocal) iter.next(); 520 long containerId = next.getContainerId(); 521 522 BaseContainer container = getContainer(containerId); 524 if( container != null ) { 525 526 TimerPrimaryKey timerId = (TimerPrimaryKey)next.getPrimaryKey(); 527 Date initialExpiration = next.getInitialExpiration(); 528 529 531 Object timedObjectPrimaryKey = null; 535 if( container instanceof EntityContainer ) { 536 timedObjectPrimaryKey = next.getTimedObjectPrimaryKey(); 537 } 538 539 RuntimeTimerState timerState = new RuntimeTimerState 540 (timerId, initialExpiration, 541 next.getIntervalDuration(), container, 542 timedObjectPrimaryKey); 543 544 timerCache_.addTimer(timerId, timerState); 545 546 Date expirationTime = initialExpiration; 552 Date now = new Date (); 553 554 if( timerState.isPeriodic() ) { 555 Date lastExpiration = next.getLastExpiration(); 559 560 568 if( (lastExpiration == null) && 569 now.after(initialExpiration) ) { 570 571 logger.log(Level.INFO, 573 "Rescheduling missed expiration for " + 574 "periodic timer " + 575 timerState + ". Timer expirations should " + 576 " have been delivered starting at " + 577 initialExpiration); 578 579 583 } else if ( (lastExpiration != null) && 584 ( (now.getTime() - lastExpiration.getTime() 585 > next.getIntervalDuration()) ) ) { 586 587 logger.log(Level.INFO, 588 "Rescheduling missed expiration for " + 589 "periodic timer " + 590 timerState + ". Last timer expiration " + 591 "occurred at " + lastExpiration); 592 593 596 600 } else { 601 602 expirationTime = 606 calcNextFixedRateExpiration(timerState); 607 } 608 609 } else { 611 if( now.after(initialExpiration) ) { 612 logger.log(Level.INFO, 613 "Rescheduling missed expiration for " + 614 "single-action timer " + 615 timerState + ". Timer expiration should " + 616 " have been delivered at " + 617 initialExpiration); 618 } 619 } 620 621 timersToRestore.put(timerState, expirationTime); 622 623 } else { 624 try { 626 next.remove(); 627 } catch(RemoveException e) { 628 logger.log(Level.FINE, 629 "Removing timer " + next.getPrimaryKey() + 630 " for unknown container " + containerId, e); 631 } 632 } 633 } 635 for(Iterator entries = timersToRestore.entrySet().iterator(); 636 entries.hasNext(); ) { 637 Map.Entry next = (Map.Entry ) entries.next(); 638 RuntimeTimerState nextTimer = (RuntimeTimerState) next.getKey(); 639 TimerPrimaryKey timerId = nextTimer.getTimerId(); 640 Date expiration = (Date ) next.getValue(); 641 scheduleTask(timerId, expiration); 642 logger.log(Level.FINE, 643 "EJBTimerService.restoreTimers(), scheduling timer " + 644 nextTimer); 645 } 646 647 logger.log(Level.FINE, "DONE EJBTimerService.restoreTimers()"); 648 } 649 650 void shutdown() { 651 shutdown_ = true; 653 } 654 655 662 void cancelEntityBeanTimers(long containerId, Object primaryKey) { 663 try { 664 Collection timers = getTimers(containerId, primaryKey); 668 if( logger.isLoggable(Level.FINE) ) { 669 if( timers.isEmpty() ) { 670 logger.log(Level.FINE, "0 cancelEntityBeanTimers for " + 671 containerId + ", " + primaryKey); 672 } 673 } 674 for(Iterator iter = timers.iterator(); iter.hasNext();) { 675 TimerLocal next = (TimerLocal) iter.next(); 676 try { 677 cancelTimer(next); 678 } catch(Exception e) { 679 logger.log(Level.WARNING, "ejb.cancel_entity_timer", 680 new Object [] { next.getPrimaryKey() }); 681 logger.log(Level.WARNING, "", e); 682 } 683 } 684 } catch(Exception e) { 685 logger.log(Level.WARNING, "ejb.cancel_entity_timers", 686 new Object [] { new Long (containerId), primaryKey }); 687 logger.log(Level.WARNING, "", e); 688 } 689 } 690 691 700 void destroyTimers(long containerId) { 701 Set timers = null; 702 703 TransactionManager tm = Switch.getSwitch().getTransactionManager(); 704 705 try { 706 707 tm.begin(); 711 712 timers = timerLocalHome_.selectTimersByContainer(containerId); 716 717 for(Iterator iter = timers.iterator(); iter.hasNext();) { 718 TimerLocal next = (TimerLocal) iter.next(); 719 TimerPrimaryKey nextTimerId = null; 720 RuntimeTimerState nextTimerState = null; 721 try { 722 nextTimerId = (TimerPrimaryKey) next.getPrimaryKey(); 723 nextTimerState = getTimerState(nextTimerId); 724 if( nextTimerState != null ) { 725 synchronized(nextTimerState) { 726 if( nextTimerState.isScheduled() ) { 727 EJBTimerTask timerTask = 728 nextTimerState.getCurrentTimerTask(); 729 timerTask.cancel(); 730 } 731 } 732 } 733 next.remove(); 734 } catch(Exception e) { 735 logger.log(Level.WARNING, "ejb.destroy_timer_error", 736 new Object [] { nextTimerId }); 737 logger.log(Level.WARNING, "", e); 738 } finally { 739 if( nextTimerState != null ) { 740 timerCache_.removeTimer(nextTimerId); 741 } 742 } 743 } 744 745 } catch(Exception ex) { 746 logger.log(Level.WARNING, "destroy_timers_error", 747 new Object [] { new Long (containerId) }); 748 logger.log(Level.WARNING, "", ex); 749 return; 750 } finally { 751 try { 752 tm.commit(); 753 } catch(Exception e) { 754 logger.log(Level.WARNING, "destroy_timers_error", 757 new Object [] { new Long (containerId) }); 758 logger.log(Level.WARNING, "", e); 759 } 760 } 761 762 return; 763 } 764 765 void rescheduleTask(TimerPrimaryKey timerId, Date expiration) { 766 scheduleTask(timerId, expiration, true); 767 } 768 769 void scheduleTask(TimerPrimaryKey timerId, Date expiration) { 770 scheduleTask(timerId, expiration, false); 771 } 772 773 void scheduleTask(TimerPrimaryKey timerId, Date expiration, 774 boolean rescheduled) { 775 776 RuntimeTimerState timerState = getTimerState(timerId); 777 778 if( timerState != null ) { 779 synchronized(timerState) { 780 781 Date timerExpiration = expiration; 782 783 if( !rescheduled ) { 784 Date cutoff = new Date (new Date ().getTime() + 791 getMinimumDeliveryInterval()); 792 if( expiration.before(cutoff) ) { 793 timerExpiration = cutoff; 794 } 795 } 796 797 EJBTimerTask timerTask = 798 new EJBTimerTask(timerExpiration, timerId, this); 799 if( logger.isLoggable(Level.FINE) ) { 800 logger.log(Level.FINE, (rescheduled ? "RE-" : "") + 801 "Scheduling " + timerState + 802 " for timeout at " + timerExpiration); 803 } 804 if( rescheduled ) { 805 timerState.rescheduled(timerTask); 806 } else { 807 timerState.scheduled(timerTask); 808 } 809 810 java.util.Timer jdkTimer = Switch.getSwitch().getTimer(); 811 jdkTimer.schedule(timerTask, timerExpiration); 812 } 813 } else { 814 815 logger.log(Level.FINE, "No timer state found for " + 816 (rescheduled ? "RE-schedule" : "schedule") + 817 " request of " + timerId + 818 " for timeout at " + expiration); 819 } 820 } 821 822 823 830 Date cancelTask(TimerPrimaryKey timerId) { 831 832 Date timeout = null; 833 834 RuntimeTimerState timerState = getTimerState(timerId); 835 if( timerState != null ) { 836 synchronized(timerState) { 837 838 if( timerState.isCreated() ) { 839 timeout = timerState.getInitialExpiration(); 840 } else if( timerState.isScheduled() ) { 841 EJBTimerTask timerTask = timerState.getCurrentTimerTask(); 842 timeout = timerTask.getTimeout(); 843 timerTask.cancel(); 844 } 845 timerState.cancelled(); 846 847 } 848 } else { 849 logger.log(Level.FINE, "No timer state found for " + 850 "cancelTask request of " + timerId); 851 } 852 853 return timeout; 854 } 855 856 860 void restoreTaskToDelivered(TimerPrimaryKey timerId) { 861 862 RuntimeTimerState timerState = getTimerState(timerId); 863 if( timerState != null ) { 864 synchronized(timerState) { 865 timerState.restoredToDelivered(); 866 } 867 if( logger.isLoggable(Level.FINE) ) { 868 logger.log(Level.FINE, "Restoring " + timerId + 869 " to delivered state after it was cancelled and " + 870 " rolled back from within its own ejbTimeout method"); 871 } 872 } else { 873 logger.log(Level.FINE, "No timer state found for " + 874 "restoreTaskToDelivered request of " + timerId); 875 } 876 } 877 878 void expungeTimer(TimerPrimaryKey timerId) { 879 expungeTimer(timerId, false); 882 } 883 884 885 private Date calcInitialFixedRateExpiration(long timerServiceWentDownAt, 886 RuntimeTimerState timerState) 887 { 888 if (!timerState.isPeriodic()) { 889 throw new IllegalStateException (); 890 } 891 Date now = new Date (); 892 893 long nowMillis = now.getTime(); 894 long initialExpiration = timerState.getInitialExpiration().getTime(); 895 896 long now2initialDiff = nowMillis - initialExpiration; 897 long count = now2initialDiff / timerState.getIntervalDuration(); 898 long previousExpiration = 899 initialExpiration + (count * timerState.getIntervalDuration()); 900 901 if ((previousExpiration >= timerServiceWentDownAt) 902 && (previousExpiration <= nowMillis)) 903 { 904 logger.log(Level.INFO, "ejb.deliver_missed_timer", 906 new Object [] { timerState.getTimerId(), 907 new Date (previousExpiration) }); 908 return now; 909 } else { 910 return calcNextFixedRateExpiration(timerState); 912 } 913 914 } 915 916 private Date calcNextFixedRateExpiration(RuntimeTimerState timerState) { 917 918 if( !timerState.isPeriodic() ) { 919 throw new IllegalStateException ("Timer " + timerState + " is " + 920 "not a periodic timer"); 921 } 922 923 Date initialExpiration = timerState.getInitialExpiration(); 924 long intervalDuration = timerState.getIntervalDuration(); 925 926 return calcNextFixedRateExpiration(initialExpiration, intervalDuration); 927 } 928 929 private Date calcNextFixedRateExpiration(Date initialExpiration, 930 long intervalDuration) { 931 932 Date now = new Date (); 933 long nowMillis = now.getTime(); 934 935 Date nextExpirationTime = initialExpiration; 937 938 if( now.after(initialExpiration) ) { 939 long timeSinceInitialExpire = 940 (nowMillis - initialExpiration.getTime()); 941 942 long numIntervals = 946 (timeSinceInitialExpire / intervalDuration); 947 948 nextExpirationTime = new Date (initialExpiration.getTime() + 951 ((numIntervals + 1) * intervalDuration)); 952 } 953 954 return nextExpirationTime; 955 } 956 957 962 private void expungeTimer(TimerPrimaryKey timerId, 963 boolean removeTimerBean) { 964 if( removeTimerBean ) { 968 removeTimerBean(timerId); 969 } 970 timerCache_.removeTimer(timerId); 971 } 972 973 977 TimerPrimaryKey createTimer(long containerId, Object timedObjectPrimaryKey, 978 long initialDuration, long intervalDuration, 979 Serializable info) throws CreateException { 980 981 Date now = new Date (); 982 983 Date initialExpiration = new Date (now.getTime() + initialDuration); 984 985 return createTimer(containerId, timedObjectPrimaryKey, 986 initialExpiration, intervalDuration, info); 987 } 988 989 993 TimerPrimaryKey createTimer(long containerId, Object timedObjectPrimaryKey, 994 Date initialExpiration, long intervalDuration, 995 Serializable info) throws CreateException { 996 997 BaseContainer container = getContainer(containerId); 998 if( container == null ) { 999 throw new CreateException ("invalid container id " + containerId + 1000 " in createTimer request"); 1001 } 1002 1003 Class ejbClass = container.getEJBClass(); 1004 if( !container.isTimedObject() ) { 1005 throw new CreateException 1006 ("Attempt to create an EJB Timer from a bean that is " + 1007 "not a Timed Object. EJB class " + ejbClass + 1008 " must implement javax.ejb.TimedObject or " + 1009 " annotation a timeout method with @Timeout"); 1010 } 1011 1012 TimerPrimaryKey timerId = new TimerPrimaryKey(getNextTimerId()); 1013 1014 RuntimeTimerState timerState = 1015 new RuntimeTimerState(timerId, initialExpiration, 1016 intervalDuration, container, 1017 timedObjectPrimaryKey); 1018 1019 synchronized(timerState) { 1020 timerCache_.addTimer(timerId, timerState); 1024 try { 1025 timerLocalHome_.create(timerId.getTimerId(), containerId, 1026 ownerIdOfThisServer_, 1027 timedObjectPrimaryKey, 1028 initialExpiration, intervalDuration, 1029 info); 1030 } catch(Exception e) { 1031 logger.log(Level.SEVERE, "ejb.create_timer_failure", 1032 new Object [] { new Long (containerId), 1033 timedObjectPrimaryKey, 1034 info }); 1035 logger.log(Level.SEVERE, "", e); 1036 timerCache_.removeTimer(timerId); 1038 if( e instanceof CreateException ) { 1039 throw ((CreateException )e); 1040 } else { 1041 EJBException ejbEx = new EJBException (); 1042 ejbEx.initCause(e); 1043 throw ejbEx; 1044 } 1045 } 1046 } 1047 1048 return timerId; 1049 } 1050 1051 1062 private Collection getTimers(long containerId, 1063 Object timedObjectPrimaryKey) 1064 throws FinderException { 1065 1066 1069 1073 Collection activeTimers = 1074 timerLocalHome_.selectActiveTimersByContainer(containerId); 1075 1076 Collection timersForTimedObject = activeTimers; 1077 1078 if( timedObjectPrimaryKey != null ) { 1079 1080 1083 timersForTimedObject = new HashSet (); 1084 1085 for(Iterator iter = activeTimers.iterator(); iter.hasNext();) { 1086 TimerLocal next = (TimerLocal) iter.next(); 1087 1088 Object nextTimedObjectPrimaryKey = 1089 next.getTimedObjectPrimaryKey(); 1090 if( nextTimedObjectPrimaryKey.equals(timedObjectPrimaryKey) ) { 1091 timersForTimedObject.add(next); 1092 } 1093 } 1094 } 1095 1096 return timersForTimedObject; 1097 } 1098 1099 1110 Collection getTimerIds(long containerId, Object timedObjectPrimaryKey) 1111 throws FinderException { 1112 1113 1116 1120 Collection timerIdsForTimedObject = new HashSet (); 1121 1122 if( timedObjectPrimaryKey == null ) { 1123 1124 timerIdsForTimedObject = 1125 timerLocalHome_.selectActiveTimerIdsByContainer(containerId); 1126 1127 } else { 1128 1129 1132 Collection timersForTimedObject = getTimers(containerId, 1133 timedObjectPrimaryKey); 1134 1135 timerIdsForTimedObject = new HashSet (); 1136 1137 for(Iterator iter = timersForTimedObject.iterator(); 1138 iter.hasNext();) { 1139 TimerLocal next = (TimerLocal) iter.next(); 1140 timerIdsForTimedObject.add(next.getPrimaryKey()); 1141 } 1142 } 1143 1144 return timerIdsForTimedObject; 1145 } 1146 1147 1151 ClassLoader getTimerClassLoader(long containerId) { 1152 BaseContainer container = getContainer(containerId); 1153 return (container != null) ? container.getClassLoader() : null; 1154 } 1155 1156 TimerLocalHome getTimerBeanHome() { 1157 return timerLocalHome_; 1158 } 1159 1160 private RuntimeTimerState getTimerState(TimerPrimaryKey timerId) { 1161 return timerCache_.getTimerState(timerId); 1162 } 1163 1164 private TimerLocal findTimer(TimerPrimaryKey timerId) 1165 throws FinderException { 1166 return timerLocalHome_.findByPrimaryKey(timerId); 1167 } 1168 1169 1173 void cancelTimer(TimerPrimaryKey timerId) 1174 throws FinderException , Exception { 1175 1176 1180 TimerLocal timerBean = findTimer(timerId); 1183 cancelTimer(timerBean); 1184 1185 } 1186 1187 private void cancelTimer(TimerLocal timerBean) throws Exception { 1188 if( timerBean.isCancelled() ) { 1189 } else { 1191 timerBean.cancel(); 1192 timerBean.remove(); 1193 } 1194 } 1195 1196 1207 Date getNextTimeout(TimerPrimaryKey timerId) throws FinderException { 1208 1209 1213 TimerLocal timerBean = findTimer(timerId); 1214 if( timerBean.isCancelled() ) { 1215 throw new FinderException ("timer " + timerId + " does not exist"); 1217 } 1218 1219 Date initialExpiration = timerBean.getInitialExpiration(); 1220 1221 Date nextTimeout = timerBean.repeats() ? 1222 calcNextFixedRateExpiration(initialExpiration, 1223 timerBean.getIntervalDuration()) : 1224 initialExpiration; 1225 1226 return nextTimeout; 1227 } 1228 1229 Serializable getInfo(TimerPrimaryKey timerId) throws FinderException { 1230 1231 1235 TimerLocal timerBean = findTimer(timerId); 1236 if( timerBean.isCancelled() ) { 1237 throw new FinderException ("timer " + timerId + " does not exist"); 1239 } 1240 1241 return timerBean.getInfo(); 1242 } 1243 1244 boolean timerExists(TimerPrimaryKey timerId) { 1245 boolean exists = false; 1246 1247 1251 try { 1252 TimerLocal timerBean = findTimer(timerId); 1253 exists = timerBean.isActive(); 1255 } catch(FinderException fe) { 1256 exists = false; 1257 } 1258 1259 return exists; 1260 } 1261 1262 private void removeTimerBean(TimerPrimaryKey timerId) { 1263 try { 1264 TimerLocal timerBean = findTimer(timerId); 1265 timerBean.remove(); 1266 } catch(Throwable t) { 1267 logger.log(Level.WARNING, "ejb.remove_timer_failure", 1268 new Object [] { timerId }); 1269 logger.log(Level.WARNING, "", t); 1270 } 1271 } 1272 1273 private BaseContainer getContainer(long containerId) { 1274 ContainerFactory cf = Switch.getSwitch().getContainerFactory(); 1275 return (BaseContainer) cf.getContainer(containerId); 1276 } 1277 1278 1281 private void deliverTimeout(TimerPrimaryKey timerId) { 1282 1283 if( logger.isLoggable(Level.FINE) ) { 1284 logger.log(Level.FINE, "EJBTimerService.deliverTimeout(): work " 1285 + 1286 "thread is processing work for timerId = " + timerId); 1287 } 1288 1289 if( shutdown_ ) { 1290 if( logger.isLoggable(Level.FINE) ) { 1291 logger.log(Level.FINE, "Cancelling timeout for " + timerId + 1292 " due to server shutdown. Expiration " + 1293 " will occur when server is restarted."); 1294 } 1295 return; 1296 } 1297 1298 RuntimeTimerState timerState = getTimerState(timerId); 1299 1300 1306 if( timerState == null ) { 1307 logger.log(Level.FINE, "Timer state is NULL for timer " + timerId + 1308 " in deliverTimeout"); 1309 return; 1310 } 1311 1312 BaseContainer container = getContainer(timerState.getContainerId()); 1313 1314 synchronized(timerState) { 1315 if( container == null ) { 1316 logger.log(Level.FINE, "Unknown container for timer " + 1317 timerId + " in deliverTimeout. Expunging timer."); 1318 expungeTimer(timerId, true); 1319 return; 1320 } else if ( !timerState.isBeingDelivered() ) { 1321 logger.log(Level.FINE, "Timer state = " + 1322 timerState.stateToString() + 1323 "for timer " + timerId + " before callEJBTimeout"); 1324 return; 1325 } else { 1326 if( logger.isLoggable(Level.FINE) ) { 1327 logger.log(Level.FINE, "Calling ejbTimeout for timer " + 1328 timerState); 1329 } 1330 } 1331 } 1332 1333 try { 1334 1335 Switch.getSwitch().getCallFlowAgent(). 1336 requestStart(RequestType.TIMER_EJB); 1337 container.onEnteringContainer(); 1338 if( performDBReadBeforeTimeout) { 1344 1345 if( logger.isLoggable(Level.FINE) ) { 1346 logger.log(Level.FINE, "For Timer :" + timerId + 1347 ": check the database to ensure that the timer is still " + 1348 " valid, before delivering the ejbTimeout call" ); 1349 } 1350 1351 if( ! checkForTimerValidity(timerId) ) { 1352 return; 1360 } 1361 } 1362 1363 boolean redeliver = container.callEJBTimeout(timerState, this); 1388 1389 if( shutdown_ ) { 1390 if( logger.isLoggable(Level.FINE) ) { 1393 logger.log(Level.FINE, "Cancelling timeout for " + timerId 1394 + 1395 " due to server shutdown. Expiration will " + 1396 " occur on server restart"); 1397 } 1398 return; 1399 } 1400 1401 1404 timerState = getTimerState(timerId); 1405 1406 if( timerState == null ) { 1407 logger.log(Level.FINE, "Timer no longer exists for " + 1410 timerId + " after callEJBTimeout"); 1411 return; 1412 } 1413 1414 1415 synchronized(timerState) { 1416 Date now = new Date (); 1417 if( timerState.isCancelled() ) { 1418 } else if( redeliver ) { 1420 if( timerState.getNumFailedDeliveries() < 1421 getMaxRedeliveries() ) { 1422 Date redeliveryTimeout = new Date 1423 (now.getTime() + getRedeliveryInterval()); 1424 if( logger.isLoggable(Level.FINE) ) { 1425 logger.log(Level.FINE,"Redelivering " + timerState); 1426 } 1427 rescheduleTask(timerId, redeliveryTimeout); 1428 } else { 1429 int numDeliv = timerState.getNumFailedDeliveries() + 1; 1430 logger.log(Level.INFO, 1431 "ejb.timer_exceeded_max_deliveries", 1432 new Object [] { timerState.toString(), 1433 new Integer (numDeliv)}); 1434 expungeTimer(timerId, true); 1435 } 1436 } else if( timerState.isPeriodic() ) { 1437 1438 1442 Date expiration = calcNextFixedRateExpiration(timerState); 1443 scheduleTask(timerId, expiration); 1444 } else { 1445 1446 } 1451 } 1452 1453 } catch(Exception e) { 1454 logger.log(Level.FINE, "callEJBTimeout threw exception " + 1455 "for timer id " + timerId , e); 1456 expungeTimer(timerId, true); 1457 } finally { 1458 container.onLeavingContainer(); 1459 Switch.getSwitch().getCallFlowAgent().requestEnd(); 1460 } 1461 } 1462 1463 1472 boolean postEjbTimeout(TimerPrimaryKey timerId) { 1473 1474 boolean success = true; 1475 1476 if( shutdown_ ) { 1477 return success; 1480 } 1481 1482 1485 RuntimeTimerState timerState = getTimerState(timerId); 1486 1487 if( timerState != null ) { 1488 1489 BaseContainer container = getContainer(timerState.getContainerId()); 1492 container.incrementDeliveredTimedObject(); 1493 1494 synchronized(timerState) { 1495 1496 if( timerState.isCancelled() ) { 1497 } else { 1499 1500 try { 1501 TimerLocal timerBean = getValidTimerFromDB( timerId ); 1502 if( null == timerBean ) { 1503 return false; 1504 } 1505 1506 if( timerState.isPeriodic() ) { 1507 Date now = new Date (); 1508 timerBean.setLastExpiration(now); 1509 1510 if( logger.isLoggable(Level.FINE) ) { 1515 logger.log(Level.FINE, 1516 "Setting last expiration " + 1517 " for periodic timer " + timerState + 1518 " to " + now); 1519 } 1520 1521 } else { 1522 1523 if( logger.isLoggable(Level.FINE) ) { 1524 logger.log(Level.FINE, "Single-action timer " + 1525 timerState + " was successfully delivered. " 1526 + " Removing..."); 1527 } 1528 1529 cancelTimer(timerBean); 1531 } 1532 } catch(Exception e) { 1533 1534 logger.log(Level.WARNING, "Error in post-ejbTimeout " + 1536 "timer processing for " + timerState, e); 1537 success = false; 1538 } 1539 } 1540 } 1541 } 1542 1543 return success; 1544 } 1545 1546 1558 private boolean checkForTimerValidity(TimerPrimaryKey timerId) { 1559 1560 boolean result = true; 1561 1562 TimerLocal timerBean = getValidTimerFromDB( timerId ); 1563 if( null == timerBean) { 1564 result = false; 1565 } 1566 1567 return result; 1568 } 1569 1570 private TimerLocal getValidTimerFromDB(TimerPrimaryKey timerId) { 1571 1572 boolean result = true; 1573 TimerLocal timerBean = null; 1574 1575 try { 1576 1577 timerBean = findTimer(timerId); 1578 1579 if( ! ( timerBean.getOwnerId().equals( 1584 ownerIdOfThisServer_) ) ) { 1585 logger.log(Level.WARNING, 1586 "The timer (" + timerId + ") is not owned by " + 1587 "server (" + ownerIdOfThisServer_ + ") that " + 1588 "initiated the ejbTimeout. This timer is now " + 1589 "owned by (" + timerBean.getOwnerId() + "). \n" + 1590 "Hence delete the timer from " + 1591 ownerIdOfThisServer_ + "'s cache."); 1592 1593 result = false; 1594 } 1595 1596 } catch( FinderException fex ) { 1597 if( logger.isLoggable(Level.FINE) ) { 1599 logger.log(Level.FINE, "Timer :" + timerId + 1600 ": has been cancelled by another server instance. " + 1601 "Expunging the timer from " + ownerIdOfThisServer_ + 1602 "'s cache."); 1603 } 1604 1605 result = false; 1606 1607 } finally { 1608 if( !result ) { 1609 expungeTimer(timerId, false); 1613 timerBean = null; 1614 } else { 1615 if( logger.isLoggable(Level.FINE) ) { 1616 logger.log(Level.FINE, 1617 "The Timer :" + timerId + 1618 ": is a valid timer for the server (" + 1619 ownerIdOfThisServer_ + ")"); 1620 } 1621 } 1622 } 1623 1624 return timerBean; 1625 } 1626 1627 1633 1634 void taskExpired(TimerPrimaryKey timerId) { 1635 RuntimeTimerState timerState = getTimerState(timerId); 1636 1637 if( timerState != null ) { 1638 synchronized(timerState) { 1639 if( timerState.isScheduled() ) { 1640 timerState.delivered(); 1641 1642 if( logger.isLoggable(Level.FINE) ) { 1643 logger.log(Level.FINE, 1644 "Adding work pool task for timer " + timerId); 1645 } 1646 1647 TaskExpiredWork work = new TaskExpiredWork(this, timerId); 1648 com.sun.ejb.containers.util.ContainerWorkPool.addLast(work); 1649 } else { 1650 logger.log(Level.FINE, "Timer " + timerId + 1651 " is not in scheduled state. Current state = " 1652 + timerState.stateToString()); 1653 } 1654 } 1655 } else { 1656 logger.log(Level.FINE, "null timer state for timer id " + timerId); 1657 } 1658 1659 return; 1660 } 1661 1662 1667 private synchronized String getNextTimerId() { 1668 1669 if( nextTimerIdCounter_ <= 0 ) { 1670 nextTimerIdMillis_ = System.currentTimeMillis(); 1671 nextTimerIdCounter_ = 1; 1672 } else { 1673 nextTimerIdCounter_++; 1674 } 1675 1676 1678 return new String (nextTimerIdCounter_ + 1679 TIMER_ID_SEP + nextTimerIdMillis_ + 1680 TIMER_ID_SEP + serverName_ + 1681 TIMER_ID_SEP + domainName_); 1682 } 1683 1684 private long getMinimumDeliveryInterval() { 1688 return minimumDeliveryInterval_; 1689 } 1690 1691 private long getMaxRedeliveries() { 1692 return maxRedeliveries_; 1693 } 1694 1695 private long getRedeliveryInterval() { 1696 return redeliveryInterval_; 1697 } 1698 1699 private class TimerCache { 1709 1710 private Map timers_; 1712 1713 1725 private Map containerTimers_; 1726 1727 public TimerCache() { 1728 timers_ = new HashMap (); 1731 containerTimers_ = new HashMap (); 1732 } 1733 1734 public synchronized void addTimer(TimerPrimaryKey timerId, 1735 RuntimeTimerState timerState) { 1736 if( logger.isLoggable(Level.FINE) ) { 1737 logger.log(Level.FINE, "Adding timer " + timerState); 1738 } 1739 1740 timers_.put(timerId, timerState); 1741 1742 Long containerId = new Long (timerState.getContainerId()); 1743 1744 Object containerInfo = containerTimers_.get(containerId); 1745 1746 if( timerState.timedObjectIsEntity() ) { 1747 Collection entityBeans; 1748 if( containerInfo == null ) { 1749 entityBeans = new ArrayList (); 1753 containerTimers_.put(containerId, entityBeans); 1754 } else { 1755 entityBeans = (Collection ) containerInfo; 1756 } 1757 entityBeans.add(timerState.getTimedObjectPrimaryKey()); 1758 } else { 1759 Long timerCount = (containerInfo == null) ? new Long (1) : 1760 new Long (((Long ) containerInfo).longValue() + 1); 1761 containerTimers_.put(containerId, timerCount); 1762 } 1763 1764 } 1765 1766 1771 public synchronized void removeTimer(TimerPrimaryKey timerId) { 1772 if( logger.isLoggable(Level.FINE) ) { 1773 logger.log(Level.FINE, "Removing timer " + timerId); 1774 } 1775 1776 RuntimeTimerState timerState = (RuntimeTimerState) 1777 timers_.remove(timerId); 1778 1779 if( timerState == null) { 1780 return; 1781 } 1782 1783 Long containerId = new Long (timerState.getContainerId()); 1784 Object containerInfo = containerTimers_.get(containerId); 1785 1786 if( containerInfo != null ) { 1787 if( timerState.timedObjectIsEntity() ) { 1788 Collection entityBeans = (Collection ) containerInfo; 1789 if( entityBeans.size() == 1 ) { 1790 containerTimers_.remove(containerId); 1792 } else { 1793 entityBeans.remove 1797 (timerState.getTimedObjectPrimaryKey()); 1798 } 1799 } else { 1800 long timerCount = ((Long ) containerInfo).longValue(); 1801 if( timerCount == 1 ) { 1802 containerTimers_.remove(containerId); 1804 } else { 1805 Long newCount = new Long (timerCount - 1); 1806 containerTimers_.put(containerId, newCount); 1807 } 1808 } 1809 } 1810 } 1811 1812 public synchronized RuntimeTimerState getTimerState(TimerPrimaryKey 1813 timerId) { 1814 return (RuntimeTimerState) timers_.get(timerId); 1815 } 1816 1817 public synchronized boolean entityBeanHasTimers(long containerId, 1819 Object pkey) { 1820 Object containerInfo = containerTimers_.get(new Long (containerId)); 1821 return (containerInfo != null) ? 1822 ((Collection ) containerInfo).contains(pkey) : false; 1823 } 1824 1825 public synchronized boolean containerHasTimers(long containerId) { 1828 return containerTimers_.containsKey(new Long (containerId)); 1829 } 1830 1831 public synchronized void validate() { 1833 } 1834 1835 } 1837 private File getTimerServiceShutdownFile() 1838 throws Exception 1839 { 1840 File timerServiceShutdownDirectory; 1841 File timerServiceShutdownFile; 1842 1843 InstanceEnvironment env = ApplicationServer.getServerContext(). 1844 getInstanceEnvironment(); 1845 AppsManager appsManager = new AppsManager(env, false); 1846 1847 String j2eeAppPath = appsManager.getLocation(appID); 1848 timerServiceShutdownDirectory = new File (j2eeAppPath + File.separator); 1849 timerServiceShutdownDirectory.mkdirs(); 1850 timerServiceShutdownFile = new File (j2eeAppPath + File.separator 1851 + TIMER_SERVICE_FILE); 1852 1853 return timerServiceShutdownFile; 1854 } 1855 1856 private long getTimerServiceDownAt() { 1857 long timerServiceWentDownAt = -1; 1858 try { 1859 File timerServiceShutdownFile = getTimerServiceShutdownFile(); 1860 1861 if (timerServiceShutdownFile.exists()) { 1862 DateFormat dateFormat = 1863 new SimpleDateFormat (TIMER_SERVICE_DOWNTIME_FORMAT); 1864 1865 FileReader fr = new FileReader (timerServiceShutdownFile); 1866 BufferedReader br = new BufferedReader (fr, 128); 1867 String line = br.readLine(); 1868 1869 Date myDate = dateFormat.parse(line); 1870 timerServiceWentDownAt = myDate.getTime(); 1871 logger.log(Level.INFO, "ejb.timer_service_last_shutdown", 1872 new Object [] { line }); 1873 } else { 1874 logger.log(Level.WARNING, "ejb.timer_service_shutdown_unknown", 1875 new Object [] { timerServiceShutdownFile }); 1876 } 1877 } catch (Throwable th) { 1878 logger.log(Level.WARNING, "ejb.timer_service_shutdown_unknown", 1879 new Object [] { "" }); 1880 logger.log(Level.WARNING, "", th); 1881 } 1882 return timerServiceWentDownAt; 1883 } 1884 1885 1886 1889 public void onShutdown() { 1890 try { 1891 DateFormat dateFormat = 1892 new SimpleDateFormat (TIMER_SERVICE_DOWNTIME_FORMAT); 1893 String downTimeStr = dateFormat.format(new Date ()); 1894 1895 File timerServiceShutdownFile = getTimerServiceShutdownFile(); 1896 FileWriter fw = new FileWriter (timerServiceShutdownFile); 1897 PrintWriter pw = new PrintWriter (fw); 1898 1899 pw.println(downTimeStr); 1900 1901 pw.flush(); 1902 pw.close(); 1903 fw.close(); 1904 logger.log(Level.INFO, "ejb.timer_service_shutdown_msg", 1905 new Object [] { downTimeStr }); 1906 } catch (Throwable th) { 1907 logger.log(Level.WARNING, "ejb.timer_service_shutdown_unknown", 1908 new Object [] { TIMER_SERVICE_FILE }); 1909 logger.log(Level.WARNING, "", th); 1910 } 1911 } 1912 1913 1914 1918 private static class TaskExpiredWork 1919 implements com.sun.enterprise.util.threadpool.Servicable 1920 { 1921 private EJBTimerService timerService_; 1922 private TimerPrimaryKey timerId_; 1923 1924 public TaskExpiredWork(EJBTimerService timerService, 1925 TimerPrimaryKey timerId) { 1926 timerService_ = timerService; 1927 timerId_ = timerId; 1928 } 1929 1930 public void prolog() { } 1931 1932 public void epilog() { } 1933 1934 public void service() { run(); } 1935 1936 public void run() { 1937 timerService_.deliverTimeout(timerId_); 1939 } 1940 1941 } 1943} 1944 | Popular Tags |