diff --git a/jee7schedule/pom.xml b/jee7schedule/pom.xml
new file mode 100644
index 0000000000..627b8723e3
--- /dev/null
+++ b/jee7schedule/pom.xml
@@ -0,0 +1,311 @@
+
+
+ 4.0.0
+
+ com.baeldung
+ jee7schedule
+ 1.0-SNAPSHOT
+ JavaEE 7 Arquillian Archetype Sample
+ war
+
+
+ 1.7
+ 3.0.0
+
+ 4.11
+ 7.0
+
+ 1.1.4.Final
+
+ 8.0.0.Final
+
+
+
+ ${maven.min.version}
+
+
+
+
+
+ org.jboss.arquillian
+ arquillian-bom
+ ${version.arquillian_core}
+ import
+ pom
+
+
+
+
+
+
+ javax
+ javaee-api
+ ${version.javaee_api}
+ provided
+
+
+
+ junit
+ junit
+ ${version.junit}
+ test
+
+
+ org.jboss.arquillian.junit
+ arquillian-junit-container
+ test
+
+
+ com.jayway.awaitility
+ awaitility
+ 1.6.0
+ test
+
+
+
+ org.jboss.shrinkwrap.resolver
+ shrinkwrap-resolver-impl-maven
+ test
+ jar
+
+
+
+ org.jboss.shrinkwrap.resolver
+ shrinkwrap-resolver-impl-maven-archive
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.1
+
+
+ ${java.min.version}
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+ 2.1.1
+
+ false
+
+
+
+
+
+
+
+
+ wildfly-managed-arquillian
+
+ true
+
+
+ standalone-full.xml
+ ${project.build.directory}/wildfly-${version.wildfly}
+
+
+
+ io.undertow
+ undertow-websockets-jsr
+ 1.0.0.Beta25
+ test
+
+
+ org.jboss.resteasy
+ resteasy-client
+ 3.0.5.Final
+ test
+
+
+ org.jboss.resteasy
+ resteasy-jaxb-provider
+ 3.0.5.Final
+ test
+
+
+ org.jboss.resteasy
+ resteasy-json-p-provider
+ 3.0.5.Final
+ test
+
+
+ org.wildfly
+ wildfly-arquillian-container-managed
+ ${version.wildfly}
+ test
+
+
+
+
+
+ maven-dependency-plugin
+ 2.8
+
+ ${maven.test.skip}
+
+
+
+ unpack
+ process-test-classes
+
+ unpack
+
+
+
+
+ org.wildfly
+ wildfly-dist
+ ${version.wildfly}
+ zip
+ false
+ ${project.build.directory}
+
+
+
+
+
+
+
+ maven-surefire-plugin
+ 2.17
+
+
+ ${project.build.directory}/wildfly-${version.wildfly}
+
+
+
+
+
+
+
+ wildfly-remote-arquillian
+
+
+ io.undertow
+ undertow-websockets-jsr
+ 1.0.0.Beta25
+ test
+
+
+ org.jboss.resteasy
+ resteasy-client
+ 3.0.5.Final
+ test
+
+
+ org.jboss.resteasy
+ resteasy-jaxb-provider
+ 3.0.5.Final
+ test
+
+
+ org.jboss.resteasy
+ resteasy-json-p-provider
+ 3.0.5.Final
+ test
+
+
+ org.wildfly
+ wildfly-arquillian-container-remote
+ ${version.wildfly}
+ test
+
+
+
+
+ glassfish-embedded-arquillian
+
+
+ org.glassfish.main.extras
+ glassfish-embedded-all
+ 4.0
+ test
+
+
+ org.glassfish
+ javax.json
+ 1.0.4
+ test
+
+
+ org.glassfish.tyrus
+ tyrus-client
+ 1.3
+ test
+
+
+ org.glassfish.tyrus
+ tyrus-container-grizzly-client
+ 1.3
+ test
+
+
+ org.glassfish.jersey.core
+ jersey-client
+ 2.4
+ test
+
+
+ org.jboss.arquillian.container
+ arquillian-glassfish-embedded-3.1
+ 1.0.0.CR4
+ test
+
+
+
+
+ glassfish-remote-arquillian
+
+
+ org.glassfish
+ javax.json
+ 1.0.4
+ test
+
+
+ org.glassfish.tyrus
+ tyrus-client
+ 1.3
+ test
+
+
+ org.glassfish.tyrus
+ tyrus-container-grizzly-client
+ 1.3
+ test
+
+
+ org.glassfish.jersey.core
+ jersey-client
+ 2.4
+ test
+
+
+ org.glassfish.jersey.media
+ jersey-media-json-jackson
+ 2.4
+ test
+
+
+ org.glassfish.jersey.media
+ jersey-media-json-processing
+ 2.4
+ test
+
+
+ org.jboss.arquillian.container
+ arquillian-glassfish-remote-3.1
+ 1.0.0.CR4
+ test
+
+
+
+
+
diff --git a/jee7schedule/src/main/java/com/baeldung/timer/AutomaticTimerBean.java b/jee7schedule/src/main/java/com/baeldung/timer/AutomaticTimerBean.java
new file mode 100644
index 0000000000..373d962f02
--- /dev/null
+++ b/jee7schedule/src/main/java/com/baeldung/timer/AutomaticTimerBean.java
@@ -0,0 +1,27 @@
+package com.baeldung.timer;
+
+import javax.ejb.Schedule;
+import javax.ejb.Singleton;
+import javax.ejb.Startup;
+import javax.enterprise.event.Event;
+import javax.inject.Inject;
+import java.util.Date;
+
+
+@Startup
+@Singleton
+public class AutomaticTimerBean {
+
+
+ @Inject
+ Event event;
+
+ /**
+ * This method will be called every 10 second and will fire an @TimerEvent
+ */
+ @Schedule(hour = "*", minute = "*", second = "*/10", info = "Every 10 second timer")
+ public void printDate() {
+ event.fire(new TimerEvent("TimerEvent sent at :" + new Date()));
+ }
+
+}
diff --git a/jee7schedule/src/main/java/com/baeldung/timer/FixedDelayTimerBean.java b/jee7schedule/src/main/java/com/baeldung/timer/FixedDelayTimerBean.java
new file mode 100644
index 0000000000..a466682dea
--- /dev/null
+++ b/jee7schedule/src/main/java/com/baeldung/timer/FixedDelayTimerBean.java
@@ -0,0 +1,20 @@
+package com.baeldung.timer;
+
+import javax.ejb.*;
+
+/**
+ * Created by ccristianchiovari on 5/2/16.
+ */
+@Singleton
+public class FixedDelayTimerBean {
+
+ @EJB
+ private WorkerBean workerBean;
+
+ @Lock(LockType.READ)
+ @Schedule(second = "*/5", minute = "*", hour = "*", persistent = false)
+ public void atSchedule() throws InterruptedException {
+ workerBean.doTimerWork();
+ }
+
+}
\ No newline at end of file
diff --git a/jee7schedule/src/main/java/com/baeldung/timer/ProgrammaticAtFixedRateTimerBean.java b/jee7schedule/src/main/java/com/baeldung/timer/ProgrammaticAtFixedRateTimerBean.java
new file mode 100644
index 0000000000..c1c210c8ac
--- /dev/null
+++ b/jee7schedule/src/main/java/com/baeldung/timer/ProgrammaticAtFixedRateTimerBean.java
@@ -0,0 +1,31 @@
+package com.baeldung.timer;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import javax.ejb.*;
+import javax.enterprise.event.Event;
+import javax.inject.Inject;
+
+/**
+ * author: Cristian Chiovari
+ */
+@Startup
+@Singleton
+public class ProgrammaticAtFixedRateTimerBean {
+
+ @Inject
+ Event event;
+
+ @Resource
+ TimerService timerService;
+
+ @PostConstruct
+ public void initialize() {
+ timerService.createTimer(0,1000, "Every second timer");
+ }
+
+ @Timeout
+ public void programmaticTimout(Timer timer) {
+ event.fire(new TimerEvent(timer.getInfo().toString()));
+ }
+}
diff --git a/jee7schedule/src/main/java/com/baeldung/timer/ProgrammaticTimerBean.java b/jee7schedule/src/main/java/com/baeldung/timer/ProgrammaticTimerBean.java
new file mode 100644
index 0000000000..d2dba1239f
--- /dev/null
+++ b/jee7schedule/src/main/java/com/baeldung/timer/ProgrammaticTimerBean.java
@@ -0,0 +1,39 @@
+package com.baeldung.timer;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import javax.ejb.*;
+import javax.enterprise.event.Event;
+import javax.inject.Inject;
+
+/**
+ * author: Jacek Jackowiak
+ */
+@Startup
+@Singleton
+public class ProgrammaticTimerBean {
+
+ @Inject
+ Event event;
+
+ @Resource
+ TimerService timerService;
+
+ @PostConstruct
+ public void initialize() {
+ ScheduleExpression scheduleExpression = new ScheduleExpression()
+ .hour("*")
+ .minute("*")
+ .second("*/5");
+
+ TimerConfig timerConfig = new TimerConfig();
+ timerConfig.setInfo("Every 5 second timer");
+
+ timerService.createCalendarTimer(scheduleExpression, timerConfig);
+ }
+
+ @Timeout
+ public void programmaticTimout(Timer timer) {
+ event.fire(new TimerEvent(timer.getInfo().toString()));
+ }
+}
diff --git a/jee7schedule/src/main/java/com/baeldung/timer/ProgrammaticWithInitialFixedDelayTimerBean.java b/jee7schedule/src/main/java/com/baeldung/timer/ProgrammaticWithInitialFixedDelayTimerBean.java
new file mode 100644
index 0000000000..9a1ebcdc57
--- /dev/null
+++ b/jee7schedule/src/main/java/com/baeldung/timer/ProgrammaticWithInitialFixedDelayTimerBean.java
@@ -0,0 +1,31 @@
+package com.baeldung.timer;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import javax.ejb.*;
+import javax.enterprise.event.Event;
+import javax.inject.Inject;
+
+/**
+ * author: Cristian Chiovari
+ */
+@Startup
+@Singleton
+public class ProgrammaticWithInitialFixedDelayTimerBean {
+
+ @Inject
+ Event event;
+
+ @Resource
+ TimerService timerService;
+
+ @PostConstruct
+ public void initialize() {
+ timerService.createTimer(10000l,5000l, "Delay 10 seconds then every 5 second timer");
+ }
+
+ @Timeout
+ public void programmaticTimout(Timer timer) {
+ event.fire(new TimerEvent(timer.getInfo().toString()));
+ }
+}
diff --git a/jee7schedule/src/main/java/com/baeldung/timer/ScheduleTimerBean.java b/jee7schedule/src/main/java/com/baeldung/timer/ScheduleTimerBean.java
new file mode 100644
index 0000000000..09fb95c889
--- /dev/null
+++ b/jee7schedule/src/main/java/com/baeldung/timer/ScheduleTimerBean.java
@@ -0,0 +1,27 @@
+package com.baeldung.timer;
+
+import javax.ejb.Schedule;
+import javax.ejb.Singleton;
+import javax.ejb.Startup;
+import javax.ejb.Timer;
+import javax.enterprise.event.Event;
+import javax.inject.Inject;
+
+
+@Startup
+@Singleton
+public class ScheduleTimerBean {
+
+ @Inject
+ Event event;
+
+ @Schedule(hour = "*", minute = "*", second = "*/5", info = "Every 5 second timer")
+ public void automaticallyScheduled(Timer timer) {
+ fireEvent(timer);
+ }
+
+
+ private void fireEvent(Timer timer) {
+ event.fire(new TimerEvent(timer.getInfo().toString()));
+ }
+}
diff --git a/jee7schedule/src/main/java/com/baeldung/timer/TimerEvent.java b/jee7schedule/src/main/java/com/baeldung/timer/TimerEvent.java
new file mode 100644
index 0000000000..e430cfc1b1
--- /dev/null
+++ b/jee7schedule/src/main/java/com/baeldung/timer/TimerEvent.java
@@ -0,0 +1,27 @@
+package com.baeldung.timer;
+
+public class TimerEvent {
+
+ private String eventInfo;
+ private long time = System.currentTimeMillis();
+
+ public TimerEvent(String s) {
+ this.eventInfo = s;
+ }
+
+ public long getTime() {
+ return time;
+ }
+
+ public String getEventInfo() {
+ return eventInfo;
+ }
+
+ @Override
+ public String toString() {
+ return "TimerEvent {" +
+ "eventInfo='" + eventInfo + '\'' +
+ ", time=" + time +
+ '}';
+ }
+}
diff --git a/jee7schedule/src/main/java/com/baeldung/timer/TimerEventListener.java b/jee7schedule/src/main/java/com/baeldung/timer/TimerEventListener.java
new file mode 100644
index 0000000000..c89677213a
--- /dev/null
+++ b/jee7schedule/src/main/java/com/baeldung/timer/TimerEventListener.java
@@ -0,0 +1,26 @@
+package com.baeldung.timer;
+
+import javax.ejb.Singleton;
+import javax.ejb.Startup;
+import javax.enterprise.event.Observes;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * This class will listen to all TimerEvent and will collect them
+ */
+@Startup
+@Singleton
+public class TimerEventListener {
+
+ final List events = new CopyOnWriteArrayList<>();
+
+ public void listen(@Observes TimerEvent event) {
+ System.out.println("event = " + event);
+ events.add(event);
+ }
+
+ public List getEvents() {
+ return events;
+ }
+}
diff --git a/jee7schedule/src/main/java/com/baeldung/timer/WorkerBean.java b/jee7schedule/src/main/java/com/baeldung/timer/WorkerBean.java
new file mode 100644
index 0000000000..c1f781e95e
--- /dev/null
+++ b/jee7schedule/src/main/java/com/baeldung/timer/WorkerBean.java
@@ -0,0 +1,33 @@
+package com.baeldung.timer;
+
+import javax.ejb.Lock;
+import javax.ejb.LockType;
+import javax.ejb.Singleton;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Created by cristianchiovari on 5/2/16.
+ */
+@Singleton
+public class WorkerBean {
+
+ private AtomicBoolean busy = new AtomicBoolean(false);
+
+ @Lock(LockType.READ)
+ public void doTimerWork() throws InterruptedException {
+
+ System.out.println("Timer method called but not started yet !");
+
+ if (!busy.compareAndSet(false, true)) {
+ return;
+ }
+
+ try {
+ System.out.println("Timer work started");
+ Thread.sleep(12000);
+ System.out.println("Timer work done");
+ } finally {
+ busy.set(false);
+ }
+ }
+}
\ No newline at end of file
diff --git a/jee7schedule/src/main/webapp/WEB-INF/beans.xml b/jee7schedule/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/jee7schedule/src/test/java/com/baeldung/timer/AutomaticTimerBeanTest.java b/jee7schedule/src/test/java/com/baeldung/timer/AutomaticTimerBeanTest.java
new file mode 100644
index 0000000000..df922b28df
--- /dev/null
+++ b/jee7schedule/src/test/java/com/baeldung/timer/AutomaticTimerBeanTest.java
@@ -0,0 +1,66 @@
+package com.baeldung.timer;
+
+import com.jayway.awaitility.Awaitility;
+import org.hamcrest.Matchers;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.jboss.shrinkwrap.resolver.api.maven.Maven;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+
+import static com.jayway.awaitility.Awaitility.await;
+import static com.jayway.awaitility.Awaitility.to;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+
+
+@RunWith(Arquillian.class)
+public class AutomaticTimerBeanTest {
+
+ //the @AutomaticTimerBean has a method called every 10 seconds
+ //testing the difference ==> 100000
+ final static long TIMEOUT = 10000l;
+
+ //the tolerance accepted , so if between two consecutive calls there has to be at least 9 or max 11 seconds.
+ //because the timer service is not intended for real-time applications so it will not be exactly 10 seconds
+ final static long TOLERANCE = 1000l;
+
+ @Inject
+ TimerEventListener timerEventListener;
+
+ @Deployment
+ public static WebArchive deploy() {
+ File[] jars = Maven.resolver().loadPomFromFile("pom.xml")
+ .resolve("com.jayway.awaitility:awaitility")
+ .withTransitivity().asFile();
+
+ //only @AutomaticTimerBean is deployed not the other timers
+ return ShrinkWrap.create(WebArchive.class)
+ .addAsLibraries(jars)
+ .addClasses(WithinWindowMatcher.class, TimerEvent.class, TimerEventListener.class, AutomaticTimerBean.class);
+ }
+
+
+
+ @Test
+ public void should_receive_two_pings() {
+ Awaitility.setDefaultTimeout(30, TimeUnit.SECONDS);
+ //the test will wait here until two events are triggered
+ await().untilCall(to(timerEventListener.getEvents()).size(), equalTo(2));
+
+ TimerEvent firstEvent = timerEventListener.getEvents().get(0);
+ TimerEvent secondEvent = timerEventListener.getEvents().get(1);
+
+ long delay = secondEvent.getTime() - firstEvent.getTime();
+ System.out.println("Actual timeout = " + delay);
+
+ //ensure that the delay between the events is more or less 10 seconds (no real time precision)
+ assertThat(delay, Matchers.is(WithinWindowMatcher.withinWindow(TIMEOUT, TOLERANCE)));
+ }
+}
diff --git a/jee7schedule/src/test/java/com/baeldung/timer/ProgrammaticAtFixedRateTimerBeanTest.java b/jee7schedule/src/test/java/com/baeldung/timer/ProgrammaticAtFixedRateTimerBeanTest.java
new file mode 100644
index 0000000000..76c0e3e5b8
--- /dev/null
+++ b/jee7schedule/src/test/java/com/baeldung/timer/ProgrammaticAtFixedRateTimerBeanTest.java
@@ -0,0 +1,56 @@
+package com.baeldung.timer;
+
+import com.jayway.awaitility.Awaitility;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.jboss.shrinkwrap.resolver.api.maven.Maven;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+
+import static com.jayway.awaitility.Awaitility.await;
+import static com.jayway.awaitility.Awaitility.to;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+
+
+@RunWith(Arquillian.class)
+public class ProgrammaticAtFixedRateTimerBeanTest {
+
+ final static long TIMEOUT = 1000;
+ final static long TOLERANCE = 500l;
+
+ @Inject
+ TimerEventListener timerEventListener;
+
+ @Deployment
+ public static WebArchive deploy() {
+ File[] jars = Maven.resolver().loadPomFromFile("pom.xml")
+ .resolve("com.jayway.awaitility:awaitility")
+ .withTransitivity().asFile();
+
+ return ShrinkWrap.create(WebArchive.class)
+ .addAsLibraries(jars)
+ .addClasses(WithinWindowMatcher.class, TimerEvent.class, TimerEventListener.class, ProgrammaticAtFixedRateTimerBean.class);
+ }
+
+ @Test
+ public void should_receive_ten_pings() {
+
+ Awaitility.setDefaultTimeout(30, TimeUnit.SECONDS);
+
+ await().untilCall(to(timerEventListener.getEvents()).size(), equalTo(10));
+ TimerEvent firstEvent = timerEventListener.getEvents().get(0);
+ TimerEvent secondEvent = timerEventListener.getEvents().get(1);
+
+ long delay = secondEvent.getTime() - firstEvent.getTime();
+ System.out.println("Actual timeout = " + delay);
+ assertThat(delay, is(WithinWindowMatcher.withinWindow(TIMEOUT, TOLERANCE)));
+ }
+}
\ No newline at end of file
diff --git a/jee7schedule/src/test/java/com/baeldung/timer/ProgrammaticTimerBeanTest.java b/jee7schedule/src/test/java/com/baeldung/timer/ProgrammaticTimerBeanTest.java
new file mode 100644
index 0000000000..f93ba61fe3
--- /dev/null
+++ b/jee7schedule/src/test/java/com/baeldung/timer/ProgrammaticTimerBeanTest.java
@@ -0,0 +1,53 @@
+package com.baeldung.timer;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.jboss.shrinkwrap.resolver.api.maven.Maven;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.inject.Inject;
+import java.io.File;
+
+import static com.jayway.awaitility.Awaitility.await;
+import static com.jayway.awaitility.Awaitility.to;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+
+
+@RunWith(Arquillian.class)
+public class ProgrammaticTimerBeanTest {
+
+ final static long TIMEOUT = 5000l;
+ final static long TOLERANCE = 1000l;
+
+ @Inject
+ TimerEventListener timerEventListener;
+
+ @Deployment
+ public static WebArchive deploy() {
+ File[] jars = Maven.resolver().loadPomFromFile("pom.xml")
+ .resolve("com.jayway.awaitility:awaitility")
+ .withTransitivity().asFile();
+
+ return ShrinkWrap.create(WebArchive.class)
+ .addAsLibraries(jars)
+ .addClasses(WithinWindowMatcher.class, TimerEvent.class, TimerEventListener.class, ProgrammaticTimerBean.class);
+ }
+
+ @Test
+ public void should_receive_two_pings() {
+
+ await().untilCall(to(timerEventListener.getEvents()).size(), equalTo(2));
+
+ TimerEvent firstEvent = timerEventListener.getEvents().get(0);
+ TimerEvent secondEvent = timerEventListener.getEvents().get(1);
+
+ long delay = secondEvent.getTime() - firstEvent.getTime();
+ System.out.println("Actual timeout = " + delay);
+ assertThat(delay, is(WithinWindowMatcher.withinWindow(TIMEOUT, TOLERANCE)));
+ }
+}
\ No newline at end of file
diff --git a/jee7schedule/src/test/java/com/baeldung/timer/ProgrammaticWithFixedDelayTimerBeanTest.java b/jee7schedule/src/test/java/com/baeldung/timer/ProgrammaticWithFixedDelayTimerBeanTest.java
new file mode 100644
index 0000000000..6764aa386d
--- /dev/null
+++ b/jee7schedule/src/test/java/com/baeldung/timer/ProgrammaticWithFixedDelayTimerBeanTest.java
@@ -0,0 +1,61 @@
+package com.baeldung.timer;
+
+import com.jayway.awaitility.Awaitility;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.jboss.shrinkwrap.resolver.api.maven.Maven;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+
+import static com.jayway.awaitility.Awaitility.await;
+import static com.jayway.awaitility.Awaitility.to;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+
+
+@RunWith(Arquillian.class)
+public class ProgrammaticWithFixedDelayTimerBeanTest {
+
+ final static long TIMEOUT = 15000l;
+ final static long TOLERANCE = 1000l;
+
+ @Inject
+ TimerEventListener timerEventListener;
+
+ @Deployment
+ public static WebArchive deploy() {
+ File[] jars = Maven.resolver().loadPomFromFile("pom.xml")
+ .resolve("com.jayway.awaitility:awaitility")
+ .withTransitivity().asFile();
+
+ return ShrinkWrap.create(WebArchive.class)
+ .addAsLibraries(jars)
+ .addClasses(WithinWindowMatcher.class, TimerEvent.class, TimerEventListener.class, ProgrammaticWithInitialFixedDelayTimerBean.class);
+ }
+
+ @Test
+ public void should_receive_two_pings() {
+
+ Awaitility.setDefaultTimeout(30, TimeUnit.SECONDS);
+
+ // 10 seconds pause so we get the startTime and it will trigger first event
+ long startTime = System.currentTimeMillis();
+
+ await().untilCall(to(timerEventListener.getEvents()).size(), equalTo(2));
+ TimerEvent firstEvent = timerEventListener.getEvents().get(0);
+ TimerEvent secondEvent = timerEventListener.getEvents().get(1);
+
+ long delay = secondEvent.getTime() - startTime;
+ System.out.println("Actual timeout = " + delay);
+
+ //apx 15 seconds = 10 delay + 2 timers (first after a pause of 10 seconds and the next others every 5 seconds)
+ assertThat(delay, is(WithinWindowMatcher.withinWindow(TIMEOUT, TOLERANCE)));
+ }
+}
\ No newline at end of file
diff --git a/jee7schedule/src/test/java/com/baeldung/timer/ScheduleTimerBeanTest.java b/jee7schedule/src/test/java/com/baeldung/timer/ScheduleTimerBeanTest.java
new file mode 100644
index 0000000000..7a5b043101
--- /dev/null
+++ b/jee7schedule/src/test/java/com/baeldung/timer/ScheduleTimerBeanTest.java
@@ -0,0 +1,59 @@
+package com.baeldung.timer;
+
+import com.jayway.awaitility.Awaitility;
+import org.hamcrest.Matchers;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.jboss.shrinkwrap.resolver.api.maven.Maven;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+
+import static com.jayway.awaitility.Awaitility.await;
+import static com.jayway.awaitility.Awaitility.to;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+
+
+@RunWith(Arquillian.class)
+public class ScheduleTimerBeanTest {
+
+ final static long TIMEOUT = 5000l;
+ final static long TOLERANCE = 1000l;
+
+ @Inject
+ TimerEventListener timerEventListener;
+
+ @Deployment
+ public static WebArchive deploy() {
+ File[] jars = Maven.resolver().loadPomFromFile("pom.xml")
+ .resolve("com.jayway.awaitility:awaitility")
+ .withTransitivity().asFile();
+
+ return ShrinkWrap.create(WebArchive.class)
+ .addAsLibraries(jars)
+ .addClasses(WithinWindowMatcher.class, TimerEvent.class, TimerEventListener.class, ScheduleTimerBean.class);
+ }
+
+ @Test
+ public void should_receive_three_pings() {
+
+ Awaitility.setDefaultTimeout(30, TimeUnit.SECONDS);
+ await().untilCall(to(timerEventListener.getEvents()).size(), equalTo(3));
+
+ TimerEvent firstEvent = timerEventListener.getEvents().get(0);
+ TimerEvent secondEvent = timerEventListener.getEvents().get(1);
+ TimerEvent thirdEvent = timerEventListener.getEvents().get(2);
+
+ long delay = secondEvent.getTime() - firstEvent.getTime();
+ assertThat(delay, Matchers.is(WithinWindowMatcher.withinWindow(TIMEOUT, TOLERANCE)));
+ delay = thirdEvent.getTime() - secondEvent.getTime();
+ assertThat(delay, Matchers.is(WithinWindowMatcher.withinWindow(TIMEOUT, TOLERANCE)));
+
+ }
+}
diff --git a/jee7schedule/src/test/java/com/baeldung/timer/WithinWindowMatcher.java b/jee7schedule/src/test/java/com/baeldung/timer/WithinWindowMatcher.java
new file mode 100644
index 0000000000..91adca6042
--- /dev/null
+++ b/jee7schedule/src/test/java/com/baeldung/timer/WithinWindowMatcher.java
@@ -0,0 +1,30 @@
+package com.baeldung.timer;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+
+class WithinWindowMatcher extends BaseMatcher {
+ private final long timeout;
+ private final long tolerance;
+
+ public WithinWindowMatcher(long timeout, long tolerance) {
+ this.timeout = timeout;
+ this.tolerance = tolerance;
+ }
+
+ @Override
+ public boolean matches(Object item) {
+ final Long actual = (Long) item;
+ return Math.abs(actual - timeout) < tolerance;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public static Matcher withinWindow(final long timeout, final long tolerance) {
+ return new WithinWindowMatcher(timeout, tolerance);
+ }
+}
diff --git a/pom.xml b/pom.xml
index 4116c0aca7..85861685ba 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,6 +23,7 @@
jooq-spring
json-path
mockito
+ jee7schedule
querydsl