Java tutorial
/** * The contents of this file are subject to the OpenMRS Public License * Version 1.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://license.openmrs.org * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * Copyright (C) OpenMRS, LLC. All Rights Reserved. */ package org.openmrs.scheduler; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.openmrs.api.context.Context; import org.openmrs.scheduler.tasks.AbstractTask; import org.openmrs.test.BaseContextSensitiveTest; import org.openmrs.util.OpenmrsClassLoader; import org.springframework.util.StringUtils; /** * TODO test all methods in ScheduleService */ public class SchedulerServiceTest extends BaseContextSensitiveTest { private static Log log = LogFactory.getLog(SchedulerServiceTest.class); // so that we can guarantee tests running accurately instead of tests interfering with the next public final Integer SAVE_TASK_LOCK = new Integer(1); // each task provides a key that will be used in this map. The value is the output private static Map<String, String> output = new HashMap<String, String>(); @Before public void setUp() throws Exception { Context.flushSession(); Collection<TaskDefinition> tasks = Context.getSchedulerService().getRegisteredTasks(); for (TaskDefinition task : tasks) { Context.getSchedulerService().shutdownTask(task); Context.getSchedulerService().deleteTask(task.getId()); } Context.flushSession(); } @Test public void shouldResolveValidTaskClass() throws Exception { String className = "org.openmrs.scheduler.tasks.TestTask"; Class c = OpenmrsClassLoader.getInstance().loadClass(className); Object o = c.newInstance(); if (o instanceof Task) assertTrue("Class " + className + " is a valid Task", true); else fail("Class " + className + " is not a valid Task"); } @Test(expected = ClassNotFoundException.class) public void shouldNotResolveInvalidClass() throws Exception { String className = "org.openmrs.scheduler.tasks.InvalidTask"; Class c = OpenmrsClassLoader.getInstance().loadClass(className); Object o = c.newInstance(); if (o instanceof Task) fail("Class " + className + " is not supposed to be a valid Task"); else assertTrue("Class " + className + " is not a valid Task", true); } /** * Longer running class used to demonstrate tasks running concurrently */ public static class ExecutePrintingTask extends AbstractTask { public void execute() { String outputKey = getTaskDefinition().getProperty("outputKey"); appendOutput(outputKey, getTaskDefinition().getProperty("id")); try { Thread.sleep(Integer.valueOf(getTaskDefinition().getProperty("delay"))); } catch (InterruptedException e) { log.error("Error generated", e); } appendOutput(outputKey, getTaskDefinition().getProperty("id")); } } /** * Helper method to append the given text to the "output" static variable map * <br/> * Map will contain string like "text, text1, text2" * * @param outputKey the key for the "output" map * @param the text to append to the value in the output map with the given key */ public synchronized static void appendOutput(String outputKey, String appendText) { if (StringUtils.hasLength(output.get(outputKey))) output.put(outputKey, output.get(outputKey) + ", " + appendText); else output.put(outputKey, appendText); } /** * Demonstrates concurrent running for tasks * * <pre> * | * SampleTask2 | ---- * SampleTask1 |------------ * |_____________ time * ^ ^ ^ ^ * Output: S-1 S-2 E-2 E-1 * </pre> */ @Test public void shouldAllowTwoTasksToRunConcurrently() throws Exception { SchedulerService schedulerService = Context.getSchedulerService(); TaskDefinition t1 = new TaskDefinition(); t1.setId(1); t1.setStartOnStartup(false); t1.setStartTime(null); t1.setTaskClass(ExecutePrintingTask.class.getName()); t1.setProperty("id", "TASK-1"); t1.setProperty("delay", "400"); // must be longer than t2's delay t1.setProperty("outputKey", "shouldAllowTwoTasksToRunConcurrently"); t1.setName("name"); t1.setRepeatInterval(5000l); TaskDefinition t2 = new TaskDefinition(); t2.setId(2); t2.setStartOnStartup(false); t2.setStartTime(null); t2.setTaskClass(ExecutePrintingTask.class.getName()); t2.setProperty("id", "TASK-2"); t2.setProperty("delay", "100"); // must be shorter than t1's delay t2.setProperty("outputKey", "shouldAllowTwoTasksToRunConcurrently"); t2.setName("name"); t2.setRepeatInterval(5000l); synchronized (SAVE_TASK_LOCK) { schedulerService.scheduleTask(t1); Thread.sleep(50); // so t2 doesn't start before t1 due to random millisecond offsets schedulerService.scheduleTask(t2); Thread.sleep(2500); // must be longer than t2's delay assertEquals("TASK-1, TASK-2, TASK-2, TASK-1", output.get("shouldAllowTwoTasksToRunConcurrently")); } } /** * Longer init'ing class for concurrent init test */ public static class SimpleTask extends AbstractTask { public void initialize(TaskDefinition config) { String outputKey = config.getProperty("outputKey"); appendOutput(outputKey, config.getProperty("id")); super.initialize(config); try { // must be less than delay before printing Thread.sleep(Integer.valueOf(config.getProperty("delay"))); } catch (InterruptedException e) { log.error("Error generated", e); } appendOutput(outputKey, config.getProperty("id")); } public void execute() { } } /** * Demonstrates concurrent initializing for tasks * * <pre> * | * SampleTask4 | ---- * SampleTask3 |------------ * |_____________ time * ^ ^ ^ ^ * Output: S-3 S-4 E-4 E-3 * </pre> */ @Test public void shouldAllowTwoTasksInitMethodsToRunConcurrently() throws Exception { SchedulerService schedulerService = Context.getSchedulerService(); TaskDefinition t3 = new TaskDefinition(); t3.setStartOnStartup(false); t3.setStartTime(null); // so it starts immediately t3.setTaskClass(SimpleTask.class.getName()); t3.setProperty("id", "TASK-3"); t3.setProperty("delay", "300"); // must be longer than t4's delay t3.setProperty("outputKey", "shouldAllowTwoTasksInitMethodsToRunConcurrently"); t3.setName("name"); t3.setRepeatInterval(5000l); TaskDefinition t4 = new TaskDefinition(); t4.setStartOnStartup(false); t4.setStartTime(null); // so it starts immediately t4.setTaskClass(SimpleTask.class.getName()); t4.setProperty("id", "TASK-4"); t4.setProperty("delay", "100"); t4.setProperty("outputKey", "shouldAllowTwoTasksInitMethodsToRunConcurrently"); t4.setName("name"); t4.setRepeatInterval(5000l); // both of these tasks start immediately synchronized (SAVE_TASK_LOCK) { schedulerService.scheduleTask(t3); // starts first, ends last schedulerService.scheduleTask(t4); // starts last, ends first } Thread.sleep(500); // must be greater than task3 delay so that it prints out its end assertEquals("TASK-3, TASK-4, TASK-4, TASK-3", output.get("shouldAllowTwoTasksInitMethodsToRunConcurrently")); // cleanup schedulerService.shutdownTask(t3); schedulerService.shutdownTask(t4); } public static class SampleTask5 extends AbstractTask { public void initialize(TaskDefinition config) { String outputKey = config.getProperty("outputKey"); appendOutput(outputKey, "INIT-START-5"); super.initialize(config); try { Thread.sleep(700); } catch (InterruptedException e) { log.error("Error generated", e); } appendOutput(outputKey, "INIT-END-5"); } public void execute() { String outputKey = getTaskDefinition().getProperty("outputKey"); appendOutput(outputKey, "IN EXECUTE"); } } /** * Demonstrates that initialization of a task is accomplished before its execution without * interleaving, which is a non-trivial behavior in the presence of a threaded initialization * method (as implemented in TaskThreadedInitializationWrapper) * * <pre> * | * SampleTask5 |------------ * |_____________ time * ^ ^ ^ ^ * Output: IS IE S E * </pre> */ @Test public void shouldNotAllowTaskExecuteToRunBeforeInitializationIsComplete() throws Exception { SchedulerService schedulerService = Context.getSchedulerService(); TaskDefinition t5 = new TaskDefinition(); t5.setId(5); t5.setStartOnStartup(false); t5.setStartTime(null); // immediate start t5.setTaskClass(SampleTask5.class.getName()); t5.setProperty("outputKey", "shouldNotAllowTaskExecuteToRunBeforeInitializationIsComplete"); t5.setName("name"); t5.setRepeatInterval(5000l); synchronized (SAVE_TASK_LOCK) { schedulerService.scheduleTask(t5); Thread.sleep(2500); assertEquals("INIT-START-5, INIT-END-5, IN EXECUTE", output.get("shouldNotAllowTaskExecuteToRunBeforeInitializationIsComplete")); } } @Test public void saveTask_shouldSaveTaskToTheDatabase() throws Exception { SchedulerService service = Context.getSchedulerService(); TaskDefinition def = new TaskDefinition(); final String TASK_NAME = "This is my test! 123459876"; def.setName(TASK_NAME); def.setStartOnStartup(false); def.setRepeatInterval(10L); def.setTaskClass(ExecutePrintingTask.class.getName()); synchronized (SAVE_TASK_LOCK) { int size = service.getRegisteredTasks().size(); service.saveTask(def); Assert.assertEquals(size + 1, service.getRegisteredTasks().size()); } def = service.getTaskByName(TASK_NAME); Assert.assertEquals(Context.getAuthenticatedUser().getUserId(), def.getCreator().getUserId()); } /** * Sample task that does not extend AbstractTask */ public static class BareTask implements Task { public static ArrayList outputList = new ArrayList(); public void execute() { synchronized (outputList) { outputList.add("TEST"); } } public TaskDefinition getTaskDefinition() { return null; } public void initialize(TaskDefinition definition) { } public boolean isExecuting() { return false; } public void shutdown() { } } /** * Task which does not return TaskDefinition in getTaskDefinition should run without throwing * exceptions. * * @throws Exception */ @Test public void shouldNotThrowExceptionWhenTaskDefinitionIsNull() throws Exception { SchedulerService schedulerService = Context.getSchedulerService(); TaskDefinition td = new TaskDefinition(); td.setId(10); td.setName("Task"); td.setStartOnStartup(false); td.setTaskClass(BareTask.class.getName()); td.setStartTime(null); td.setName("name"); td.setRepeatInterval(5000l); synchronized (SAVE_TASK_LOCK) { schedulerService.scheduleTask(td); } Thread.sleep(500); assertTrue(BareTask.outputList.contains("TEST")); } /** * Task opens a session and stores the execution time. */ public static class SessionTask extends AbstractTask { public void execute() { try { // something would happen here... actualExecutionTime = System.currentTimeMillis(); } finally { } } } public static Long actualExecutionTime; /** * Check saved last execution time. */ @Test public void shouldSaveLastExecutionTime() throws Exception { final String NAME = "Session Task"; SchedulerService service = Context.getSchedulerService(); TaskDefinition td = new TaskDefinition(); td.setName(NAME); td.setStartOnStartup(false); td.setTaskClass(SessionTask.class.getName()); td.setStartTime(null); td.setRepeatInterval(new Long(0));//0 indicates single execution synchronized (SAVE_TASK_LOCK) { service.saveTask(td); service.scheduleTask(td); } // refetch the task td = service.getTaskByName(NAME); // sleep a while until the task has executed, up to 30 times for (int x = 0; x < 30 && (actualExecutionTime == null || td.getLastExecutionTime() == null); x++) Thread.sleep(200); Assert.assertNotNull( "The actualExecutionTime variable is null, so either the SessionTask.execute method hasn't finished or didn't get run", actualExecutionTime); assertEquals("Last execution time in seconds is wrong", actualExecutionTime.longValue() / 1000, td.getLastExecutionTime().getTime() / 1000, 1); } }