From af7d382624f48293d59cfc98842dde1808b7690c Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Fri, 19 Oct 2018 12:58:45 +0530 Subject: [PATCH 1/7] PAYARA-2905 Prevent creation of databases used for Timer Service when only non-persistent timers are present --- .../ejb/EjbNamingReferenceManagerImpl.java | 17 +- .../AbstractSessionContextImpl.java | 23 +- .../com/sun/ejb/containers/BaseContainer.java | 22 +- .../sun/ejb/containers/EJBTimerService.java | 491 ++++++++---------- .../containers/EJBTimerServiceWrapper.java | 241 +++++---- .../NonPersistentEJBTimerService.java | 224 ++++++++ .../ejb/containers/SessionContextImpl.java | 4 +- .../ejb/containers/SingletonContextImpl.java | 8 +- .../containers/TimerServiceNamingProxy.java | 27 +- .../com/sun/ejb/containers/TimerWrapper.java | 79 +-- .../glassfish/ejb/admin/cli/ListTimers.java | 16 +- .../ejb/admin/cli/MigrateTimers.java | 6 +- .../handlers/AbstractEjbHandler.java | 3 +- .../glassfish/ejb/startup/EjbDeployer.java | 36 +- .../ejb/mdb/MessageBeanContextImpl.java | 12 +- .../timer/DistributedEJBTimerService.java | 6 +- .../timer/PersistentEJBTimerService.java | 15 +- .../ejb/persistent/timer/TimerBean.java | 16 +- .../ejb/persistent/timer/TimerState.java | 18 +- 19 files changed, 760 insertions(+), 504 deletions(-) create mode 100644 appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/NonPersistentEJBTimerService.java diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/EjbNamingReferenceManagerImpl.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/EjbNamingReferenceManagerImpl.java index 8ec1a7f886f..f8281d723a2 100644 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/EjbNamingReferenceManagerImpl.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/EjbNamingReferenceManagerImpl.java @@ -37,11 +37,11 @@ * only if the new code is made subject to such option by the copyright * holder. */ +// Portions Copyright [2018] [Payara Foundation and/or its affiliates] package com.sun.ejb; import com.sun.ejb.containers.EJBContextImpl; -import com.sun.ejb.containers.EJBTimerServiceWrapper; import com.sun.ejb.containers.EJBTimerService; import com.sun.enterprise.container.common.spi.EjbNamingReferenceManager; import com.sun.enterprise.deployment.EjbReferenceDescriptor; @@ -50,13 +50,10 @@ import org.glassfish.enterprise.iiop.api.GlassFishORBHelper; import javax.inject.Inject; import org.jvnet.hk2.annotations.Service; - import javax.inject.Provider; import javax.naming.Context; import com.sun.enterprise.util.Utility; - import org.omg.CORBA.ORB; - import javax.naming.NamingException; /** @@ -200,6 +197,7 @@ public Object resolveEjbReference(EjbReferenceDescriptor ejbRefDesc, Context con return resolved ? jndiObj : EJBUtils.resolveEjbRefObject(ejbRefDesc, jndiObj); } + @Override public boolean isEjbReferenceCacheable(EjbReferenceDescriptor ejbRefDesc) { // Ejb-ref is only eligible for caching if it refers to the legacy // Home view and it is resolved to an ejb within the same application. @@ -211,6 +209,7 @@ public boolean isEjbReferenceCacheable(EjbReferenceDescriptor ejbRefDesc) { } + @Override public Object getEJBContextObject(String contextType) { ComponentInvocation currentInv = invMgr.getCurrentInvocation(); @@ -228,16 +227,10 @@ public Object getEJBContextObject(String contextType) { Object returnObject = ejbInv.context; - if( contextType.equals("javax.ejb.TimerService") ) { - if (EJBTimerService.getEJBTimerService() == null ) { - throw new IllegalStateException("EJB Timer Service not " + - "available"); - } - returnObject = new EJBTimerServiceWrapper - (EJBTimerService.getEJBTimerService(), (EJBContextImpl) ejbInv.context); + if (contextType.equals("javax.ejb.TimerService")) { + returnObject = EJBTimerService.getEJBTimerServiceWrapper((EJBContextImpl) ejbInv.context); } - return returnObject; } diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/AbstractSessionContextImpl.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/AbstractSessionContextImpl.java index bdac9d59a52..bb5266a61f5 100644 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/AbstractSessionContextImpl.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/AbstractSessionContextImpl.java @@ -37,29 +37,20 @@ * only if the new code is made subject to such option by the copyright * holder. */ +// Portions Copyright [2018] [Payara Foundation and/or its affiliates] package com.sun.ejb.containers; import com.sun.ejb.EjbInvocation; -import com.sun.ejb.spi.container.StatefulEJBContext; import com.sun.enterprise.deployment.EjbDescriptor; import com.sun.enterprise.deployment.EjbSessionDescriptor; import org.glassfish.api.invocation.ComponentInvocation; import org.glassfish.api.invocation.InvocationManager; - -import javax.ejb.EJBException; import javax.ejb.SessionContext; import javax.ejb.TimerService; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; import javax.transaction.UserTransaction; import javax.xml.rpc.handler.MessageContext; import com.sun.ejb.EJBUtils; -import java.util.*; - - -import static com.sun.ejb.containers.StatefulSessionContainer.EEMRefInfo; -import static com.sun.ejb.containers.StatefulSessionContainer.EEMRefInfoKey; /** * Implementation of EJBContext for SessionBeans @@ -92,10 +83,12 @@ public void setInstanceKey(Object instanceKey) { this.instanceKey = instanceKey; } + @Override public String toString() { return ejbName + "; id: " + instanceKey; } + @Override public TimerService getTimerService() throws IllegalStateException { // Instance key is first set between after setSessionContext and @@ -104,10 +97,10 @@ public TimerService getTimerService() throws IllegalStateException { throw new IllegalStateException("Operation not allowed"); } - EJBTimerService timerService = EJBTimerService.getValidEJBTimerService(); - return new EJBTimerServiceWrapper(timerService, this); + return EJBTimerService.getEJBTimerServiceWrapper(this); } + @Override public UserTransaction getUserTransaction() throws IllegalStateException { // The state check ensures that an exception is thrown if this @@ -120,6 +113,7 @@ public UserTransaction getUserTransaction() return ((BaseContainer) getContainer()).getUserTransaction(); } + @Override public MessageContext getMessageContext() { InvocationManager invManager = EjbContainerUtilImpl.getInstance().getInvocationManager(); try { @@ -138,6 +132,7 @@ public MessageContext getMessageContext() { } } + @Override public T getBusinessObject(Class businessInterface) throws IllegalStateException { @@ -202,6 +197,7 @@ public T getBusinessObject(Class businessInterface) return businessObject; } + @Override public Class getInvokedBusinessInterface() throws IllegalStateException { @@ -235,6 +231,7 @@ public Class getInvokedBusinessInterface() return businessInterface; } + @Override public boolean wasCancelCalled() { try { @@ -262,6 +259,7 @@ public boolean wasCancelCalled() { "outside an ejb invocation"); } + @Override protected void checkAccessToCallerSecurity() throws IllegalStateException { if (state == BeanState.CREATED) { @@ -270,6 +268,7 @@ protected void checkAccessToCallerSecurity() } + @Override public void checkTimerServiceMethodAccess() throws IllegalStateException { // checks that apply to both stateful AND stateless diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/BaseContainer.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/BaseContainer.java index 9a6baba27f5..edae085adba 100644 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/BaseContainer.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/BaseContainer.java @@ -315,6 +315,7 @@ public enum ContainerType { protected boolean isWebServiceEndpoint = false; private boolean isTimedObject_ = false; + private boolean isPersistenceTimer; /***************************************** * Data members for Local views * @@ -793,6 +794,7 @@ protected BaseContainer(ContainerType type, EjbDescriptor ejbDesc, ClassLoader l // ignore. Will happen for EJB 3.0 session beans } + isPersistenceTimer = false; if ( ejbDescriptor.isTimedObject() ) { warnIfNotFullProfile("use of persistent EJB Timer Service"); @@ -802,6 +804,7 @@ protected BaseContainer(ContainerType type, EjbDescriptor ejbDesc, ClassLoader l // Can be a @Timeout or @Schedule or TimedObject if (ejbTimeoutMethodDesc != null) { Method method = ejbTimeoutMethodDesc.getMethod(ejbDescriptor); + isPersistenceTimer = true; // timers defined in runtime processEjbTimeoutMethod(method); ejbTimeoutMethod = method; @@ -818,14 +821,17 @@ protected BaseContainer(ContainerType type, EjbDescriptor ejbDesc, ClassLoader l ejbClass.getName(), schd.getTimeoutMethod().getFormattedString())); } - if ( _logger.isLoggable(Level.FINE) ) { - _logger.log(Level.FINE, "... processing " + method ); + if (_logger.isLoggable(Level.FINE)) { + _logger.log(Level.FINE, "... processing {0}", method); + } + if (!isPersistenceTimer) { + isPersistenceTimer = schd.getPersistent(); } processEjbTimeoutMethod(method); List list = schedules.get(method); if (list == null) { - list = new ArrayList(); + list = new ArrayList<>(); schedules.put(method, list); } list.add(schd); @@ -836,7 +842,7 @@ protected BaseContainer(ContainerType type, EjbDescriptor ejbDesc, ClassLoader l if( !isStatefulSession ) { // EJBTimerService should be accessed only if needed // not to cause it to be loaded if it's not used. - EJBTimerService timerService = EJBTimerService.getEJBTimerService(); + EJBTimerService timerService = EJBTimerService.getEJBTimerService(isPersistenceTimer); if( timerService != null ) { timerService.timedObjectCount(); } @@ -2237,7 +2243,7 @@ void addSchedule(TimerPrimaryKey timerId, EJBTimerSchedule ts) { } /** - * Check timeout method and set it accessable + * Check timeout method and set it accessible */ private void processEjbTimeoutMethod(Method method) throws Exception { Class[] params = method.getParameterTypes(); @@ -2493,7 +2499,7 @@ protected void cancelTimers(Object key) { if ( isTimedObject() ) { // EJBTimerService should be accessed only if needed // not to cause it to be loaded if it's not used. - EJBTimerService timerService = EJBTimerService.getEJBTimerService(); + EJBTimerService timerService = EJBTimerService.getEJBTimerService(isPersistenceTimer); if ( timerService != null ) { timerService.cancelTimersByKey(getContainerId(), key); } @@ -2502,7 +2508,7 @@ protected void cancelTimers(Object key) { private void stopTimers() { if ( isTimedObject() ) { - EJBTimerService ejbTimerService = EJBTimerService.getEJBTimerService(); + EJBTimerService ejbTimerService = EJBTimerService.getEJBTimerService(isPersistenceTimer); if ( ejbTimerService != null ) { ejbTimerService.stopTimers(getContainerId()); } @@ -4024,7 +4030,7 @@ public void startApplication(boolean deploy) { if ( isTimedObject_ ) { // EJBTimerService should be accessed only if needed // not to cause it to be loaded if it's not used. - EJBTimerService timerService = EJBTimerService.getEJBTimerService(); + EJBTimerService timerService = EJBTimerService.getEJBTimerService(isPersistenceTimer); if (timerService != null) { boolean deploy0 = deploy; //avoid modifying param if (deploy0 && ejbDescriptor.getApplication().getKeepStateResolved()) { diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java index dff3320ffd6..501b239cb4e 100755 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -// Portions Copyright [2016-2017] [Payara Foundation and/or its affiliates] +// Portions Copyright [2016-2018] [Payara Foundation and/or its affiliates] package com.sun.ejb.containers; import java.io.Serializable; @@ -79,6 +79,8 @@ import fish.payara.notification.requesttracing.RequestTraceSpan; import fish.payara.nucleus.requesttracing.RequestTracingService; import fish.payara.nucleus.healthcheck.stuck.StuckThreadsStore; +import static java.util.Objects.isNull; +import static java.util.logging.Level.INFO; import org.glassfish.internal.api.Globals; /* @@ -91,7 +93,7 @@ * @author Kenneth Saks * @author Marina Vatkina */ -public class EJBTimerService { +public abstract class EJBTimerService { protected EjbContainerUtil ejbContainerUtil = EjbContainerUtilImpl.getInstance(); @@ -151,21 +153,23 @@ public class EJBTimerService { // amount of time the container waits between timer redelivery attempts. private long redeliveryInterval_ = REDELIVERY_INTERVAL; - private static final String TIMER_SERVICE_DOWNTIME_FORMAT = - "yyyy/MM/dd HH:mm:ss"; + private static final String TIMER_SERVICE_DOWNTIME_FORMAT = "yyyy/MM/dd HH:mm:ss"; - private Agent agent = ejbContainerUtil.getCallFlowAgent(); + private final Agent agent = ejbContainerUtil.getCallFlowAgent(); // Allow to reschedule a failed timer for the next delivery private static final String RESCHEDULE_FAILED_TIMER = "reschedule-failed-timer"; private boolean rescheduleFailedTimer = false; + private static final Object LOCK = new Object(); + // Flag that allows to load EJBTimerService on the 1st access and // distinguish between not available and not loaded - private static volatile boolean _timerServiceVerified = false; - private static Object lock = new Object(); + private static volatile boolean persistentTimerServiceVerified = false; + private static volatile boolean nonPersistentTimerServiceVerified = false; - private static EJBTimerService _timerService; + private static EJBTimerService persistentTimerService; + private static EJBTimerService nonPersistentTimerService; protected EJBTimerService() throws Exception { timerCache_ = new TimerCache(); @@ -185,59 +189,216 @@ protected EJBTimerService() throws Exception { initProperties(); } - protected static void setEJBTimerService(EJBTimerService es) { - _timerService = es; + public static EJBTimerServiceWrapper getEJBTimerServiceWrapper(EJBContextImpl ejbContext) { + if (isNull(persistentTimerService) && isNull(nonPersistentTimerService)) { + throw new IllegalStateException("EJB Timer Service not available"); + } + return new EJBTimerServiceWrapper( + persistentTimerService, + nonPersistentTimerService, + ejbContext + ); + } + + protected static void setPersistentTimerService(EJBTimerService timerService) { + persistentTimerService = timerService; + } + + protected static void setNonPersistentTimerService(EJBTimerService timerService) { + nonPersistentTimerService = timerService; } static void unsetEJBTimerService() { - _timerServiceVerified = false; - _timerService = null; + persistentTimerServiceVerified = false; + persistentTimerService = null; + + nonPersistentTimerServiceVerified = false; + nonPersistentTimerService = null; } - public static EJBTimerService getEJBTimerService() { - return getEJBTimerService(null); + public static EJBTimerService getPersistentTimerService() { + return getEJBTimerService(null, false, true); } - public static EJBTimerService getValidEJBTimerService() { - getEJBTimerService(); - if (_timerService == null) { - throw new EJBException("EJB Timer service not available"); - } + public static EJBTimerService getNonPersistentTimerService() { + return getEJBTimerService(null, false, false); + } + + public static boolean isPersistentTimerServiceLoaded() { + return persistentTimerServiceVerified; + } - return _timerService; + public static boolean isNonPersistentTimerServiceLoaded() { + return nonPersistentTimerServiceVerified; } - public static boolean isEJBTimerServiceLoaded() { - return _timerServiceVerified; + public static EJBTimerService getEJBTimerService(boolean persistent) { + return getEJBTimerService(null, true, persistent); } public static EJBTimerService getEJBTimerService(String target) { - if (!_timerServiceVerified) { - initEJBTimerService(target, true); - } - return _timerService; + return getEJBTimerService(target, true, true); } public static EJBTimerService getEJBTimerService(String target, boolean force) { - if (!_timerServiceVerified) { - initEJBTimerService(target, force); + return getEJBTimerService(target, force, true); + } + + public static EJBTimerService getEJBTimerService(String target, boolean force, boolean persistent) { + if (persistent) { + if (!persistentTimerServiceVerified) { + initPersistentTimerService(target, force); + } + return persistentTimerService; + } else { + if (!nonPersistentTimerServiceVerified) { + initNonPersistentTimerService(target, force); + } + return nonPersistentTimerService; } - return _timerService; } - private static synchronized void initEJBTimerService(String target, boolean force) { - if (_timerService == null) { - List persistentTSList = - EjbContainerUtilImpl.getInstance().getServices().getAllServices(PersistentTimerService.class); + + public abstract boolean isPersistent(); + + /** + * Called by CLI.Take ownership of another server's timers. + * @param fromOwnerId + * @return + */ + public abstract int migrateTimers(String fromOwnerId); + + /** + * Called at server startup *after* user apps have been re-activated to + * restart any active EJB timers or cleanup old timers.No-op for + * non-persistent timers + * + * @param target + */ + protected abstract void resetEJBTimers(String target); + + /** + * Remove from the cache and stop all timers associated with a particular + * ejb container and known to this server instance.This is typically called + * when an ejb is disabled or on a server shutdown to avoid accidentally + * removing a valid timer. This is also called when an ejb is disabled as + * part of an undeploy. Removal of the associated timer from the database is + * done as the last step of undeployment. + * + * @param containerId + */ + protected abstract void stopTimers(long containerId); + + /** + * Called by EJBTimerServiceWrapper when caller calls getTimers. + * + * @param containerId the id of the EJB which owns the timers + * @param timedObjectPrimaryKey can be null if not entity bean + * @return Collection of Timer Ids. + */ + protected abstract Collection getTimerIds( + long containerId, + Object timedObjectPrimaryKey + ); + + /** + * @param containerIds the EJBs which own the timers + * @return Collection of Timer Ids. + */ + protected abstract Collection getTimerIds(Collection containerIds); + + /** + * Cancel all timers associated with a particular entity bean identity.This + * is typically called when an entity bean is removed.Note that this action + * falls under the normal EJB Timer removal semantics, which means it can be + * rolled back if the transaction rolls back. + * + * @param containerId + * @param primaryKey + */ + protected abstract void cancelTimersByKey(long containerId, Object primaryKey); + + protected abstract void cancelTimer(TimerPrimaryKey timerId) + throws FinderException, Exception; + + /** + * Return next planned timeout for this timer.We have a fair amount of + * leeway regarding the consistency of this information.We should strive to + * detect the case where the timer no longer exists.However, since the + * current timer instance may not even own this timer, it's difficult to + * know the exact time of delivery in another server instance. In the case + * of single-action timers, we return the expiration time that was provided + * upon timer creation. For periodic timers, we can derive the next + * scheduled fixed rate expiration based on the initial expiration and the + * interval. + * + * @param timerId + * @return + * @throws javax.ejb.FinderException + */ + protected abstract Date getNextTimeout(TimerPrimaryKey timerId) throws FinderException; + + protected abstract Serializable getInfo(TimerPrimaryKey timerId) throws FinderException; + + protected abstract boolean isPersistent(TimerPrimaryKey timerId) throws FinderException; + + protected abstract boolean timerExists(TimerPrimaryKey timerId); + + /** + * Called by #getScheduleExpression and #isCalendarTimer + * @param timerId + * @return + * @throws javax.ejb.FinderException + */ + protected abstract EJBTimerSchedule getTimerSchedule(TimerPrimaryKey timerId) + throws FinderException; + + /** + * Non-persistent timers are always valid because to be executed on this + * server instance. + * @param timerId + * @param timerState + * @return + */ + protected abstract boolean isValidTimerForThisServer( + TimerPrimaryKey timerId, + RuntimeTimerState timerState + ); + + /** + * Nothing special to do for non-persistent timers + * @param timerId + * @param timerState + */ + protected abstract void resetLastExpiration( + TimerPrimaryKey timerId, + RuntimeTimerState timerState + ); + + private static synchronized void initNonPersistentTimerService(String target, boolean force) { + if(nonPersistentTimerService == null) { + try { + nonPersistentTimerService = new NonPersistentEJBTimerService(); + nonPersistentTimerServiceVerified = true; + } catch (Exception e) { + logger.log(Level.WARNING, "Cannot start non-persistent EJBTimerService: ", e); + } + } + } + + private static synchronized void initPersistentTimerService(String target, boolean force) { + if (persistentTimerService == null) { + List persistentTSList + = EjbContainerUtilImpl.getInstance().getServices().getAllServices(PersistentTimerService.class); if (persistentTSList.isEmpty()) { try { - _timerService = new EJBTimerService(); - _timerServiceVerified = true; + persistentTimerService = new NonPersistentEJBTimerService(); + persistentTimerServiceVerified = true; } catch (Exception e) { - logger.log (Level.WARNING, "Cannot start EJBTimerService: ", e); + logger.log(Level.WARNING, "Cannot start persistent EJBTimerService: ", e); } } else { - synchronized (lock) { + synchronized (LOCK) { // choose service based on the configuration setting EjbContainerUtil ejbContainerUtil = EjbContainerUtilImpl.getInstance(); String serviceType = ejbContainerUtil.getEjbContainer().getEjbTimerService().getEjbTimerService(); @@ -252,21 +413,21 @@ private static synchronized void initEJBTimerService(String target, boolean forc persistentTS.initPersistentTimerService(target); } else { // Fall back to the non-persistent service try { - _timerService = new EJBTimerService(); + persistentTimerService = new NonPersistentEJBTimerService(); } catch (Exception e) { - logger.log (Level.WARNING, "Cannot start EJBTimerService: ", e); + logger.log(Level.WARNING, "Cannot start persistent EJBTimerService: ", e); } } - _timerServiceVerified = true; + persistentTimerServiceVerified = true; } // Do postprocessing if everything is OK - if (_timerService != null) { - _timerService.resetEJBTimers(target); - } else if (!force) { + if (persistentTimerService != null) { + persistentTimerService.resetEJBTimers(target); + } else if (!force) { // If it was a request with force == false, and we failed to load the service, // do not mark it as verified - _timerServiceVerified = false; + persistentTimerServiceVerified = false; } } } @@ -348,14 +509,6 @@ public String[] listTimers( String[] serverIds ) { return result; } - - /** - * Called by CLI. Take ownership of another server's timers. - */ - public int migrateTimers(String fromOwnerId) { - throw new IllegalStateException("Non-persistent timers cannot be migrated"); - } - /** * Create EJBException using the exception that is passed in */ @@ -365,15 +518,6 @@ protected EJBException createEJBException( Exception ex ) { return ejbEx; } - /** - * Called at server startup *after* user apps have been re-activated - * to restart any active EJB timers or cleanup old timers. - * No-op for non-persitent timers - */ - protected void resetEJBTimers(String target) { - // Nothing to do - } - protected void addToSchedules(long containerId, TimerPrimaryKey timerId, EJBTimerSchedule ts) { BaseContainer container = getContainer(containerId); @@ -414,17 +558,6 @@ public void destroyAllTimers(long applicationId) { protected void _destroyTimers(long id, boolean all) { // do nothing } - /** - * Remove from the cache and stop all timers associated with a particular ejb container - * and known to this server instance. - * This is typically called when an ejb is disabled or on a server shutdown - * to avoid accidentally removing a valid timer. - * This is also called when an ejb is disabled as part of an undeploy. Removal - * of the associated timer from the database is done as the last step of undeployment. - */ - protected void stopTimers(long containerId) { - stopTimers(timerCache_.getNonPersistentTimerIdsForContainer(containerId)); - } protected void stopTimers(Set timerIds) { for(TimerPrimaryKey nextTimerId: timerIds) { @@ -913,37 +1046,6 @@ protected void createSchedules(long containerId, long applicationId, } } - /** - * Called by EJBTimerServiceWrapper when caller calls getTimers. - * - * @param containerId the id of the EJB which owns the timers - * @param timedObjectPrimaryKey can be null if not entity bean - * @return Collection of Timer Ids. - */ - protected Collection getTimerIds(long containerId, Object timedObjectPrimaryKey) { - - Collection timerIdsForTimedObject = - new HashSet(); - - // Add active non-persistent timer ids - timerIdsForTimedObject.addAll( - timerCache_.getNonPersistentActiveTimerIdsForContainer(containerId)); - - return timerIdsForTimedObject; - } - - /** - * @param containerIds the EJBs which own the timers - * @return Collection of Timer Ids. - */ - protected Collection getTimerIds(Collection containerIds) { - Collection timerIds = new HashSet(); - for (long containerId : containerIds) { - timerIds.addAll(timerCache_.getNonPersistentActiveTimerIdsForContainer(containerId)); - } - return timerIds; - } - /** * Get the application class loader for the timed object * that created a given timer. @@ -957,73 +1059,6 @@ protected RuntimeTimerState getTimerState(TimerPrimaryKey timerId) { return timerCache_.getTimerState(timerId); } - private RuntimeTimerState getNonPersistentTimerState(TimerPrimaryKey timerId) { - return timerCache_.getNonPersistentTimerState(timerId); - } - - // Returns a Set of active non-persistent timer ids for this server - public Set getNonPersistentActiveTimerIdsByThisServer() { - return timerCache_.getNonPersistentActiveTimerIdsByThisServer(); - } - - /** - * Cancel all timers associated with a particular entity bean - * identity. This is typically called when an entity bean is - * removed. Note that this action falls under the normal EJB - * Timer removal semantics, which means it can be rolled back - * if the transaction rolls back. - */ - protected void cancelTimersByKey(long containerId, Object primaryKey) { - //Do nothing or throw an exception? - } - - // - // Logic used by TimerWrapper for javax.ejb.Timer methods. - // - - protected void cancelTimer(TimerPrimaryKey timerId) - throws FinderException, Exception { - cancelNonPersistentTimer(timerId); - } - - protected boolean cancelNonPersistentTimer(TimerPrimaryKey timerId) - throws FinderException, Exception { - - RuntimeTimerState rt = getNonPersistentTimerState(timerId); - if (rt != null) { - if( rt.isCancelled()) { - // already cancelled or removed - return true; - } - - cancelTimerSynchronization(null, timerId, - rt.getContainerId(), ownerIdOfThisServer_, false); - } - - return (rt != null); - } - - /** - * Return next planned timeout for this timer. We have a fair amount - * of leeway regarding the consistency of this information. We should - * strive to detect the case where the timer no longer exists. However, - * since the current timer instance may not even own this timer, - * it's difficult to know the exact time of delivery in another server - * instance. In the case of single-action timers, we return the - * expiration time that was provided upon timer creation. For - * periodic timers, we can derive the next scheduled fixed rate - * expiration based on the initial expiration and the interval. - */ - protected Date getNextTimeout(TimerPrimaryKey timerId) throws FinderException { - - RuntimeTimerState rt = getNonPersistentTimer(timerId); - if (rt != null) { - return _getNextTimeout(rt); - } - - throw new FinderException("Timer does not exist"); - } - /** * Non-persistent part of the implementation of the getNextTimeout() call */ @@ -1046,17 +1081,6 @@ protected Date _getNextTimeout(RuntimeTimerState rt) { return nextTimeout; } - protected Serializable getInfo(TimerPrimaryKey timerId) throws FinderException { - - // Check non-persistent timers only - RuntimeTimerState rt = getNonPersistentTimer(timerId); - if (rt != null) { - return rt.getInfo(); - } - - return null; - } - ScheduleExpression getScheduleExpression(TimerPrimaryKey timerId) throws FinderException { EJBTimerSchedule ts = getTimerSchedule(timerId); @@ -1069,61 +1093,6 @@ boolean isCalendarTimer(TimerPrimaryKey timerId) throws FinderException { return (ts != null); } - protected boolean isPersistent(TimerPrimaryKey timerId) throws FinderException { - - // Check non-persistent timers only - RuntimeTimerState rt = getNonPersistentTimer(timerId); - if (rt != null) { - // Found and active - return false; - } - - // XXX? - return true; - } - - protected boolean timerExists(TimerPrimaryKey timerId) { - boolean exists = false; - - // Check non-persistent timers only - RuntimeTimerState rt = getNonPersistentTimerState(timerId); - if (rt != null) { - exists = rt.isActive(); - } - - return exists; - } - - /** - * Called by #getScheduleExpression and #isCalendarTimer - */ - protected EJBTimerSchedule getTimerSchedule(TimerPrimaryKey timerId) throws FinderException { - - // Check non-persistent timers first - EJBTimerSchedule ts = null; - - RuntimeTimerState rt = getNonPersistentTimer(timerId); - if (rt != null) { - ts = rt.getTimerSchedule(); - } - - return ts; - } - - protected RuntimeTimerState getNonPersistentTimer(TimerPrimaryKey timerId) - throws FinderException { - RuntimeTimerState rt = getNonPersistentTimerState(timerId); - if (rt != null) { - if (rt.isCancelled()) { - // The timer has been cancelled within this tx. - throw new FinderException("Non-persistent timer " + timerId - + " does not exist"); - } - } - - return rt; - } - protected BaseContainer getContainer(long containerId) { return ejbContainerUtil.getContainer(containerId); } @@ -1409,23 +1378,6 @@ boolean postEjbTimeout(TimerPrimaryKey timerId) { return success; } - /** - * Non-persistent timers are always valid because to be - * executed on this server instance. - */ - protected boolean isValidTimerForThisServer(TimerPrimaryKey timerId, - RuntimeTimerState timerState) { - return true; - } - - /** - * Nothing special to do for non-persistent timers - */ - protected void resetLastExpiration(TimerPrimaryKey timerId, - RuntimeTimerState timerState) { - // Do nothing - } - /** * This method is called back from the EJBTimerTask object * on the JDK Timer Thread. Work performed in this callback @@ -1545,7 +1497,7 @@ public void cancelTimerSynchronization(EJBContextImpl context_, cancelTimerSynchronization(context_, timerId, containerId, ownerId, true); } - private void cancelTimerSynchronization(EJBContextImpl context_, + protected void cancelTimerSynchronization(EJBContextImpl context_, TimerPrimaryKey timerId, long containerId, String ownerId, boolean persistent) throws Exception { @@ -1652,25 +1604,26 @@ ContainerSynchronization getContainerSynch(EJBContextImpl context_, return ejbContainerUtil.getContainerSync(transaction); } - public boolean isPersistent() { - return false; - } - /** * Called from TimerBean PreDestroy */ public static void onShutdown() { - if( _timerService == null || _timerService.shutdown_ ) { - return; + boolean shutdown = false; + if (nonPersistentTimerService != null && !nonPersistentTimerService.shutdown_) { + nonPersistentTimerService.shutdown(); + shutdown = true; } + if (persistentTimerService != null && !persistentTimerService.shutdown_) { + persistentTimerService.shutdown(); + shutdown = true; + } + if (shutdown) { + DateFormat dateFormat + = new SimpleDateFormat(TIMER_SERVICE_DOWNTIME_FORMAT); + String downTimeStr = dateFormat.format(new Date()); - _timerService.shutdown(); - DateFormat dateFormat = - new SimpleDateFormat(TIMER_SERVICE_DOWNTIME_FORMAT); - String downTimeStr = dateFormat.format(new Date()); - - logger.log(Level.INFO, "ejb.timer_service_shutdown_msg", - new Object[] { downTimeStr }); + logger.log(INFO, "ejb.timer_service_shutdown_msg", new Object[]{downTimeStr}); + } } static String txStatusToString(int txStatus) { @@ -1775,7 +1728,7 @@ public TimerCache() { public synchronized void addTimer(TimerPrimaryKey timerId, RuntimeTimerState timerState) { if( logger.isLoggable(Level.FINE) ) { - logger.log(Level.FINE, "Adding timer " + timerState); + logger.log(Level.FINE, "Adding timer {0}", timerState); } timers_.put(timerId, timerState); @@ -1814,7 +1767,7 @@ public synchronized void addTimer(TimerPrimaryKey timerId, */ public synchronized void removeTimer(TimerPrimaryKey timerId) { if( logger.isLoggable(Level.FINE) ) { - logger.log(Level.FINE, "Removing timer " + timerId); + logger.log(Level.FINE, "Removing timer {0}", timerId); } RuntimeTimerState timerState = (RuntimeTimerState) diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerServiceWrapper.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerServiceWrapper.java index 7202a791937..0cf666e6e72 100644 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerServiceWrapper.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerServiceWrapper.java @@ -37,29 +37,22 @@ * only if the new code is made subject to such option by the copyright * holder. */ +// Portions Copyright [2018] [Payara Foundation and/or its affiliates] package com.sun.ejb.containers; -import com.sun.enterprise.deployment.EjbBundleDescriptor; import org.glassfish.ejb.deployment.descriptor.EjbDescriptor; - import java.util.Date; import java.util.Collection; -import java.util.Set; import java.util.HashSet; import java.util.Iterator; - import java.io.Serializable; - -import javax.ejb.EJBLocalObject; import javax.ejb.TimerService; import javax.ejb.Timer; import javax.ejb.TimerConfig; import javax.ejb.ScheduleExpression; import javax.ejb.EJBException; -import javax.ejb.FinderException; import javax.ejb.CreateException; - import javax.ejb.EntityContext; /* @@ -70,7 +63,8 @@ */ public class EJBTimerServiceWrapper implements TimerService { - private EJBTimerService timerService_; + private EJBTimerService persistentTimerService_; + private EJBTimerService nonPersistentTimerService_; private EJBContextImpl ejbContext_; private EjbDescriptor ejbDescriptor_; @@ -79,10 +73,12 @@ public class EJBTimerServiceWrapper implements TimerService { // Only used for entity beans private Object timedObjectPrimaryKey_; - public EJBTimerServiceWrapper(EJBTimerService timerService, - EJBContextImpl ejbContext) - { - timerService_ = timerService; + public EJBTimerServiceWrapper( + EJBTimerService persistentTimerService, + EJBTimerService nonPersistentTimerService, + EJBContextImpl ejbContext) { + persistentTimerService_ = persistentTimerService; + nonPersistentTimerService_ = nonPersistentTimerService; ejbContext_ = ejbContext; BaseContainer container = (BaseContainer) ejbContext.getContainer(); ejbDescriptor_ = container.getEjbDescriptor(); @@ -90,16 +86,18 @@ public EJBTimerServiceWrapper(EJBTimerService timerService, timedObjectPrimaryKey_ = null; } - public EJBTimerServiceWrapper(EJBTimerService timerService, - EntityContext entityContext) - { - this(timerService, ((EJBContextImpl)entityContext)); - entity_ = true; + public EJBTimerServiceWrapper( + EJBTimerService persistentTimerService, + EJBTimerService nonPersistentTimerService, + EntityContext entityContext) { + this(persistentTimerService, nonPersistentTimerService, ((EJBContextImpl) entityContext)); + entity_ = true; // Delay access of primary key since this might have been called // from ejbCreate - timedObjectPrimaryKey_ = null; + timedObjectPrimaryKey_ = null; } + @Override public Timer createTimer(long duration, Serializable info) throws IllegalArgumentException, IllegalStateException, EJBException { @@ -109,9 +107,12 @@ public Timer createTimer(long duration, Serializable info) return createTimerInternal(duration, 0, info); } - public Timer createTimer(long initialDuration, long intervalDuration, - Serializable info) - throws IllegalArgumentException, IllegalStateException, EJBException { + @Override + public Timer createTimer( + long initialDuration, + long intervalDuration, + Serializable info + ) throws IllegalArgumentException, IllegalStateException, EJBException { checkCreateTimerCallPermission(); checkInitialDuration(initialDuration); @@ -119,6 +120,7 @@ public Timer createTimer(long initialDuration, long intervalDuration, return createTimerInternal(initialDuration, intervalDuration, info); } + @Override public Timer createTimer(Date expiration, Serializable info) throws IllegalArgumentException, IllegalStateException, EJBException { @@ -128,9 +130,12 @@ public Timer createTimer(Date expiration, Serializable info) return createTimerInternal(expiration, 0, info); } - public Timer createTimer(Date initialExpiration, long intervalDuration, - Serializable info) - throws IllegalArgumentException, IllegalStateException, EJBException { + @Override + public Timer createTimer( + Date initialExpiration, + long intervalDuration, + Serializable info + ) throws IllegalArgumentException, IllegalStateException, EJBException { checkCreateTimerCallPermission(); checkExpiration(initialExpiration); @@ -138,58 +143,64 @@ public Timer createTimer(Date initialExpiration, long intervalDuration, return createTimerInternal(initialExpiration, intervalDuration, info); } - public Timer createSingleActionTimer(long duration, TimerConfig timerConfig) throws - java.lang.IllegalArgumentException, java.lang.IllegalStateException, - javax.ejb.EJBException { + @Override + public Timer createSingleActionTimer(long duration, TimerConfig timerConfig) + throws IllegalArgumentException, IllegalStateException, EJBException { checkCreateTimerCallPermission(); checkDuration(duration); return createTimerInternal(duration, 0, timerConfig); } - public Timer createIntervalTimer(long initialDuration, - long intervalDuration, TimerConfig timerConfig) throws - java.lang.IllegalArgumentException, java.lang.IllegalStateException, - javax.ejb.EJBException { + @Override + public Timer createIntervalTimer( + long initialDuration, + long intervalDuration, + TimerConfig timerConfig + ) throws IllegalArgumentException, IllegalStateException, EJBException { checkCreateTimerCallPermission(); checkInitialDuration(initialDuration); return createTimerInternal(initialDuration, intervalDuration, timerConfig); } - public Timer createSingleActionTimer(Date initialExpiration, - TimerConfig timerConfig) throws - java.lang.IllegalArgumentException, java.lang.IllegalStateException, - javax.ejb.EJBException { + @Override + public Timer createSingleActionTimer( + Date initialExpiration, + TimerConfig timerConfig + ) throws IllegalArgumentException, IllegalStateException, EJBException { checkCreateTimerCallPermission(); checkExpiration(initialExpiration); return createTimerInternal(initialExpiration, 0, timerConfig); } - public Timer createIntervalTimer(Date initialExpiration, - long intervalDuration, TimerConfig timerConfig) throws - java.lang.IllegalArgumentException, java.lang.IllegalStateException, - javax.ejb.EJBException { + @Override + public Timer createIntervalTimer( + Date initialExpiration, + long intervalDuration, + TimerConfig timerConfig + ) throws IllegalArgumentException, IllegalStateException, EJBException { checkCreateTimerCallPermission(); checkExpiration(initialExpiration); return createTimerInternal(initialExpiration, intervalDuration, timerConfig); } - public Timer createCalendarTimer(ScheduleExpression schedule, - TimerConfig timerConfig) throws - java.lang.IllegalArgumentException, java.lang.IllegalStateException, - javax.ejb.EJBException { + @Override + public Timer createCalendarTimer( + ScheduleExpression schedule, + TimerConfig timerConfig + ) throws IllegalArgumentException, IllegalStateException, EJBException { checkCreateTimerCallPermission(); checkScheduleExpression(schedule); return createTimerInternal(schedule, timerConfig); } - public Timer createCalendarTimer(ScheduleExpression schedule) throws - java.lang.IllegalArgumentException, java.lang.IllegalStateException, - javax.ejb.EJBException { + @Override + public Timer createCalendarTimer(ScheduleExpression schedule) + throws IllegalArgumentException, IllegalStateException, EJBException { checkCreateTimerCallPermission(); checkScheduleExpression(schedule); @@ -199,45 +210,73 @@ public Timer createCalendarTimer(ScheduleExpression schedule) throws return createTimerInternal(schedule, tc); } - public Collection getTimers() throws IllegalStateException, EJBException { - + @Override + public Collection getTimers() + throws IllegalStateException, EJBException { + checkCallPermission(); - - Collection timerIds = new HashSet(); - if( ejbContext_.isTimedObject() ) { + Collection timerWrappers = new HashSet(); + timerWrappers.addAll(getTimers(persistentTimerService_)); + timerWrappers.addAll(getTimers(nonPersistentTimerService_)); + + return timerWrappers; + } + + private Collection getTimers(EJBTimerService timerService_) + throws IllegalStateException, EJBException { + Collection timerWrappers = new HashSet(); + + Collection timerIds = new HashSet(); + if (timerService_ != null && ejbContext_.isTimedObject()) { try { - timerIds = timerService_.getTimerIds - (ejbDescriptor_.getUniqueId(), getTimedObjectPrimaryKey()); - } catch(Exception fe) { + timerIds = timerService_.getTimerIds( + ejbDescriptor_.getUniqueId(), + getTimedObjectPrimaryKey() + ); + } catch (Exception fe) { EJBException ejbEx = new EJBException(); ejbEx.initCause(fe); - throw ejbEx; + throw ejbEx; } - } - - Collection timerWrappers = new HashSet(); - - for(Iterator iter = timerIds.iterator(); iter.hasNext();) { + } + for (Iterator iter = timerIds.iterator(); iter.hasNext();) { TimerPrimaryKey next = (TimerPrimaryKey) iter.next(); - timerWrappers.add( new TimerWrapper(next, timerService_) ); + timerWrappers.add(new TimerWrapper(next, timerService_)); } return timerWrappers; } @Override - public Collection getAllTimers() throws IllegalStateException, EJBException { + public Collection getAllTimers() + throws IllegalStateException, EJBException { checkCallPermission(); Collection timerWrappers = new HashSet(); Collection containerIds = ejbDescriptor_.getEjbBundleDescriptor().getDescriptorIds(); - Collection timerIds = - timerService_.getTimerIds(containerIds); - for (TimerPrimaryKey timerPrimaryKey : timerIds) { - timerWrappers.add(new TimerWrapper(timerPrimaryKey, timerService_)); + timerWrappers.addAll(getAllTimers(persistentTimerService_, containerIds)); + timerWrappers.addAll(getAllTimers(nonPersistentTimerService_, containerIds)); + + return timerWrappers; + } + + public Collection getAllTimers( + EJBTimerService timerService_, + Collection containerIds + ) throws IllegalStateException, EJBException { + + Collection timerWrappers = new HashSet(); + + if (timerService_ != null) { + Collection timerIds + = timerService_.getTimerIds(containerIds); + for (TimerPrimaryKey timerId : timerIds) { + timerWrappers.add(new TimerWrapper(timerId, persistentTimerService_)); + } } + return timerWrappers; } @@ -271,40 +310,48 @@ private void checkCallPermission() ejbContext_.checkTimerServiceMethodAccess(); } - private Timer createTimerInternal(long initialDuration, long intervalDuration, - Serializable info) - throws IllegalArgumentException, IllegalStateException, EJBException { + private Timer createTimerInternal( + long initialDuration, + long intervalDuration, + Serializable info + ) throws IllegalArgumentException, IllegalStateException, EJBException { - TimerConfig tc = new TimerConfig(); - tc.setInfo(info); + TimerConfig timerConfig = new TimerConfig(); + timerConfig.setInfo(info); - return createTimerInternal(initialDuration, intervalDuration, tc); + return createTimerInternal(initialDuration, intervalDuration, timerConfig); } - private Timer createTimerInternal(long initialDuration, long intervalDuration, - TimerConfig tc) - throws IllegalArgumentException, IllegalStateException, EJBException { + private Timer createTimerInternal( + long initialDuration, + long intervalDuration, + TimerConfig timerConfig + ) throws IllegalArgumentException, IllegalStateException, EJBException { checkIntervalDuration(intervalDuration); + EJBTimerService timerService_ + = timerConfig == null || timerConfig.isPersistent() ? persistentTimerService_ : nonPersistentTimerService_; TimerPrimaryKey timerId = null; try { timerId = timerService_.createTimer(ejbDescriptor_.getUniqueId(), ejbDescriptor_.getApplication().getUniqueId(), getTimedObjectPrimaryKey(), - initialDuration, intervalDuration, tc); - } catch(CreateException ce) { + initialDuration, intervalDuration, timerConfig); + } catch (CreateException ce) { EJBException ejbEx = new EJBException(); ejbEx.initCause(ce); - throw ejbEx; + throw ejbEx; } return new TimerWrapper(timerId, timerService_); } - private Timer createTimerInternal(Date initialExpiration, long intervalDuration, - Serializable info) - throws IllegalArgumentException, IllegalStateException, EJBException { + private Timer createTimerInternal( + Date initialExpiration, + long intervalDuration, + Serializable info + ) throws IllegalArgumentException, IllegalStateException, EJBException { TimerConfig tc = new TimerConfig(); tc.setInfo(info); @@ -312,40 +359,48 @@ private Timer createTimerInternal(Date initialExpiration, long intervalDuration, return createTimerInternal(initialExpiration, intervalDuration, tc); } - private Timer createTimerInternal(Date initialExpiration, long intervalDuration, - TimerConfig tc) - throws IllegalArgumentException, IllegalStateException, EJBException { + private Timer createTimerInternal( + Date initialExpiration, + long intervalDuration, + TimerConfig timerConfig + ) throws IllegalArgumentException, IllegalStateException, EJBException { checkIntervalDuration(intervalDuration); + EJBTimerService timerService_ + = timerConfig == null || timerConfig.isPersistent() ? persistentTimerService_ : nonPersistentTimerService_; TimerPrimaryKey timerId = null; try { timerId = timerService_.createTimer(ejbDescriptor_.getUniqueId(), ejbDescriptor_.getApplication().getUniqueId(), getTimedObjectPrimaryKey(), - initialExpiration, intervalDuration, tc); - } catch(CreateException ce) { + initialExpiration, intervalDuration, timerConfig); + } catch (CreateException ce) { EJBException ejbEx = new EJBException(); ejbEx.initCause(ce); - throw ejbEx; + throw ejbEx; } return new TimerWrapper(timerId, timerService_); } - private Timer createTimerInternal(ScheduleExpression schedule, TimerConfig tc) - throws IllegalArgumentException, IllegalStateException, EJBException { + private Timer createTimerInternal( + ScheduleExpression schedule, + TimerConfig timerConfig + ) throws IllegalArgumentException, IllegalStateException, EJBException { + EJBTimerService timerService_ + = timerConfig == null || timerConfig.isPersistent() ? persistentTimerService_ : nonPersistentTimerService_; TimerPrimaryKey timerId = null; try { timerId = timerService_.createTimer(ejbDescriptor_.getUniqueId(), ejbDescriptor_.getApplication().getUniqueId(), getTimedObjectPrimaryKey(), - new EJBTimerSchedule(schedule), tc); - } catch(CreateException ce) { + new EJBTimerSchedule(schedule), timerConfig); + } catch (CreateException ce) { EJBException ejbEx = new EJBException(); ejbEx.initCause(ce); - throw ejbEx; + throw ejbEx; } return new TimerWrapper(timerId, timerService_); diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/NonPersistentEJBTimerService.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/NonPersistentEJBTimerService.java new file mode 100644 index 00000000000..682caf2a713 --- /dev/null +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/NonPersistentEJBTimerService.java @@ -0,0 +1,224 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2011-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +// Portions Copyright [2018] [Payara Foundation and/or its affiliates] + +package com.sun.ejb.containers; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; +import javax.ejb.FinderException; + +public class NonPersistentEJBTimerService extends EJBTimerService { + + protected NonPersistentEJBTimerService() throws Exception { + super(); + } + + @Override + public boolean isPersistent() { + return false; + } + + @Override + public int migrateTimers(String fromOwnerId) { + throw new IllegalStateException("Non-persistent timers cannot be migrated"); + } + + @Override + protected void resetEJBTimers(String target) { + // Nothing to do + } + + @Override + protected void cancelTimer(TimerPrimaryKey timerId) + throws FinderException, Exception { + cancelNonPersistentTimer(timerId); + } + + @Override + protected void cancelTimersByKey(long containerId, Object primaryKey) { + //Do nothing or throw an exception ? + } + + @Override + protected Date getNextTimeout(TimerPrimaryKey timerId) throws FinderException { + + RuntimeTimerState rt = getNonPersistentTimer(timerId); + if (rt != null) { + return _getNextTimeout(rt); + } + + throw new FinderException("Timer does not exist"); + } + + + @Override + protected void resetLastExpiration( + TimerPrimaryKey timerId, + RuntimeTimerState timerState) { + // Do nothing + } + + @Override + protected boolean isValidTimerForThisServer( + TimerPrimaryKey timerId, + RuntimeTimerState timerState) { + return true; + } + + protected boolean cancelNonPersistentTimer(TimerPrimaryKey timerId) + throws FinderException, Exception { + + RuntimeTimerState rt = getNonPersistentTimerState(timerId); + if (rt != null) { + if (rt.isCancelled()) { + // already cancelled or removed + return true; + } + + cancelTimerSynchronization(null, timerId, + rt.getContainerId(), ownerIdOfThisServer_, false); + } + + return (rt != null); + } + + + private RuntimeTimerState getNonPersistentTimerState(TimerPrimaryKey timerId) { + return timerCache_.getNonPersistentTimerState(timerId); + } + + // Returns a Set of active non-persistent timer ids for this server + public Set getNonPersistentActiveTimerIdsByThisServer() { + return timerCache_.getNonPersistentActiveTimerIdsByThisServer(); + } + + protected RuntimeTimerState getNonPersistentTimer(TimerPrimaryKey timerId) + throws FinderException { + RuntimeTimerState rt = getNonPersistentTimerState(timerId); + if (rt != null) { + if (rt.isCancelled()) { + // The timer has been cancelled within this tx. + throw new FinderException("Non-persistent timer " + timerId + + " does not exist"); + } + } + + return rt; + } + + protected EJBTimerSchedule getTimerSchedule(TimerPrimaryKey timerId) throws FinderException { + + // Check non-persistent timers first + EJBTimerSchedule ts = null; + + RuntimeTimerState rt = getNonPersistentTimer(timerId); + if (rt != null) { + ts = rt.getTimerSchedule(); + } + + return ts; + } + + @Override + protected boolean isPersistent(TimerPrimaryKey timerId) throws FinderException { + + // Check non-persistent timers only + RuntimeTimerState rt = getNonPersistentTimer(timerId); + return rt == null; + } + + @Override + protected boolean timerExists(TimerPrimaryKey timerId) { + boolean exists = false; + + // Check non-persistent timers only + RuntimeTimerState rt = getNonPersistentTimerState(timerId); + if (rt != null) { + exists = rt.isActive(); + } + + return exists; + } + + @Override + protected Serializable getInfo(TimerPrimaryKey timerId) throws FinderException { + + // Check non-persistent timers only + RuntimeTimerState rt = getNonPersistentTimer(timerId); + if (rt != null) { + return rt.getInfo(); + } + + return null; + } + + @Override + protected Collection getTimerIds(Collection containerIds) { + Collection timerIds = new HashSet<>(); + for (long containerId : containerIds) { + timerIds.addAll(timerCache_.getNonPersistentActiveTimerIdsForContainer(containerId)); + } + return timerIds; + } + + @Override + protected Collection getTimerIds( + long containerId, + Object timedObjectPrimaryKey) { + + Collection timerIdsForTimedObject = new HashSet<>(); + + // Add active non-persistent timer ids + timerIdsForTimedObject.addAll( + timerCache_.getNonPersistentActiveTimerIdsForContainer(containerId)); + + return timerIdsForTimedObject; + } + + @Override + protected void stopTimers(long containerId) { + stopTimers(timerCache_.getNonPersistentTimerIdsForContainer(containerId)); + } + +} diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/SessionContextImpl.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/SessionContextImpl.java index 7bf7c5c38cd..aaff8c8c52c 100644 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/SessionContextImpl.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/SessionContextImpl.java @@ -37,6 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ +// Portions Copyright [2018] [Payara Foundation and/or its affiliates] package com.sun.ejb.containers; @@ -188,8 +189,7 @@ public TimerService getTimerService() throws IllegalStateException { throw new IllegalStateException("Operation not allowed"); } - EJBTimerService timerService = EJBTimerService.getValidEJBTimerService(); - return new EJBTimerServiceWrapper(timerService, this); + return EJBTimerService.getEJBTimerServiceWrapper(this); } @Override diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/SingletonContextImpl.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/SingletonContextImpl.java index 6e465c089c5..6492ac9556c 100644 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/SingletonContextImpl.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/SingletonContextImpl.java @@ -37,13 +37,11 @@ * only if the new code is made subject to such option by the copyright * holder. */ +// Portions Copyright [2018] [Payara Foundation and/or its affiliates] package com.sun.ejb.containers; -import org.glassfish.api.invocation.ComponentInvocation; - import javax.ejb.TimerService; -import javax.ejb.EJBException; import javax.transaction.TransactionManager; import javax.transaction.Status; import javax.naming.InitialContext; @@ -79,9 +77,7 @@ public TimerService getTimerService() throws IllegalStateException { throw new IllegalStateException("Operation not allowed"); } - EJBTimerService timerService = EJBTimerService.getValidEJBTimerService(); - return new EJBTimerServiceWrapper(timerService, this); - + return EJBTimerService.getEJBTimerServiceWrapper(this); } @Override diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/TimerServiceNamingProxy.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/TimerServiceNamingProxy.java index 22052911e53..40b0d2cab53 100644 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/TimerServiceNamingProxy.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/TimerServiceNamingProxy.java @@ -37,6 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ +// Portions Copyright [2018] [Payara Foundation and/or its affiliates] package com.sun.ejb.containers; @@ -62,9 +63,9 @@ public class TimerServiceNamingProxy implements NamedNamingObjectProxy { - static final String EJB_TIMER_SERVICE - = "java:comp/TimerService"; + static final String EJB_TIMER_SERVICE = "java:comp/TimerService"; + @Override public Object handle(String name) throws NamingException { if (EJB_TIMER_SERVICE.equals(name)) { @@ -83,22 +84,16 @@ private Object getTimerServiceWrapper() { ComponentInvocation currentInv = EjbContainerUtilImpl.getInstance().getCurrentInvocation(); - if(currentInv == null) { + if (currentInv == null) { throw new IllegalStateException("no current invocation"); - } else if (currentInv.getInvocationType() != - ComponentInvocation.ComponentInvocationType.EJB_INVOCATION) { - throw new IllegalStateException - ("Illegal invocation type for EJB Context : " - + currentInv.getInvocationType()); + } else if (currentInv.getInvocationType() + != ComponentInvocation.ComponentInvocationType.EJB_INVOCATION) { + throw new IllegalStateException("Illegal invocation type for EJB Context : " + + currentInv.getInvocationType()); } - EJBTimerService ejbTimerService = EJBTimerService.getEJBTimerService(); - if( ejbTimerService == null ) { - throw new IllegalStateException("EJB Timer Service not " + - "available"); - } - - return new EJBTimerServiceWrapper - (ejbTimerService, (EJBContextImpl) ((EjbInvocation) currentInv).context); + return EJBTimerService.getEJBTimerServiceWrapper( + (EJBContextImpl) ((EjbInvocation) currentInv).context + ); } } diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/TimerWrapper.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/TimerWrapper.java index 50867b385d9..1ac56fee073 100644 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/TimerWrapper.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/TimerWrapper.java @@ -37,12 +37,12 @@ * only if the new code is made subject to such option by the copyright * holder. */ +// Portions Copyright [2018] [Payara Foundation and/or its affiliates] package com.sun.ejb.containers; import java.util.Date; import java.io.Serializable; - import javax.ejb.NoSuchObjectLocalException; import javax.ejb.NoMoreTimeoutsException; import javax.ejb.EJBException; @@ -50,12 +50,9 @@ import javax.ejb.TimerHandle; import javax.ejb.ScheduleExpression; import javax.ejb.FinderException; - - import com.sun.enterprise.container.common.spi.util.IndirectlySerializable; import com.sun.enterprise.container.common.spi.util.SerializableObjectFactory; import org.glassfish.api.invocation.ComponentInvocation; - import com.sun.ejb.EjbInvocation; import com.sun.ejb.ComponentContext; @@ -81,6 +78,7 @@ public class TimerWrapper /* * Implementations of javax.ejb.Timer methods */ + @Override public void cancel() throws IllegalStateException, NoSuchObjectLocalException, EJBException { @@ -96,6 +94,7 @@ public void cancel() } + @Override public long getTimeRemaining() throws IllegalStateException, NoMoreTimeoutsException, NoSuchObjectLocalException { @@ -107,6 +106,7 @@ public long getTimeRemaining() return (timeRemaining > 0) ? timeRemaining : 0; } + @Override public Date getNextTimeout() throws IllegalStateException, NoMoreTimeoutsException, NoSuchObjectLocalException { @@ -127,6 +127,7 @@ public Date getNextTimeout() return nextTimeout; } + @Override public Serializable getInfo() throws IllegalStateException, NoSuchObjectLocalException { @@ -143,6 +144,7 @@ public Serializable getInfo() return info; } + @Override public TimerHandle getHandle() throws IllegalStateException, NoSuchObjectLocalException { @@ -156,11 +158,12 @@ public TimerHandle getHandle() throw new NoSuchObjectLocalException("timer no longer exists"); } - return new TimerHandleImpl(timerId_); + return new TimerHandleImpl(timerId_, timerService_); } - public ScheduleExpression getSchedule() throws java.lang.IllegalStateException, - javax.ejb.NoSuchObjectLocalException, javax.ejb.EJBException { + @Override + public ScheduleExpression getSchedule() + throws IllegalStateException, NoSuchObjectLocalException, EJBException { checkCallPermission(); ScheduleExpression schedule; @@ -178,6 +181,7 @@ public ScheduleExpression getSchedule() throws java.lang.IllegalStateException, return schedule; } + @Override public boolean isCalendarTimer() throws java.lang.IllegalStateException, javax.ejb.NoSuchObjectLocalException, javax.ejb.EJBException { @@ -191,6 +195,7 @@ public boolean isCalendarTimer() throws java.lang.IllegalStateException, } + @Override public boolean isPersistent() throws java.lang.IllegalStateException, javax.ejb.NoSuchObjectLocalException, javax.ejb.EJBException { @@ -202,6 +207,7 @@ public boolean isPersistent() throws java.lang.IllegalStateException, } } + @Override public boolean equals(Object o) { boolean equal = false; if(o instanceof TimerWrapper) { @@ -211,10 +217,12 @@ public boolean equals(Object o) { return equal; } + @Override public int hashCode() { return timerId_.hashCode(); } + @Override public String toString() { return "Timer " + timerId_; } @@ -229,16 +237,9 @@ private static void checkCallPermission() throws IllegalStateException { // Can't store a static ref because in embedded container it can be // changed by server restart EjbContainerUtil ejbContainerUtil = EjbContainerUtilImpl.getInstance(); - EJBTimerService timerService = EJBTimerService.getEJBTimerService(); - if( timerService == null ) { - throw new IllegalStateException - ("EJBTimerService is not available"); - } - ComponentInvocation inv = ejbContainerUtil.getCurrentInvocation(); if (inv == null) { - throw new IllegalStateException - ("Invocation cannot be null"); + throw new IllegalStateException("Invocation cannot be null"); } ComponentInvocation.ComponentInvocationType invType = inv.getInvocationType(); if( invType == ComponentInvocation.ComponentInvocationType.EJB_INVOCATION ) { @@ -251,8 +252,9 @@ private static void checkCallPermission() throws IllegalStateException { } } + @Override public SerializableObjectFactory getSerializableObjectFactory() { - return new SerializedTimerWrapper(timerId_); + return new SerializedTimerWrapper(timerId_, timerService_); } /** @@ -261,36 +263,44 @@ public SerializableObjectFactory getSerializableObjectFactory() { * we know it started as a TimerWrapper instead of a TimerHandle. */ public static class SerializedTimerWrapper - implements SerializableObjectFactory - { + implements SerializableObjectFactory { + private TimerPrimaryKey timerId_; + private EJBTimerService timerService_; + SerializedTimerWrapper() {} - SerializedTimerWrapper(TimerPrimaryKey timerId) { + SerializedTimerWrapper(TimerPrimaryKey timerId, EJBTimerService timerService) { timerId_ = timerId; + timerService_ = timerService; } /** - * When deserializing the timer wrapper create a TimerWrapper object. - * Check if the record is valid only when making calls on the object. + * When deserializing the timer wrapper create a TimerWrapper + * object. Check if the record is valid only when making calls on the + * object. + * + * @param appUniqueId + * @return */ + @Override public Object createObject(long appUniqueId) throws EJBException { // Can't store a static ref because in embedded container it can be // changed by server restart - EJBTimerService timerService = EJBTimerService.getEJBTimerService(); - TimerWrapper timer = new TimerWrapper(timerId_, timerService); - - return timer; + return new TimerWrapper(timerId_, timerService_); } } private static class TimerHandleImpl implements TimerHandle { - private TimerPrimaryKey timerId_; + private final TimerPrimaryKey timerId_; + + private final EJBTimerService timerService_; - public TimerHandleImpl(TimerPrimaryKey timerId) { + public TimerHandleImpl(TimerPrimaryKey timerId, EJBTimerService timerService) { timerId_ = timerId; + timerService_ = timerService; } /** @@ -298,6 +308,7 @@ public TimerHandleImpl(TimerPrimaryKey timerId) { * very defensively, since handle use might be attempted from * outside of EJB container. */ + @Override public Timer getTimer() throws IllegalStateException, NoSuchObjectLocalException, EJBException { TimerWrapper timer = null; @@ -310,7 +321,7 @@ public Timer getTimer() throws IllegalStateException, // Make sure use of timer service methods are allowed checkCallPermission(); - timer = getTimerInternal(timerId_); + timer = getTimerInternal(); } else { throw new IllegalStateException @@ -320,17 +331,17 @@ public Timer getTimer() throws IllegalStateException, return timer; } - private TimerWrapper getTimerInternal(TimerPrimaryKey timerId) + private TimerWrapper getTimerInternal() throws NoSuchObjectLocalException, EJBException { TimerWrapper timer = null; - // Can't store a static ref because in embedded container it can be + // Can't store a static ref because in embedded container it can be // changed by server restart - EJBTimerService timerService = EJBTimerService.getEJBTimerService(); + // Timer handles are only available for persistent timers. - if( timerService != null ) { - if( timerService.timerExists(timerId) ) { - timer = new TimerWrapper(timerId, timerService); + if( timerService_ != null ) { + if( timerService_.timerExists(timerId_) ) { + timer = new TimerWrapper(timerId_, timerService_); } else { throw new NoSuchObjectLocalException ("timer is no longer active"); diff --git a/appserver/ejb/ejb-container/src/main/java/org/glassfish/ejb/admin/cli/ListTimers.java b/appserver/ejb/ejb-container/src/main/java/org/glassfish/ejb/admin/cli/ListTimers.java index 14bdac99b7a..16ec308c4f5 100644 --- a/appserver/ejb/ejb-container/src/main/java/org/glassfish/ejb/admin/cli/ListTimers.java +++ b/appserver/ejb/ejb-container/src/main/java/org/glassfish/ejb/admin/cli/ListTimers.java @@ -129,18 +129,16 @@ public void execute(AdminCommandContext context) { private String[] listTimers( String[] serverIds ) { String[] result = new String[serverIds.length]; - if (EJBTimerService.isEJBTimerServiceLoaded()) { - EJBTimerService ejbTimerService = EJBTimerService.getEJBTimerService(); + if (EJBTimerService.isPersistentTimerServiceLoaded()) { + EJBTimerService ejbTimerService = EJBTimerService.getPersistentTimerService(); if (ejbTimerService != null) { - result = ejbTimerService.listTimers( serverIds ); + result = ejbTimerService.listTimers(serverIds); } - } else { - //FIXME: Should throw IllegalStateException - for (int i=0; i Date: Fri, 19 Oct 2018 17:32:14 +0530 Subject: [PATCH 2/7] PAYARA-2905 Non-Persistent TimerService API update in Hazelcast Store --- .../com/sun/ejb/containers/EJBTimerService.java | 3 ++- .../com/sun/ejb/timers/TimerWelcomeServlet.java | 17 +++++++++++------ .../timer/hazelcast/HazelcastTimerStore.java | 11 ++++++----- .../entitybean/container/EntityContextImpl.java | 10 ++-------- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java index 501b239cb4e..d60bd1fa08a 100755 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java @@ -81,6 +81,7 @@ import fish.payara.nucleus.healthcheck.stuck.StuckThreadsStore; import static java.util.Objects.isNull; import static java.util.logging.Level.INFO; +import javax.ejb.EJBContext; import org.glassfish.internal.api.Globals; /* @@ -189,7 +190,7 @@ protected EJBTimerService() throws Exception { initProperties(); } - public static EJBTimerServiceWrapper getEJBTimerServiceWrapper(EJBContextImpl ejbContext) { + public static EJBTimerServiceWrapper getEJBTimerServiceWrapper(EJBContext ejbContext) { if (isNull(persistentTimerService) && isNull(nonPersistentTimerService)) { throw new IllegalStateException("EJB Timer Service not available"); } diff --git a/appserver/ejb/ejb-timer-service-app/src/main/java/com/sun/ejb/timers/TimerWelcomeServlet.java b/appserver/ejb/ejb-timer-service-app/src/main/java/com/sun/ejb/timers/TimerWelcomeServlet.java index 7cfee69de2f..c8cc9244a3b 100644 --- a/appserver/ejb/ejb-timer-service-app/src/main/java/com/sun/ejb/timers/TimerWelcomeServlet.java +++ b/appserver/ejb/ejb-timer-service-app/src/main/java/com/sun/ejb/timers/TimerWelcomeServlet.java @@ -37,11 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ - -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ +// Portions Copyright [2018] [Payara Foundation and/or its affiliates] package com.sun.ejb.timers; @@ -55,6 +51,7 @@ import org.glassfish.ejb.persistent.timer.TimerLocal; import com.sun.ejb.containers.EJBTimerService; +import com.sun.ejb.containers.NonPersistentEJBTimerService; /** * @@ -88,7 +85,15 @@ protected void processRequest(HttpServletRequest request, HttpServletResponse re Set persistenttimers = timer.findActiveTimersOwnedByThisServer(); // Non-persistent timers get directly from the service - EJBTimerService ejbTimerService = EJBTimerService.getEJBTimerService(); + NonPersistentEJBTimerService ejbTimerService = null; + if (EJBTimerService.isPersistentTimerServiceLoaded()) { + ejbTimerService = (NonPersistentEJBTimerService) EJBTimerService.getPersistentTimerService(); + } else if (EJBTimerService.isPersistentTimerServiceLoaded()) { + ejbTimerService = (NonPersistentEJBTimerService) EJBTimerService.getNonPersistentTimerService(); + } + if(ejbTimerService == null) { + throw new IllegalStateException("EJBTimer Service is not available"); + } Set nonpersistenttimers = ejbTimerService.getNonPersistentActiveTimerIdsByThisServer(); int persistentsize = persistenttimers.size(); int nonpersistentsize = nonpersistenttimers.size(); diff --git a/appserver/payara-appserver-modules/hazelcast-ejb-timer/src/main/java/fish/payara/ejb/timer/hazelcast/HazelcastTimerStore.java b/appserver/payara-appserver-modules/hazelcast-ejb-timer/src/main/java/fish/payara/ejb/timer/hazelcast/HazelcastTimerStore.java index d81d825a6d0..e3de69cba85 100644 --- a/appserver/payara-appserver-modules/hazelcast-ejb-timer/src/main/java/fish/payara/ejb/timer/hazelcast/HazelcastTimerStore.java +++ b/appserver/payara-appserver-modules/hazelcast-ejb-timer/src/main/java/fish/payara/ejb/timer/hazelcast/HazelcastTimerStore.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2016-2017 Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) 2016-2018 Payara Foundation and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -43,6 +43,7 @@ import com.sun.ejb.containers.BaseContainer; import com.sun.ejb.containers.EJBTimerSchedule; import com.sun.ejb.containers.EJBTimerService; +import com.sun.ejb.containers.NonPersistentEJBTimerService; import com.sun.ejb.containers.RuntimeTimerState; import com.sun.ejb.containers.TimerPrimaryKey; import com.sun.enterprise.deployment.MethodDescriptor; @@ -72,7 +73,7 @@ * * @author steve */ -public class HazelcastTimerStore extends EJBTimerService { +public class HazelcastTimerStore extends NonPersistentEJBTimerService { private static final String EJB_TIMER_CACHE_NAME = "HZEjbTmerCache"; private static final String EJB_TIMER_CONTAINER_CACHE_NAME = "HZEjbTmerContainerCache"; @@ -88,7 +89,7 @@ public class HazelcastTimerStore extends EJBTimerService { static void init(HazelcastCore core) { try { - EJBTimerService.setEJBTimerService(new HazelcastTimerStore(core)); + EJBTimerService.setPersistentTimerService(new HazelcastTimerStore(core)); } catch (Exception ex) { Logger.getLogger(HazelcastTimerStore.class.getName()).log(Level.WARNING, "Problem when initialising Timer Store", ex); } @@ -203,7 +204,7 @@ protected void cancelTimer(TimerPrimaryKey timerId) throws FinderException, Exce if (localTx) { tm.begin(); } - EJBTimerService.getEJBTimerService().cancelTimerSynchronization(null, timerId, + super.cancelTimerSynchronization(null, timerId, timer.getContainerId(), timer.getOwnerId()); if (localTx) { tm.commit(); @@ -1182,7 +1183,7 @@ private boolean restoreEJBTimers() { } } catch (Exception ex) { // Problem accessing timer service so disable it. - EJBTimerService.setEJBTimerService(null); + EJBTimerService.setPersistentTimerService(null); logger.log(Level.WARNING, "ejb.timer_service_init_error", ex); diff --git a/appserver/persistence/entitybean-container/src/main/java/org/glassfish/persistence/ejb/entitybean/container/EntityContextImpl.java b/appserver/persistence/entitybean-container/src/main/java/org/glassfish/persistence/ejb/entitybean/container/EntityContextImpl.java index 75ee8225864..0aed45a4f6c 100644 --- a/appserver/persistence/entitybean-container/src/main/java/org/glassfish/persistence/ejb/entitybean/container/EntityContextImpl.java +++ b/appserver/persistence/entitybean-container/src/main/java/org/glassfish/persistence/ejb/entitybean/container/EntityContextImpl.java @@ -37,22 +37,17 @@ * only if the new code is made subject to such option by the copyright * holder. */ +// Portions Copyright [2018] [Payara Foundation and/or its affiliates] package org.glassfish.persistence.ejb.entitybean.container; -import java.rmi.RemoteException; import java.lang.reflect.Method; import javax.ejb.*; - import org.glassfish.api.invocation.ComponentInvocation; - import com.sun.ejb.EjbInvocation; import com.sun.ejb.containers.EJBContextImpl; import com.sun.ejb.containers.BaseContainer; -import com.sun.ejb.containers.EJBObjectImpl; -import com.sun.ejb.containers.EJBLocalObjectImpl; import com.sun.ejb.containers.EJBTimerService; -import com.sun.ejb.containers.EJBTimerServiceWrapper; import org.glassfish.persistence.ejb.entitybean.container.spi.CascadeDeleteNotifier; /** @@ -187,8 +182,7 @@ public TimerService getTimerService() throws IllegalStateException { throw new IllegalStateException("Operation not allowed"); } - EJBTimerService timerService = EJBTimerService.getValidEJBTimerService(); - return new EJBTimerServiceWrapper(timerService, (EntityContext) this); + return EJBTimerService.getEJBTimerServiceWrapper(this); } protected void checkAccessToCallerSecurity() From 075adbc38446854aac9042d85aa33bca0deb1e01 Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Fri, 19 Oct 2018 19:47:36 +0530 Subject: [PATCH 3/7] PAYARA-2905 typo fix in sample timer service app --- .../src/main/java/com/sun/ejb/timers/TimerWelcomeServlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appserver/ejb/ejb-timer-service-app/src/main/java/com/sun/ejb/timers/TimerWelcomeServlet.java b/appserver/ejb/ejb-timer-service-app/src/main/java/com/sun/ejb/timers/TimerWelcomeServlet.java index c8cc9244a3b..333daaee8d7 100644 --- a/appserver/ejb/ejb-timer-service-app/src/main/java/com/sun/ejb/timers/TimerWelcomeServlet.java +++ b/appserver/ejb/ejb-timer-service-app/src/main/java/com/sun/ejb/timers/TimerWelcomeServlet.java @@ -88,7 +88,7 @@ protected void processRequest(HttpServletRequest request, HttpServletResponse re NonPersistentEJBTimerService ejbTimerService = null; if (EJBTimerService.isPersistentTimerServiceLoaded()) { ejbTimerService = (NonPersistentEJBTimerService) EJBTimerService.getPersistentTimerService(); - } else if (EJBTimerService.isPersistentTimerServiceLoaded()) { + } else if (EJBTimerService.isNonPersistentTimerServiceLoaded()) { ejbTimerService = (NonPersistentEJBTimerService) EJBTimerService.getNonPersistentTimerService(); } if(ejbTimerService == null) { From 73de961d4bab3caf5ff484d362dd30f6af811b9c Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Fri, 19 Oct 2018 19:59:52 +0530 Subject: [PATCH 4/7] PAYARA-2905 Revert getEJBTimerServiceWrapper method signature --- .../src/main/java/com/sun/ejb/containers/EJBTimerService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java index d60bd1fa08a..501b239cb4e 100755 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java @@ -81,7 +81,6 @@ import fish.payara.nucleus.healthcheck.stuck.StuckThreadsStore; import static java.util.Objects.isNull; import static java.util.logging.Level.INFO; -import javax.ejb.EJBContext; import org.glassfish.internal.api.Globals; /* @@ -190,7 +189,7 @@ protected EJBTimerService() throws Exception { initProperties(); } - public static EJBTimerServiceWrapper getEJBTimerServiceWrapper(EJBContext ejbContext) { + public static EJBTimerServiceWrapper getEJBTimerServiceWrapper(EJBContextImpl ejbContext) { if (isNull(persistentTimerService) && isNull(nonPersistentTimerService)) { throw new IllegalStateException("EJB Timer Service not available"); } From 501ea47e290353234aa979c3bbd87d47735fcebb Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Sat, 20 Oct 2018 23:09:24 +0530 Subject: [PATCH 5/7] PAYARA-2905 Polish Non-Persistent timer service --- .../sun/ejb/containers/EJBTimerService.java | 3 ++ .../containers/EJBTimerServiceWrapper.java | 6 +-- .../NonPersistentEJBTimerService.java | 40 ++++++++++++------- .../glassfish/ejb/admin/cli/ListTimers.java | 18 ++++----- .../timer/PersistentEJBTimerService.java | 1 + .../sun/ejb/timers/TimerWelcomeServlet.java | 34 ++++++++-------- 6 files changed, 58 insertions(+), 44 deletions(-) diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java index 501b239cb4e..c1ad691c2cf 100755 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java @@ -375,6 +375,9 @@ protected abstract void resetLastExpiration( RuntimeTimerState timerState ); + + public abstract Set getNonPersistentActiveTimerIdsByThisServer(); + private static synchronized void initNonPersistentTimerService(String target, boolean force) { if(nonPersistentTimerService == null) { try { diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerServiceWrapper.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerServiceWrapper.java index 0cf666e6e72..70836c13fa1 100644 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerServiceWrapper.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerServiceWrapper.java @@ -227,7 +227,7 @@ private Collection getTimers(EJBTimerService timerService_) throws IllegalStateException, EJBException { Collection timerWrappers = new HashSet(); - Collection timerIds = new HashSet(); + Collection timerIds = new HashSet<>(); if (timerService_ != null && ejbContext_.isTimedObject()) { try { timerIds = timerService_.getTimerIds( @@ -240,8 +240,8 @@ private Collection getTimers(EJBTimerService timerService_) throw ejbEx; } } - for (Iterator iter = timerIds.iterator(); iter.hasNext();) { - TimerPrimaryKey next = (TimerPrimaryKey) iter.next(); + for (Iterator iter = timerIds.iterator(); iter.hasNext();) { + TimerPrimaryKey next = iter.next(); timerWrappers.add(new TimerWrapper(next, timerService_)); } diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/NonPersistentEJBTimerService.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/NonPersistentEJBTimerService.java index 682caf2a713..8f0ddafb9ab 100644 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/NonPersistentEJBTimerService.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/NonPersistentEJBTimerService.java @@ -41,11 +41,13 @@ package com.sun.ejb.containers; +import static com.sun.ejb.containers.EJBTimerService.logger; import java.io.Serializable; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.Set; +import static java.util.logging.Level.WARNING; import javax.ejb.FinderException; public class NonPersistentEJBTimerService extends EJBTimerService { @@ -64,11 +66,6 @@ public int migrateTimers(String fromOwnerId) { throw new IllegalStateException("Non-persistent timers cannot be migrated"); } - @Override - protected void resetEJBTimers(String target) { - // Nothing to do - } - @Override protected void cancelTimer(TimerPrimaryKey timerId) throws FinderException, Exception { @@ -77,11 +74,18 @@ protected void cancelTimer(TimerPrimaryKey timerId) @Override protected void cancelTimersByKey(long containerId, Object primaryKey) { - //Do nothing or throw an exception ? + Collection timerIds = getTimerIds(containerId, primaryKey); + for(TimerPrimaryKey timerId : timerIds) { + try { + cancelTimer(timerId); + } catch (Exception ex) { + logger.log(WARNING, "Cannot cancel non-persistent timer " + timerId, ex); + } + } } @Override - protected Date getNextTimeout(TimerPrimaryKey timerId) throws FinderException { + protected Date getNextTimeout(TimerPrimaryKey timerId) throws FinderException { RuntimeTimerState rt = getNonPersistentTimer(timerId); if (rt != null) { @@ -91,14 +95,6 @@ protected Date getNextTimeout(TimerPrimaryKey timerId) throws FinderException { throw new FinderException("Timer does not exist"); } - - @Override - protected void resetLastExpiration( - TimerPrimaryKey timerId, - RuntimeTimerState timerState) { - // Do nothing - } - @Override protected boolean isValidTimerForThisServer( TimerPrimaryKey timerId, @@ -129,6 +125,7 @@ private RuntimeTimerState getNonPersistentTimerState(TimerPrimaryKey timerId) { } // Returns a Set of active non-persistent timer ids for this server + @Override public Set getNonPersistentActiveTimerIdsByThisServer() { return timerCache_.getNonPersistentActiveTimerIdsByThisServer(); } @@ -147,6 +144,7 @@ protected RuntimeTimerState getNonPersistentTimer(TimerPrimaryKey timerId) return rt; } + @Override protected EJBTimerSchedule getTimerSchedule(TimerPrimaryKey timerId) throws FinderException { // Check non-persistent timers first @@ -221,4 +219,16 @@ protected void stopTimers(long containerId) { stopTimers(timerCache_.getNonPersistentTimerIdsForContainer(containerId)); } + @Override + protected void resetEJBTimers(String target) { + // Do nothing + } + + @Override + protected void resetLastExpiration( + TimerPrimaryKey timerId, + RuntimeTimerState timerState) { + // Do nothing + } + } diff --git a/appserver/ejb/ejb-container/src/main/java/org/glassfish/ejb/admin/cli/ListTimers.java b/appserver/ejb/ejb-container/src/main/java/org/glassfish/ejb/admin/cli/ListTimers.java index 16ec308c4f5..2bb9554ec0d 100644 --- a/appserver/ejb/ejb-container/src/main/java/org/glassfish/ejb/admin/cli/ListTimers.java +++ b/appserver/ejb/ejb-container/src/main/java/org/glassfish/ejb/admin/cli/ListTimers.java @@ -48,6 +48,7 @@ import com.sun.ejb.containers.EJBTimerService; import com.sun.ejb.containers.EjbContainerUtil; +import static java.util.Objects.nonNull; import org.glassfish.api.ActionReport; import org.glassfish.api.I18n; import org.glassfish.api.Param; @@ -127,18 +128,17 @@ public void execute(AdminCommandContext context) { } } - private String[] listTimers( String[] serverIds ) { + private String[] listTimers(String[] serverIds) { String[] result = new String[serverIds.length]; + + EJBTimerService ejbTimerService = null; if (EJBTimerService.isPersistentTimerServiceLoaded()) { - EJBTimerService ejbTimerService = EJBTimerService.getPersistentTimerService(); - if (ejbTimerService != null) { - result = ejbTimerService.listTimers(serverIds); - } + ejbTimerService = EJBTimerService.getPersistentTimerService(); } else if (EJBTimerService.isNonPersistentTimerServiceLoaded()) { - EJBTimerService ejbTimerService = EJBTimerService.getNonPersistentTimerService(); - if (ejbTimerService != null) { - result = ejbTimerService.listTimers(serverIds); - } + ejbTimerService = EJBTimerService.getNonPersistentTimerService(); + } + if (nonNull(ejbTimerService)) { + result = ejbTimerService.listTimers(serverIds); } return result; diff --git a/appserver/ejb/ejb-full-container/src/main/java/org/glassfish/ejb/persistent/timer/PersistentEJBTimerService.java b/appserver/ejb/ejb-full-container/src/main/java/org/glassfish/ejb/persistent/timer/PersistentEJBTimerService.java index 0219df99c41..d33cbf7cc41 100755 --- a/appserver/ejb/ejb-full-container/src/main/java/org/glassfish/ejb/persistent/timer/PersistentEJBTimerService.java +++ b/appserver/ejb/ejb-full-container/src/main/java/org/glassfish/ejb/persistent/timer/PersistentEJBTimerService.java @@ -299,6 +299,7 @@ public int migrateTimers(String fromOwnerId) { } //migrateTimers() + @Override public boolean isPersistent() { return true; } diff --git a/appserver/ejb/ejb-timer-service-app/src/main/java/com/sun/ejb/timers/TimerWelcomeServlet.java b/appserver/ejb/ejb-timer-service-app/src/main/java/com/sun/ejb/timers/TimerWelcomeServlet.java index 333daaee8d7..1033eb61853 100644 --- a/appserver/ejb/ejb-timer-service-app/src/main/java/com/sun/ejb/timers/TimerWelcomeServlet.java +++ b/appserver/ejb/ejb-timer-service-app/src/main/java/com/sun/ejb/timers/TimerWelcomeServlet.java @@ -42,7 +42,6 @@ package com.sun.ejb.timers; import java.io.*; -import java.net.*; import java.util.Set; import javax.servlet.*; @@ -51,7 +50,8 @@ import org.glassfish.ejb.persistent.timer.TimerLocal; import com.sun.ejb.containers.EJBTimerService; -import com.sun.ejb.containers.NonPersistentEJBTimerService; +import com.sun.ejb.containers.TimerPrimaryKey; +import static java.util.Objects.isNull; /** * @@ -82,30 +82,30 @@ protected void processRequest(HttpServletRequest request, HttpServletResponse re out.println("
"); // Persistent timers - Set persistenttimers = timer.findActiveTimersOwnedByThisServer(); + Set persistentTimers = timer.findActiveTimersOwnedByThisServer(); // Non-persistent timers get directly from the service - NonPersistentEJBTimerService ejbTimerService = null; + EJBTimerService ejbTimerService = null; if (EJBTimerService.isPersistentTimerServiceLoaded()) { - ejbTimerService = (NonPersistentEJBTimerService) EJBTimerService.getPersistentTimerService(); + ejbTimerService = EJBTimerService.getPersistentTimerService(); } else if (EJBTimerService.isNonPersistentTimerServiceLoaded()) { - ejbTimerService = (NonPersistentEJBTimerService) EJBTimerService.getNonPersistentTimerService(); + ejbTimerService = EJBTimerService.getNonPersistentTimerService(); } - if(ejbTimerService == null) { - throw new IllegalStateException("EJBTimer Service is not available"); + if(isNull(ejbTimerService)) { + throw new IllegalStateException("EJB Timer Service is not available"); } - Set nonpersistenttimers = ejbTimerService.getNonPersistentActiveTimerIdsByThisServer(); - int persistentsize = persistenttimers.size(); - int nonpersistentsize = nonpersistenttimers.size(); + Set nonPersistentTimers = ejbTimerService.getNonPersistentActiveTimerIdsByThisServer(); + int persistentTimerSize = persistentTimers.size(); + int nonPersistentTimerSize = nonPersistentTimers.size(); - out.println("There " + ((persistentsize == 1)? "is " : "are ") - + persistentsize - + " active persistent timer" + ((persistentsize == 1)? "" : "s") + out.println("There " + ((persistentTimerSize == 1)? "is " : "are ") + + persistentTimerSize + + " active persistent timer" + ((persistentTimerSize == 1)? "" : "s") + " on this container"); out.println("
"); - out.println("There " + ((nonpersistentsize == 1)? "is " : "are ") - + nonpersistentsize - + " active non-persistent timer" + ((nonpersistentsize == 1)? "" : "s") + out.println("There " + ((nonPersistentTimerSize == 1)? "is " : "are ") + + nonPersistentTimerSize + + " active non-persistent timer" + ((nonPersistentTimerSize == 1)? "" : "s") + " on this container"); out.println("
"); From 2c61b6dd6d61763da392766ce64742b7bdfefb35 Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Tue, 30 Oct 2018 23:54:14 +0530 Subject: [PATCH 6/7] PAYARA-2905 java.util.Objects method reverted --- .../src/main/java/com/sun/ejb/containers/EJBTimerService.java | 3 +-- .../src/main/java/org/glassfish/ejb/admin/cli/ListTimers.java | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java index c1ad691c2cf..1a86556f09e 100755 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBTimerService.java @@ -79,7 +79,6 @@ import fish.payara.notification.requesttracing.RequestTraceSpan; import fish.payara.nucleus.requesttracing.RequestTracingService; import fish.payara.nucleus.healthcheck.stuck.StuckThreadsStore; -import static java.util.Objects.isNull; import static java.util.logging.Level.INFO; import org.glassfish.internal.api.Globals; @@ -190,7 +189,7 @@ protected EJBTimerService() throws Exception { } public static EJBTimerServiceWrapper getEJBTimerServiceWrapper(EJBContextImpl ejbContext) { - if (isNull(persistentTimerService) && isNull(nonPersistentTimerService)) { + if (persistentTimerService == null && nonPersistentTimerService == null) { throw new IllegalStateException("EJB Timer Service not available"); } return new EJBTimerServiceWrapper( diff --git a/appserver/ejb/ejb-container/src/main/java/org/glassfish/ejb/admin/cli/ListTimers.java b/appserver/ejb/ejb-container/src/main/java/org/glassfish/ejb/admin/cli/ListTimers.java index 2bb9554ec0d..34dee55907a 100644 --- a/appserver/ejb/ejb-container/src/main/java/org/glassfish/ejb/admin/cli/ListTimers.java +++ b/appserver/ejb/ejb-container/src/main/java/org/glassfish/ejb/admin/cli/ListTimers.java @@ -48,7 +48,6 @@ import com.sun.ejb.containers.EJBTimerService; import com.sun.ejb.containers.EjbContainerUtil; -import static java.util.Objects.nonNull; import org.glassfish.api.ActionReport; import org.glassfish.api.I18n; import org.glassfish.api.Param; @@ -137,7 +136,7 @@ private String[] listTimers(String[] serverIds) { } else if (EJBTimerService.isNonPersistentTimerServiceLoaded()) { ejbTimerService = EJBTimerService.getNonPersistentTimerService(); } - if (nonNull(ejbTimerService)) { + if (ejbTimerService != null) { result = ejbTimerService.listTimers(serverIds); } From 43c7a4509a2ba6499a82ae115be26b5c3970c8a3 Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Wed, 31 Oct 2018 00:09:03 +0530 Subject: [PATCH 7/7] PAYARA-2905 java.util.Objects method reverted from timer service example --- .../src/main/java/com/sun/ejb/timers/TimerWelcomeServlet.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/appserver/ejb/ejb-timer-service-app/src/main/java/com/sun/ejb/timers/TimerWelcomeServlet.java b/appserver/ejb/ejb-timer-service-app/src/main/java/com/sun/ejb/timers/TimerWelcomeServlet.java index 1033eb61853..dcd264e6644 100644 --- a/appserver/ejb/ejb-timer-service-app/src/main/java/com/sun/ejb/timers/TimerWelcomeServlet.java +++ b/appserver/ejb/ejb-timer-service-app/src/main/java/com/sun/ejb/timers/TimerWelcomeServlet.java @@ -51,7 +51,6 @@ import org.glassfish.ejb.persistent.timer.TimerLocal; import com.sun.ejb.containers.EJBTimerService; import com.sun.ejb.containers.TimerPrimaryKey; -import static java.util.Objects.isNull; /** * @@ -91,7 +90,7 @@ protected void processRequest(HttpServletRequest request, HttpServletResponse re } else if (EJBTimerService.isNonPersistentTimerServiceLoaded()) { ejbTimerService = EJBTimerService.getNonPersistentTimerService(); } - if(isNull(ejbTimerService)) { + if(ejbTimerService == null) { throw new IllegalStateException("EJB Timer Service is not available"); } Set nonPersistentTimers = ejbTimerService.getNonPersistentActiveTimerIdsByThisServer();