/** * 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. */ package org.apache.openejb.timer; import junit.framework.TestCase; import org.apache.openejb.OpenEJB; import org.apache.openejb.assembler.classic.Assembler; import org.apache.openejb.assembler.classic.EjbJarInfo; import org.apache.openejb.assembler.classic.ProxyFactoryInfo; import org.apache.openejb.assembler.classic.SecurityServiceInfo; import org.apache.openejb.assembler.classic.TransactionServiceInfo; import org.apache.openejb.config.ConfigurationFactory; import org.apache.openejb.core.LocalInitialContextFactory; import org.apache.openejb.jee.EjbJar; import org.apache.openejb.jee.NamedMethod; import org.apache.openejb.jee.StatefulBean; import org.apache.openejb.jee.StatelessBean; import org.apache.openejb.jee.Timer; import org.apache.openejb.jee.TimerSchedule; import org.junit.Assert; import javax.ejb.Local; import javax.ejb.Schedule; import javax.ejb.Stateful; import javax.ejb.Stateless; import javax.ejb.TimedObject; import javax.ejb.Timeout; import javax.interceptor.AroundTimeout; import javax.interceptor.InvocationContext; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * @version $Rev$ $Date$ */ public class ScheduleTest extends TestCase { private static final List<Call> result = new ArrayList<Call>(); private static final CountDownLatch countDownLatch = new CountDownLatch(3); @Override protected void tearDown() throws Exception { OpenEJB.destroy(); } public void testSchedule() throws Exception { System.setProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY, LocalInitialContextFactory.class.getName()); final Assembler assembler = new Assembler(); final ConfigurationFactory config = new ConfigurationFactory(); assembler.createProxyFactory(config.configureService(ProxyFactoryInfo.class)); assembler.createTransactionManager(config.configureService(TransactionServiceInfo.class)); assembler.createSecurityService(config.configureService(SecurityServiceInfo.class)); final EjbJar ejbJar = new EjbJar(); //Configure schedule by deployment plan final StatelessBean subBeanA = new StatelessBean(SubBeanA.class); final Timer subBeanATimer = new Timer(); subBeanATimer.setTimeoutMethod(new NamedMethod("subBeanA", "javax.ejb.Timer")); final TimerSchedule timerScheduleA = new TimerSchedule(); timerScheduleA.setSecond("2"); timerScheduleA.setMinute("*"); timerScheduleA.setHour("*"); subBeanATimer.setSchedule(timerScheduleA); subBeanATimer.setInfo("SubBeanAInfo"); subBeanA.getTimer().add(subBeanATimer); ejbJar.addEnterpriseBean(subBeanA); //Configure schedule by annotation final StatelessBean subBeanB = new StatelessBean(SubBeanB.class); ejbJar.addEnterpriseBean(subBeanB); //Override aroundTimeout annotation by deployment plan final StatelessBean subBeanC = new StatelessBean(SubBeanC.class); final Timer subBeanCTimer = new Timer(); subBeanCTimer.setTimeoutMethod(new NamedMethod("subBeanC", "javax.ejb.Timer")); final TimerSchedule timerScheduleC = new TimerSchedule(); timerScheduleC.setSecond("2"); timerScheduleC.setMinute("*"); timerScheduleC.setHour("*"); subBeanCTimer.setSchedule(timerScheduleC); subBeanCTimer.setInfo("SubBeanCInfo"); subBeanC.getTimer().add(subBeanCTimer); ejbJar.addEnterpriseBean(subBeanC); final StatefulBean subBeanM = new StatefulBean(SubBeanM.class); ejbJar.addEnterpriseBean(subBeanM); final EjbJarInfo ejbJarInfo = config.configureApplication(ejbJar); assembler.createApplication(ejbJarInfo); countDownLatch.await(1L, TimeUnit.MINUTES); //A better way for validation ? int beforeAroundInvocationCount = 0; int afterAroundInvocationCount = 0; int timeoutInvocationCount = 0; final int size; synchronized (result) { size = result.size(); for (final Call call : result) { switch (call) { case BEAN_BEFORE_AROUNDTIMEOUT: beforeAroundInvocationCount++; break; case BEAN_AFTER_AROUNDTIMEOUT: afterAroundInvocationCount++; break; case TIMEOUT: timeoutInvocationCount++; break; } } } assertEquals(3, beforeAroundInvocationCount); assertEquals(3, afterAroundInvocationCount); assertEquals(3, timeoutInvocationCount); assertEquals(9, size); } public static interface BeanInterface { public void simpleMethod(); } public static class BaseBean implements BeanInterface { @Override public void simpleMethod() { } @AroundTimeout public Object beanTimeoutAround(final InvocationContext context) throws Exception { synchronized (result) { assertNotNull(context.getTimer()); result.add(Call.BEAN_BEFORE_AROUNDTIMEOUT); Object ret = null; try { ret = context.proceed(); } catch (final Throwable t) { throw new Exception(t); } finally { result.add(Call.BEAN_AFTER_AROUNDTIMEOUT); countDownLatch.countDown(); } return ret; } } } @Stateless @Local(BeanInterface.class) public static class SubBeanA extends BaseBean implements TimedObject { public void subBeanA(final javax.ejb.Timer timer) { synchronized (result) { assertEquals("SubBeanAInfo", timer.getInfo()); result.add(Call.TIMEOUT); } } @Override public void ejbTimeout(final javax.ejb.Timer arg0) { Assert.fail("This method should not be invoked, we might confuse the auto-created timers and timeout timer"); } } @Stateful @Local(BeanInterface.class) public static class SubBeanM extends BaseBean implements TimedObject { @Schedule(second = "2", minute = "*", hour = "*", info = "SubBeanBInfo") public void subBeanA(final javax.ejb.Timer timer) { synchronized (result) { assertEquals("SubBeanAInfo", timer.getInfo()); result.add(Call.TIMEOUT); } } @Override public void ejbTimeout(final javax.ejb.Timer arg0) { fail("This method should not be invoked, we might confuse the auto-created timers and timeout timer"); } } @Stateless @Local(BeanInterface.class) public static class SubBeanB extends BaseBean { @Schedule(second = "2", minute = "*", hour = "*", info = "SubBeanBInfo") public void subBeanB(final javax.ejb.Timer timer) { synchronized (result) { assertEquals("SubBeanBInfo", timer.getInfo()); result.add(Call.TIMEOUT); } } @Timeout public void ejbT(final javax.ejb.Timer timer) { fail("This method should not be invoked, we might confuse the auto-created timers and timeout timer"); } } @Stateless @Local(BeanInterface.class) public static class SubBeanC extends BaseBean { @Schedule(info = "badValue") public void subBeanC(final javax.ejb.Timer timer) { synchronized (result) { assertEquals("SubBeanCInfo", timer.getInfo()); result.add(Call.TIMEOUT); } } @Timeout public void ejbT() { fail("This method should not be invoked, we might confuse the auto-created timers and timeout timer"); } } public static enum Call { BEAN_TIMEOUT, BEAN_BEFORE_AROUNDTIMEOUT, BEAN_AFTER_AROUNDTIMEOUT, BAD_VALUE, TIMEOUT } }