Author: rmannibucau
Date: Wed Nov 14 17:38:11 2012
New Revision: 1409270

URL: http://svn.apache.org/viewvc?rev=1409270&view=rev
Log:
when deserializing a quartz scheduler trying to do it lazily to avoid dead lock

Modified:
    
openejb/trunk/openejb/container/openejb-core/src/main/java/org/apache/openejb/core/timer/EjbTimerServiceImpl.java
    
openejb/trunk/openejb/container/openejb-core/src/test/java/org/apache/openejb/timer/EjbTimerImplSerializableTest.java

Modified: 
openejb/trunk/openejb/container/openejb-core/src/main/java/org/apache/openejb/core/timer/EjbTimerServiceImpl.java
URL: 
http://svn.apache.org/viewvc/openejb/trunk/openejb/container/openejb-core/src/main/java/org/apache/openejb/core/timer/EjbTimerServiceImpl.java?rev=1409270&r1=1409269&r2=1409270&view=diff
==============================================================================
--- 
openejb/trunk/openejb/container/openejb-core/src/main/java/org/apache/openejb/core/timer/EjbTimerServiceImpl.java
 (original)
+++ 
openejb/trunk/openejb/container/openejb-core/src/main/java/org/apache/openejb/core/timer/EjbTimerServiceImpl.java
 Wed Nov 14 17:38:11 2012
@@ -52,7 +52,9 @@ import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.Serializable;
+import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
@@ -115,114 +117,123 @@ public class EjbTimerServiceImpl impleme
         deployment = 
SystemInstance.get().getComponent(ContainerSystem.class).getBeanContext(dId);
         transactionManager = getDefaultTransactionManager();
         timerStore = new MemoryTimerStore(transactionManager); // TODO: check 
it should be serialized or not
-        scheduler = getDefaultScheduler(deployment);
+        scheduler = (Scheduler) 
Proxy.newProxyInstance(deployment.getClassLoader(), new Class<?>[] { 
Scheduler.class }, new LazyScheduler(deployment));
     }
 
-    public static synchronized Scheduler getDefaultScheduler(BeanContext 
deployment) {
+    public static synchronized Scheduler getDefaultScheduler(final BeanContext 
deployment) {
         Scheduler scheduler = deployment.get(Scheduler.class);
-        boolean valid;
-        try {
-            valid = !scheduler.isShutdown();
-        } catch (Exception ignored) {
-            valid = false;
-        }
-        if (scheduler != null && valid) {
-            return scheduler;
-        }
-
-        final Properties properties = new Properties();
-        putAll(properties, SystemInstance.get().getProperties());
-        putAll(properties, 
deployment.getModuleContext().getAppContext().getProperties());
-        putAll(properties, deployment.getModuleContext().getProperties());
-        putAll(properties, deployment.getProperties());
-
-        // custom config -> don't use default scheduler
-        boolean newInstance = false;
-        for (String key : properties.stringPropertyNames()) {
-            if (key.startsWith("org.quartz.")) {
-                newInstance = true;
-                break;
+        if (scheduler != null) {
+            boolean valid;
+            try {
+                valid = !scheduler.isShutdown();
+            } catch (Exception ignored) {
+                valid = false;
+            }
+            if (valid) {
+                return scheduler;
             }
         }
 
-        final SystemInstance systemInstance = SystemInstance.get();
+        Scheduler thisScheduler;
+        synchronized (deployment) { // should be done only once so no perf 
issues
+            scheduler = deployment.get(Scheduler.class);
+            if (scheduler != null) {
+                return scheduler;
+            }
+
+            final Properties properties = new Properties();
+            putAll(properties, SystemInstance.get().getProperties());
+            putAll(properties, 
deployment.getModuleContext().getAppContext().getProperties());
+            putAll(properties, deployment.getModuleContext().getProperties());
+            putAll(properties, deployment.getProperties());
+
+            // custom config -> don't use default scheduler
+            boolean newInstance = false;
+            for (String key : properties.stringPropertyNames()) {
+                if (key.startsWith("org.quartz.")) {
+                    newInstance = true;
+                    break;
+                }
+            }
 
-        final String defaultThreadPool = 
DefaultTimerThreadPoolAdapter.class.getName();
-        if 
(!properties.containsKey(StdSchedulerFactory.PROP_THREAD_POOL_CLASS)) {
-            properties.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, 
defaultThreadPool);
-        }
-        if 
(!properties.containsKey(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME)) {
-            properties.put(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, 
"OpenEJB-TimerService-Scheduler");
-        }
-        if (!properties.containsKey(QUARTZ_MAKE_SCHEDULER_THREAD_DAEMON)) {
-            properties.put(QUARTZ_MAKE_SCHEDULER_THREAD_DAEMON, "true");
-        }
-        if (!properties.containsKey(QUARTZ_JMX) && 
LocalMBeanServer.isJMXActive()) {
-            properties.put(QUARTZ_JMX, "true");
-        }
-        if 
(!properties.containsKey(StdSchedulerFactory.PROP_SCHED_INSTANCE_ID)) {
-            if (!newInstance) {
-                
properties.setProperty(StdSchedulerFactory.PROP_SCHED_INSTANCE_ID, "OpenEJB");
-            } else {
-                
properties.setProperty(StdSchedulerFactory.PROP_SCHED_INSTANCE_ID, 
deployment.getDeploymentID().toString());
+            final SystemInstance systemInstance = SystemInstance.get();
+
+            final String defaultThreadPool = 
DefaultTimerThreadPoolAdapter.class.getName();
+            if 
(!properties.containsKey(StdSchedulerFactory.PROP_THREAD_POOL_CLASS)) {
+                properties.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, 
defaultThreadPool);
+            }
+            if 
(!properties.containsKey(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME)) {
+                properties.put(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, 
"OpenEJB-TimerService-Scheduler");
+            }
+            if (!properties.containsKey(QUARTZ_MAKE_SCHEDULER_THREAD_DAEMON)) {
+                properties.put(QUARTZ_MAKE_SCHEDULER_THREAD_DAEMON, "true");
+            }
+            if (!properties.containsKey(QUARTZ_JMX) && 
LocalMBeanServer.isJMXActive()) {
+                properties.put(QUARTZ_JMX, "true");
+            }
+            if 
(!properties.containsKey(StdSchedulerFactory.PROP_SCHED_INSTANCE_ID)) {
+                if (!newInstance) {
+                    
properties.setProperty(StdSchedulerFactory.PROP_SCHED_INSTANCE_ID, "OpenEJB");
+                } else {
+                    
properties.setProperty(StdSchedulerFactory.PROP_SCHED_INSTANCE_ID, 
deployment.getDeploymentID().toString());
+                }
             }
-        }
 
-        // adding our custom persister
-        if (properties.containsKey("org.quartz.jobStore.class") && 
!properties.containsKey("org.quartz.jobStore.driverDelegateInitString")) {
-            properties.put("org.quartz.jobStore.driverDelegateInitString",
-                    "triggerPersistenceDelegateClasses=" + 
EJBCronTriggerPersistenceDelegate.class.getName());
-        }
+            // adding our custom persister
+            if (properties.containsKey("org.quartz.jobStore.class") && 
!properties.containsKey("org.quartz.jobStore.driverDelegateInitString")) {
+                properties.put("org.quartz.jobStore.driverDelegateInitString",
+                        "triggerPersistenceDelegateClasses=" + 
EJBCronTriggerPersistenceDelegate.class.getName());
+            }
 
-        if 
(defaultThreadPool.equals(properties.get(StdSchedulerFactory.PROP_THREAD_POOL_CLASS))
-                && properties.containsKey("org.quartz.threadPool.threadCount")
-                && 
!properties.containsKey(DefaultTimerThreadPoolAdapter.OPENEJB_TIMER_POOL_SIZE)) 
{
-            log.info("Found property 'org.quartz.threadPool.threadCount' for 
default thread pool, please use '"
-                            + 
DefaultTimerThreadPoolAdapter.OPENEJB_TIMER_POOL_SIZE + "' instead");
-        }
+            if 
(defaultThreadPool.equals(properties.get(StdSchedulerFactory.PROP_THREAD_POOL_CLASS))
+                    && 
properties.containsKey("org.quartz.threadPool.threadCount")
+                    && 
!properties.containsKey(DefaultTimerThreadPoolAdapter.OPENEJB_TIMER_POOL_SIZE)) 
{
+                log.info("Found property 'org.quartz.threadPool.threadCount' 
for default thread pool, please use '"
+                                + 
DefaultTimerThreadPoolAdapter.OPENEJB_TIMER_POOL_SIZE + "' instead");
+            }
 
-        // to ensure we can shutdown correctly, default doesn't support such a 
configuration
-        if (!properties.getProperty(StdSchedulerFactory.PROP_JOB_STORE_CLASS, 
RAMJobStore.class.getName()).equals(RAMJobStore.class.getName())) {
-            properties.put("org.quartz.jobStore.makeThreadsDaemons", 
properties.getProperty("org.quartz.jobStore.makeThreadsDaemon", "true"));
-        }
+            // to ensure we can shutdown correctly, default doesn't support 
such a configuration
+            if 
(!properties.getProperty(StdSchedulerFactory.PROP_JOB_STORE_CLASS, 
RAMJobStore.class.getName()).equals(RAMJobStore.class.getName())) {
+                properties.put("org.quartz.jobStore.makeThreadsDaemons", 
properties.getProperty("org.quartz.jobStore.makeThreadsDaemon", "true"));
+            }
 
-        scheduler = systemInstance.getComponent(Scheduler.class);
-        Scheduler thisScheduler;
-        if (scheduler == null || newInstance) {
-            try {
-                // start in container context to avoid thread leaks
-                final ClassLoader oldCl = 
Thread.currentThread().getContextClassLoader();
-                if 
(!"true".equals(deployment.getProperties().getProperty(OPENEJB_QUARTZ_USE_TCCL, 
"false"))) {
-                    
Thread.currentThread().setContextClassLoader(EjbTimerServiceImpl.class.getClassLoader());
-                }
+            scheduler = systemInstance.getComponent(Scheduler.class);
+            if (scheduler == null || newInstance) {
                 try {
-                    thisScheduler = new 
StdSchedulerFactory(properties).getScheduler();
-                    thisScheduler.start();
-                } finally {
-                    Thread.currentThread().setContextClassLoader(oldCl);
+                    // start in container context to avoid thread leaks
+                    final ClassLoader oldCl = 
Thread.currentThread().getContextClassLoader();
+                    if 
(!"true".equals(deployment.getProperties().getProperty(OPENEJB_QUARTZ_USE_TCCL, 
"false"))) {
+                        
Thread.currentThread().setContextClassLoader(EjbTimerServiceImpl.class.getClassLoader());
+                    }
+                    try {
+                        thisScheduler = new 
StdSchedulerFactory(properties).getScheduler();
+                        thisScheduler.start();
+                    } finally {
+                        Thread.currentThread().setContextClassLoader(oldCl);
+                    }
+
+                    //durability is configured with true, which means that the 
job will be kept in the store even if no trigger is attached to it.
+                    //Currently, all the EJB beans share with the same job 
instance
+                    JobDetail job = JobBuilder.newJob(EjbTimeoutJob.class)
+                            .withIdentity(OPENEJB_TIMEOUT_JOB_NAME, 
OPENEJB_TIMEOUT_JOB_GROUP_NAME)
+                            .storeDurably(true)
+                            .requestRecovery(false)
+                            .build();
+                    thisScheduler.addJob(job, true);
+                } catch (SchedulerException e) {
+                    throw new OpenEJBRuntimeException("Fail to initialize the 
default scheduler", e);
                 }
 
-                //durability is configured with true, which means that the job 
will be kept in the store even if no trigger is attached to it.
-                //Currently, all the EJB beans share with the same job instance
-                JobDetail job = JobBuilder.newJob(EjbTimeoutJob.class)
-                        .withIdentity(OPENEJB_TIMEOUT_JOB_NAME, 
OPENEJB_TIMEOUT_JOB_GROUP_NAME)
-                        .storeDurably(true)
-                        .requestRecovery(false)
-                        .build();
-                thisScheduler.addJob(job, true);
-            } catch (SchedulerException e) {
-                throw new OpenEJBRuntimeException("Fail to initialize the 
default scheduler", e);
+                if (!newInstance) {
+                    systemInstance.setComponent(Scheduler.class, 
thisScheduler);
+                }
+            } else {
+                thisScheduler = scheduler;
             }
 
-            if (!newInstance) {
-                systemInstance.setComponent(Scheduler.class, thisScheduler);
-            }
-        } else {
-            thisScheduler = scheduler;
+            deployment.set(Scheduler.class, thisScheduler);
         }
 
-        deployment.set(Scheduler.class, thisScheduler);
-
         return thisScheduler;
     }
 
@@ -266,6 +277,10 @@ public class EjbTimerServiceImpl impleme
     }
 
     private void shutdownMyScheduler() {
+        if (scheduler == null) {
+            return;
+        }
+
         boolean defaultScheduler = false;
         final Scheduler ds = 
SystemInstance.get().getComponent(Scheduler.class);
         try { // == is the faster way to test, we rely on name (key in quartz 
registry) only for serialization
@@ -275,7 +290,7 @@ public class EjbTimerServiceImpl impleme
         }
 
         // if specific instance
-        if (scheduler != null && !defaultScheduler) {
+        if (!defaultScheduler) {
             try {
                 scheduler.shutdown();
             } catch (SchedulerException e) {
@@ -285,12 +300,13 @@ public class EjbTimerServiceImpl impleme
     }
 
     public static void shutdown() {
-
-        Scheduler scheduler = 
SystemInstance.get().getComponent(Scheduler.class);
-        if (scheduler != null) try {
-            scheduler.shutdown();
-        } catch (SchedulerException e) {
-            throw new OpenEJBRuntimeException("Unable to shutdown scheduler", 
e);
+        final Scheduler scheduler = 
SystemInstance.get().getComponent(Scheduler.class);
+        if (scheduler != null) {
+            try {
+                scheduler.shutdown();
+            } catch (SchedulerException e) {
+                throw new OpenEJBRuntimeException("Unable to shutdown 
scheduler", e);
+            }
         }
     }
 
@@ -633,4 +649,17 @@ public class EjbTimerServiceImpl impleme
             });
         }
     }*/
+
+    private static class LazyScheduler implements InvocationHandler {
+        private final BeanContext ejb;
+
+        public LazyScheduler(final BeanContext deployment) {
+            ejb = deployment;
+        }
+
+        @Override
+        public Object invoke(final Object proxy, final Method method, final 
Object[] args) throws Throwable {
+            return method.invoke(getDefaultScheduler(ejb), args);
+        }
+    }
 }
\ No newline at end of file

Modified: 
openejb/trunk/openejb/container/openejb-core/src/test/java/org/apache/openejb/timer/EjbTimerImplSerializableTest.java
URL: 
http://svn.apache.org/viewvc/openejb/trunk/openejb/container/openejb-core/src/test/java/org/apache/openejb/timer/EjbTimerImplSerializableTest.java?rev=1409270&r1=1409269&r2=1409270&view=diff
==============================================================================
--- 
openejb/trunk/openejb/container/openejb-core/src/test/java/org/apache/openejb/timer/EjbTimerImplSerializableTest.java
 (original)
+++ 
openejb/trunk/openejb/container/openejb-core/src/test/java/org/apache/openejb/timer/EjbTimerImplSerializableTest.java
 Wed Nov 14 17:38:11 2012
@@ -44,6 +44,7 @@ import java.io.ObjectOutputStream;
 import java.lang.reflect.Field;
 
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.IsNull.notNullValue;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
@@ -62,6 +63,7 @@ public class EjbTimerImplSerializableTes
         final EjbTimerService timerDeserialized = (EjbTimerService) 
deserialize(serial);
 
         assertThat(timerDeserialized, instanceOf(EjbTimerServiceImpl.class));
+        assertThat(((EjbTimerServiceImpl) timerDeserialized).getScheduler(), 
notNullValue());
 
         assertEqualsByReflection(timer, timerDeserialized, "deployment");
         assertEqualsByReflection(timer, timerDeserialized, "transacted");


Reply via email to