You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by db...@apache.org on 2012/10/19 08:13:00 UTC

svn commit: r1399980 - in /openejb/trunk/openejb/examples/schedule-events: README.md pom.xml src/test/java/org/superbiz/schedule/events/SchedulerTest.java

Author: dblevins
Date: Fri Oct 19 06:12:59 2012
New Revision: 1399980

URL: http://svn.apache.org/viewvc?rev=1399980&view=rev
Log:
Missed pom.xml.  Added README.md
TOMEE-485

Added:
    openejb/trunk/openejb/examples/schedule-events/README.md
    openejb/trunk/openejb/examples/schedule-events/pom.xml   (with props)
Modified:
    openejb/trunk/openejb/examples/schedule-events/src/test/java/org/superbiz/schedule/events/SchedulerTest.java

Added: openejb/trunk/openejb/examples/schedule-events/README.md
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/examples/schedule-events/README.md?rev=1399980&view=auto
==============================================================================
--- openejb/trunk/openejb/examples/schedule-events/README.md (added)
+++ openejb/trunk/openejb/examples/schedule-events/README.md Fri Oct 19 06:12:59 2012
@@ -0,0 +1,163 @@
+Title: Schedule CDI Events
+
+This example uses a nice CDI/EJB combination to schedule CDI Events.  This is useful if you want CDI Events that fire regularly or at a specific time or calendar date.
+
+Effectively this is a simple wrapper around the `BeanManager.fireEvent(Object,Annotations...)` method that adds `ScheduleExpression` into the mix.
+
+## ScheduleExpression and @Timeout
+
+The logic here is simple, we effecitvely expose a method identical to `BeanManager.fireEvent(Object, Annotations...)` and wrap it as  `scheduleEvent(ScheduleExpression, Object, Annotation...)`
+
+To do that we use the EJB `TimerService` (under the covers this is Quartz) and create an `@Timeout` method which will be run when the `ScheduleExpression` activates.
+
+The `@Timeout` method, simply called `timeout`, takes the event and fires it.
+
+    @Singleton
+    @Lock(LockType.READ)
+    public class Scheduler {
+
+        @Resource
+        private TimerService timerService;
+
+        @Resource
+        private BeanManager beanManager;
+
+        public void scheduleEvent(ScheduleExpression schedule, Object event, Annotation... qualifiers) {
+
+            timerService.createCalendarTimer(schedule, new TimerConfig(new EventConfig(event, qualifiers), false));
+        }
+
+        @Timeout
+        private void timeout(Timer timer) {
+            final EventConfig config = (EventConfig) timer.getInfo();
+
+            beanManager.fireEvent(config.getEvent(), config.getQualifiers());
+        }
+
+        // Doesn't actually need to be serializable, just has to implement it
+        private final class EventConfig implements Serializable {
+
+            private final Object event;
+            private final Annotation[] qualifiers;
+
+            private EventConfig(Object event, Annotation[] qualifiers) {
+                this.event = event;
+                this.qualifiers = qualifiers;
+            }
+
+            public Object getEvent() {
+                return event;
+            }
+
+            public Annotation[] getQualifiers() {
+                return qualifiers;
+            }
+        }
+    }
+
+Then to use it, have `Scheduler` injected as an EJB and enjoy.
+
+    public class SomeBean {
+
+        @EJB
+        private Scheduler scheduler;
+
+        public void doit() throws Exception {
+
+            // every five minutes
+            final ScheduleExpression schedule = new ScheduleExpression()
+                    .hour("*")
+                    .minute("*")
+                    .second("*/5");
+
+            scheduler.scheduleEvent(schedule, new TestEvent("five"));
+        }
+
+        /**
+         * Event will fire every five minutes
+         */
+        public void observe(@Observes TestEvent event) {
+            // process the event
+        }
+
+    }
+
+## Test Case
+
+A working test case for the above would be as follows:
+
+    import org.junit.Assert;
+    import org.junit.Before;
+    import org.junit.Test;
+
+    import javax.ejb.AccessTimeout;
+    import javax.ejb.EJB;
+    import javax.ejb.ScheduleExpression;
+    import javax.ejb.embeddable.EJBContainer;
+    import javax.enterprise.event.Observes;
+    import java.util.concurrent.CountDownLatch;
+    import java.util.concurrent.TimeUnit;
+
+    /**
+     * @version $Revision$ $Date$
+     */
+    public class SchedulerTest {
+
+        public static final CountDownLatch events = new CountDownLatch(3);
+
+        @EJB
+        private Scheduler scheduler;
+
+        @Test
+        public void test() throws Exception {
+
+            final ScheduleExpression schedule = new ScheduleExpression()
+                    .hour("*")
+                    .minute("*")
+                    .second("*/5");
+
+            scheduler.scheduleEvent(schedule, new TestEvent("five"));
+
+            Assert.assertTrue(events.await(1, TimeUnit.MINUTES));
+        }
+
+
+        @AccessTimeout(value = 1, unit = TimeUnit.MINUTES)
+        public void observe(@Observes TestEvent event) {
+            if ("five".equals(event.getMessage())) {
+                events.countDown();
+            }
+        }
+
+        public static class TestEvent {
+            private final String message;
+
+            public TestEvent(String message) {
+                this.message = message;
+            }
+
+            public String getMessage() {
+                return message;
+            }
+        }
+
+        @Before
+        public void setup() throws Exception {
+            EJBContainer.createEJBContainer().getContext().bind("inject", this);
+        }
+    }
+
+
+## You must know
+
+ - CDI Events are not multi-treaded
+
+If there are 10 observers and each of them take 7 minutes to execute, then the total execution time for the one event is 70 minutes.  It would do you absolutely no good to schedule that event to fire more frequently than 70 minutes.
+
+What would happen if you did?  Depends on the `@Singleton` `@Lock` policy
+
+ - `@Lock(WRITE)` is the default.  In this mode the `timeout` method would essentially be locked until the previous invocation completes.  Having it fire every 5 minutes even though you can only process one every 70 minutes would eventually cause all the pooled timer threads to be waiting on your Singleton.
+ - `@Lock(READ)` allows for parallel execution of the `timeout` method.  Events will fire in parallel for a while.  However since they actually are taking 70 minutes each, within an hour or so we'll run out of threads in the timer pool just like above.
+
+The elegant solution is to use `@Lock(WRITE)` then specify some short timeout like `@AccessTimeout(value = 1, unit = TimeUnit.MINUTES)` on the `timeout` method.  When the next 5 minute invocation is triggered, it will wait up until 1 minute to get access to the Singleton before giving up.  This will keep your timer pool from filling up with backed up jobs -- the "overflow" is simply discarded.
+

Added: openejb/trunk/openejb/examples/schedule-events/pom.xml
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/examples/schedule-events/pom.xml?rev=1399980&view=auto
==============================================================================
--- openejb/trunk/openejb/examples/schedule-events/pom.xml (added)
+++ openejb/trunk/openejb/examples/schedule-events/pom.xml Fri Oct 19 06:12:59 2012
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<!-- $Rev: 1387962 $ $Date: 2012-09-20 05:53:17 -0500 (Thu, 20 Sep 2012) $ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.superbiz</groupId>
+  <artifactId>schedule-events</artifactId>
+  <packaging>jar</packaging>
+  <version>1.1-SNAPSHOT</version>
+  <name>OpenEJB :: Examples :: @Schedule Events</name>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+  <build>
+    <defaultGoal>install</defaultGoal>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>2.4</version>
+        <configuration>
+          <source>1.6</source>
+          <target>1.6</target>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <repositories>
+    <repository>
+      <id>apache-m2-snapshot</id>
+      <name>Apache Snapshot Repository</name>
+      <url>http://repository.apache.org/snapshots</url>
+    </repository>
+  </repositories>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.openejb</groupId>
+      <artifactId>javaee-api</artifactId>
+      <version>6.0-4</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.10</version>
+      <scope>test</scope>
+    </dependency>
+    <!--
+    The <scope>test</scope> guarantees that non of your runtime
+    code is dependent on any OpenEJB classes.
+    -->
+    <dependency>
+      <groupId>org.apache.openejb</groupId>
+      <artifactId>openejb-core</artifactId>
+      <version>4.5.1-SNAPSHOT</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <!--
+  This section allows you to configure where to publish libraries for sharing.
+  It is not required and may be deleted.  For more information see:
+  http://maven.apache.org/plugins/maven-deploy-plugin/
+  -->
+  <distributionManagement>
+    <repository>
+      <id>localhost</id>
+      <url>file://${basedir}/target/repo/</url>
+    </repository>
+    <snapshotRepository>
+      <id>localhost</id>
+      <url>file://${basedir}/target/snapshot-repo/</url>
+    </snapshotRepository>
+  </distributionManagement>
+</project>
\ No newline at end of file

Propchange: openejb/trunk/openejb/examples/schedule-events/pom.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: openejb/trunk/openejb/examples/schedule-events/src/test/java/org/superbiz/schedule/events/SchedulerTest.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb/examples/schedule-events/src/test/java/org/superbiz/schedule/events/SchedulerTest.java?rev=1399980&r1=1399979&r2=1399980&view=diff
==============================================================================
--- openejb/trunk/openejb/examples/schedule-events/src/test/java/org/superbiz/schedule/events/SchedulerTest.java (original)
+++ openejb/trunk/openejb/examples/schedule-events/src/test/java/org/superbiz/schedule/events/SchedulerTest.java Fri Oct 19 06:12:59 2012
@@ -20,6 +20,7 @@ import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
+import javax.ejb.AccessTimeout;
 import javax.ejb.EJB;
 import javax.ejb.ScheduleExpression;
 import javax.ejb.embeddable.EJBContainer;
@@ -40,12 +41,18 @@ public class SchedulerTest {
     @Test
     public void test() throws Exception {
 
-        scheduler.scheduleEvent(new ScheduleExpression().hour("*").minute("*").second("*/5"), new TestEvent("five"));
+        final ScheduleExpression schedule = new ScheduleExpression()
+                .hour("*")
+                .minute("*")
+                .second("*/5");
+
+        scheduler.scheduleEvent(schedule, new TestEvent("five"));
 
         Assert.assertTrue(events.await(1, TimeUnit.MINUTES));
     }
 
 
+    @AccessTimeout(value = 1, unit = TimeUnit.MINUTES)
     public void observe(@Observes TestEvent event) {
         if ("five".equals(event.getMessage())) {
             events.countDown();