SLING-5831 : Support different thread pools for scheduled tasks
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1752822 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index 6f399f4..a94606b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -160,7 +160,7 @@
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.commons.threads</artifactId>
- <version>3.0.0</version>
+ <version>3.2.6</version>
<scope>provided</scope>
</dependency>
<dependency>
diff --git a/src/main/java/org/apache/sling/commons/scheduler/ScheduleOptions.java b/src/main/java/org/apache/sling/commons/scheduler/ScheduleOptions.java
index 7395859..0b5e6f1 100644
--- a/src/main/java/org/apache/sling/commons/scheduler/ScheduleOptions.java
+++ b/src/main/java/org/apache/sling/commons/scheduler/ScheduleOptions.java
@@ -91,6 +91,7 @@
*
* If {@link #onLeaderOnly(boolean)} or {@link #onSingleInstanceOnly(boolean)} has been called before,
* that option is reset and overwritten by the value of this method.
+ *
* @param slingIds Array of Sling IDs this job should run on
* @return The schedule options.
*/
@@ -99,12 +100,13 @@
/**
* Define the thread pool to be used.
* Scheduled jobs can run using different thread pools. By default, the default
- * thread pool from the thread pool manager service is used.
- * If a thread pool name is specified, a pool with that name will be get from
- * the thread pool manager. If such a pool does not exist, it will be created.
+ * thread pool of the scheduler is used.
+ * If a thread pool name is specified, it is up to the scheduler to put the job
+ * in the defined thread pool or any other thread pool.
* This option must be used with special care as it might create new thread pools.
* It should only be used if there is a good reason to not use the default thread
* pool.
+ *
* @param name The thread pool name
* @return The schedule options.
* @since 2.5.0
diff --git a/src/main/java/org/apache/sling/commons/scheduler/Scheduler.java b/src/main/java/org/apache/sling/commons/scheduler/Scheduler.java
index 83af956..cf87f84 100644
--- a/src/main/java/org/apache/sling/commons/scheduler/Scheduler.java
+++ b/src/main/java/org/apache/sling/commons/scheduler/Scheduler.java
@@ -104,9 +104,9 @@
/**
* Name of the configuration property to define the thread pool to be used.
* Scheduled jobs can run using different thread pools. By default, the default
- * thread pool from the thread pool manager service is used.
- * If a thread pool name is specified, a pool with that name will be get from
- * the thread pool manager. If such a pool does not exist, it will be created.
+ * thread pool of the scheduler is used.
+ * If a thread pool name is specified, it is up to the scheduler to put the job
+ * in the defined thread pool or any other thread pool.
* This option must be used with special care as it might create new thread pools.
* It should only be used if there is a good reason to not use the default thread
* pool.
diff --git a/src/main/java/org/apache/sling/commons/scheduler/impl/QuartzScheduler.java b/src/main/java/org/apache/sling/commons/scheduler/impl/QuartzScheduler.java
index 20aa9a5..070ab4f 100644
--- a/src/main/java/org/apache/sling/commons/scheduler/impl/QuartzScheduler.java
+++ b/src/main/java/org/apache/sling/commons/scheduler/impl/QuartzScheduler.java
@@ -17,7 +17,9 @@
package org.apache.sling.commons.scheduler.impl;
import java.io.Serializable;
+import java.util.Collections;
import java.util.Date;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
@@ -28,6 +30,7 @@
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.PropertyUnbounded;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.commons.scheduler.Job;
@@ -62,9 +65,6 @@
@Service(value=QuartzScheduler.class)
public class QuartzScheduler implements BundleListener {
- /** Default logger. */
- private final Logger logger = LoggerFactory.getLogger(this.getClass());
-
/** Map key for the job object */
static final String DATA_MAP_OBJECT = "QuartzJobScheduler.Object";
@@ -86,16 +86,31 @@
/** Map key for the bundle information (Long). */
static final String DATA_MAP_SERVICE_ID = "QuartzJobScheduler.serviceId";
- /** The quartz scheduler. */
- private volatile SchedulerProxy scheduler;
+ @Property(label="Thread Pool Name",
+ description="The name of a configured thread pool - if no name is configured " +
+ "the default pool is used.")
+ private static final String PROPERTY_POOL_NAME = "poolName";
+
+ @Property(label="Allowed Thread Pools",
+ description="The names of thread pools that are allowed to be used by jobs. " +
+ "If a job is using a pool not in this list, the default pool is used.",
+ unbounded=PropertyUnbounded.ARRAY)
+ private static final String PROPERTY_ALLOWED_POOLS = "allowedPoolNames";
+
+ /** Default logger. */
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Reference
private ThreadPoolManager threadPoolManager;
- @Property(label="Thread Pool Name",
- description="The name of a configured thread pool - if no name is configured " +
- "the default pool is used.")
- private static final String PROPERTY_POOL_NAME = "poolName";
+ /** The quartz schedulers. */
+ private final Map<String, SchedulerProxy> schedulers = new HashMap<String, SchedulerProxy>();
+
+ private volatile String defaultPoolName;
+
+ private volatile String[] allowedPoolNames;
+
+ private volatile boolean active;
/**
* Activate this component.
@@ -103,22 +118,32 @@
* @throws Exception
*/
@Activate
- protected void activate(final BundleContext ctx, final Map<String, Object> props) throws Exception {
+ protected void activate(final BundleContext ctx, final Map<String, Object> props) {
// SLING-2261 Prevent Quartz from checking for updates
System.setProperty("org.terracotta.quartz.skipUpdateCheck", Boolean.TRUE.toString());
final Object poolNameObj = props.get(PROPERTY_POOL_NAME);
- final String poolName;
if ( poolNameObj != null && poolNameObj.toString().trim().length() > 0 ) {
- poolName = poolNameObj.toString().trim();
+ this.defaultPoolName = poolNameObj.toString().trim();
} else {
- poolName = null;
+ this.defaultPoolName = ThreadPoolManager.DEFAULT_THREADPOOL_NAME;
}
-
+ final Object value = props.get(PROPERTY_ALLOWED_POOLS);
+ if ( value instanceof String[] ) {
+ this.allowedPoolNames = (String[])value;
+ } else if ( value != null ) {
+ this.allowedPoolNames = new String[] {value.toString()};
+ }
+ for(int i=0;i<this.allowedPoolNames.length;i++) {
+ if ( this.allowedPoolNames[i] == null ) {
+ this.allowedPoolNames[i] = "";
+ } else {
+ this.allowedPoolNames[i] = this.allowedPoolNames[i].trim();
+ }
+ }
ctx.addBundleListener(this);
- // start scheduler
- this.scheduler = new SchedulerProxy(this.threadPoolManager, getThreadPoolName(poolName));
+ this.active = true;
}
/**
@@ -129,25 +154,42 @@
protected void deactivate(final BundleContext ctx) {
ctx.removeBundleListener(this);
- final SchedulerProxy s = this.scheduler;
- this.scheduler = null;
- if ( s != null ) {
- s.dispose();
+ final Map<String, SchedulerProxy> proxies;
+ synchronized ( this.schedulers ) {
+ this.active = false;
+ proxies = new HashMap<String, SchedulerProxy>(this.schedulers);
+ this.schedulers.clear();
+ }
+ for(final SchedulerProxy proxy : proxies.values()) {
+ proxy.dispose();
}
}
private String getThreadPoolName(final String name) {
if ( name == null || name.trim().isEmpty() ) {
- return ThreadPoolManager.DEFAULT_THREADPOOL_NAME;
+ return this.defaultPoolName;
}
- return name.trim();
+ for(final String n : this.allowedPoolNames) {
+ if ( name.trim().equals(n) ) {
+ return n;
+ }
+ }
+ return this.defaultPoolName;
}
- private org.quartz.Scheduler getScheduler(final SchedulerProxy proxy) {
- if ( proxy != null ) {
- return proxy.getScheduler();
+ private SchedulerProxy getScheduler(final String pName) throws SchedulerException {
+ final String poolName = getThreadPoolName(pName);
+ SchedulerProxy proxy = null;
+ synchronized ( this.schedulers ) {
+ if ( this.active ) {
+ proxy = this.schedulers.get(poolName);
+ if ( proxy == null ) {
+ proxy = new SchedulerProxy(this.threadPoolManager, poolName);
+ this.schedulers.put(poolName, proxy);
+ }
+ }
}
- return null;
+ return proxy;
}
/**
@@ -158,15 +200,22 @@
if ( event.getType() == BundleEvent.STOPPED ) {
final Long bundleId = event.getBundle().getBundleId();
- final org.quartz.Scheduler s = getScheduler(this.scheduler);
- if ( s != null ) {
- synchronized ( this ) {
+ final Map<String, SchedulerProxy> proxies;
+ synchronized ( this.schedulers ) {
+ if ( this.active ) {
+ proxies = new HashMap<String, SchedulerProxy>(this.schedulers);
+ } else {
+ proxies = Collections.emptyMap();
+ }
+ }
+ for(final SchedulerProxy proxy : proxies.values()) {
+ synchronized ( proxy ) {
try {
- final List<String> groups = s.getJobGroupNames();
+ final List<String> groups = proxy.getScheduler().getJobGroupNames();
for(final String group : groups) {
- final Set<JobKey> keys = s.getJobKeys(GroupMatcher.jobGroupEquals(group));
+ final Set<JobKey> keys = proxy.getScheduler().getJobKeys(GroupMatcher.jobGroupEquals(group));
for(final JobKey key : keys) {
- final JobDetail detail = s.getJobDetail(key);
+ final JobDetail detail = proxy.getScheduler().getJobDetail(key);
if ( detail != null ) {
final String jobName = (String) detail.getJobDataMap().get(QuartzScheduler.DATA_MAP_NAME);
final Object job = detail.getJobDataMap().get(QuartzScheduler.DATA_MAP_OBJECT);
@@ -174,7 +223,7 @@
if ( jobName != null && job != null ) {
final Long jobBundleId = (Long) detail.getJobDataMap().get(QuartzScheduler.DATA_MAP_BUNDLE_ID);
if ( jobBundleId != null && jobBundleId.equals(bundleId) ) {
- s.deleteJob(key);
+ proxy.getScheduler().deleteJob(key);
this.logger.debug("Unscheduling job with name {}", jobName);
}
}
@@ -365,30 +414,36 @@
/**
* @see org.apache.sling.commons.scheduler.Scheduler#removeJob(java.lang.String)
*/
- public void removeJob(final Long bundleId, final String name) throws NoSuchElementException {
+ public void removeJob(final Long bundleId, final String jobName) throws NoSuchElementException {
// as this method might be called from unbind and during
// unbind a deactivate could happen, we check the scheduler first
- final org.quartz.Scheduler s = this.getScheduler(this.scheduler);
- if ( s != null ) {
- synchronized ( this ) {
+ final Map<String, SchedulerProxy> proxies;
+ synchronized ( this.schedulers ) {
+ if ( this.active ) {
+ proxies = new HashMap<String, SchedulerProxy>(this.schedulers);
+ } else {
+ proxies = Collections.emptyMap();
+ }
+ }
+ for(final SchedulerProxy proxy : proxies.values()) {
+ synchronized ( proxy ) {
try {
- s.deleteJob(JobKey.jobKey(name));
- this.logger.debug("Unscheduling job with name {}", name);
- } catch (final SchedulerException se) {
- throw new NoSuchElementException(se.getMessage());
+ final JobKey key = JobKey.jobKey(jobName);
+ final JobDetail jobdetail = proxy.getScheduler().getJobDetail(key);
+ if (jobdetail != null) {
+ proxy.getScheduler().deleteJob(key);
+ this.logger.debug("Unscheduling job with name {}", jobName);
+ return;
+ }
+ } catch (final SchedulerException ignored) {
+ // ignore
}
}
}
+ throw new NoSuchElementException("No job found with name " + jobName);
}
- /** Used by the web console plugin. */
- org.quartz.Scheduler getScheduler() {
- return this.getScheduler(this.scheduler);
- }
-
-
-
- /**
+ /**
* @see org.apache.sling.commons.scheduler.Scheduler#NOW()
*/
public ScheduleOptions NOW() {
@@ -487,19 +542,24 @@
* @see org.apache.sling.commons.scheduler.Scheduler#unschedule(java.lang.String)
*/
public boolean unschedule(final Long bundleId, final String jobName) {
- final org.quartz.Scheduler s = this.getScheduler(this.scheduler);
- if ( jobName != null && s != null ) {
- synchronized ( this ) {
- try {
- final JobKey key = JobKey.jobKey(jobName);
- final JobDetail jobdetail = s.getJobDetail(key);
- if (jobdetail != null) {
- s.deleteJob(key);
- this.logger.debug("Unscheduling job with name {}", jobName);
- return true;
+ if ( jobName != null ) {
+ final Map<String, SchedulerProxy> proxies;
+ synchronized ( this.schedulers ) {
+ proxies = new HashMap<String, SchedulerProxy>(this.schedulers);
+ }
+ for(final SchedulerProxy proxy : proxies.values()) {
+ synchronized ( proxy ) {
+ try {
+ final JobKey key = JobKey.jobKey(jobName);
+ final JobDetail jobdetail = proxy.getScheduler().getJobDetail(key);
+ if (jobdetail != null) {
+ proxy.getScheduler().deleteJob(key);
+ this.logger.debug("Unscheduling job with name {}", jobName);
+ return true;
+ }
+ } catch (final SchedulerException ignored) {
+ // ignore
}
- } catch (final SchedulerException ignored) {
- // ignore
}
}
}
@@ -526,25 +586,16 @@
// as this method might be called from unbind and during
// unbind a deactivate could happen, we check the scheduler first
- final org.quartz.Scheduler s = this.getScheduler(this.scheduler);
- if ( s == null ) {
+ final SchedulerProxy proxy = this.getScheduler(opts.threadPoolName);
+ if ( proxy == null ) {
throw new IllegalStateException("Scheduler is not available anymore.");
}
- synchronized ( this ) {
+ synchronized ( proxy ) {
final String name;
if ( opts.name != null ) {
// if there is already a job with the name, remove it first
- try {
- final JobKey key = JobKey.jobKey(opts.name);
- final JobDetail jobdetail = s.getJobDetail(key);
- if (jobdetail != null) {
- s.deleteJob(key);
- this.logger.debug("Unscheduling job with name {}", opts.name);
- }
- } catch (final SchedulerException ignored) {
- // ignore
- }
+ this.unschedule(bundleId, opts.name);
name = opts.name;
} else {
name = job.getClass().getName() + ':' + UUID.randomUUID();
@@ -558,7 +609,17 @@
final JobDetail detail = this.createJobDetail(name, jobDataMap, opts.canRunConcurrently);
this.logger.debug("Scheduling job {} with name {} and trigger {}", new Object[] {job, name, trigger});
- s.scheduleJob(detail, trigger);
+ proxy.getScheduler().scheduleJob(detail, trigger);
+ }
+ }
+
+ /**
+ * This is used by the web console plugin
+ * @return All current schedulers
+ */
+ Map<String, SchedulerProxy> getSchedulers() {
+ synchronized ( this.schedulers ) {
+ return new HashMap<String, SchedulerProxy>(this.schedulers);
}
}
}
diff --git a/src/main/java/org/apache/sling/commons/scheduler/impl/WebConsolePrinter.java b/src/main/java/org/apache/sling/commons/scheduler/impl/WebConsolePrinter.java
index b4dfefd..84e643d 100644
--- a/src/main/java/org/apache/sling/commons/scheduler/impl/WebConsolePrinter.java
+++ b/src/main/java/org/apache/sling/commons/scheduler/impl/WebConsolePrinter.java
@@ -21,6 +21,7 @@
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import org.apache.felix.scr.annotations.Component;
@@ -64,93 +65,99 @@
public void printConfiguration(PrintWriter pw) {
pw.println(HEADLINE);
pw.println();
- final Scheduler s = this.scheduler.getScheduler();
- if ( s != null ) {
+ final Map<String, SchedulerProxy> proxies = this.scheduler.getSchedulers();
+ if ( !proxies.isEmpty() ) {
pw.println("Status : active");
- try {
- pw.print ("Name : ");
- pw.println(s.getSchedulerName());
- pw.print ("Id : ");
- pw.println(s.getSchedulerInstanceId());
- pw.println();
- final List<String> groups = s.getJobGroupNames();
- for(final String group : groups) {
- final Set<JobKey> keys = s.getJobKeys(GroupMatcher.jobGroupEquals(group));
- for(final JobKey key : keys) {
- final JobDetail detail = s.getJobDetail(key);
- final String jobName = (String) detail.getJobDataMap().get(QuartzScheduler.DATA_MAP_NAME);
- final Object job = detail.getJobDataMap().get(QuartzScheduler.DATA_MAP_OBJECT);
- // only print jobs started through the sling scheduler
- if ( jobName != null && job != null ) {
- pw.print("Job : ");
- pw.print(detail.getJobDataMap().get(QuartzScheduler.DATA_MAP_NAME));
- if ( detail.getDescription() != null && detail.getDescription().length() > 0 ) {
- pw.print(" (");
- pw.print(detail.getDescription());
- pw.print(")");
- }
- pw.print(", class: ");
- pw.print(job.getClass().getName());
- pw.print(", concurrent: ");
- pw.print(!detail.isConcurrentExectionDisallowed());
- final String[] runOn = (String[])detail.getJobDataMap().get(QuartzScheduler.DATA_MAP_RUN_ON);
- if ( runOn != null ) {
- pw.print(", runOn: ");
- pw.print(Arrays.toString(runOn));
- // check run on information
- if ( runOn.length == 1 &&
- (org.apache.sling.commons.scheduler.Scheduler.VALUE_RUN_ON_LEADER.equals(runOn[0]) || org.apache.sling.commons.scheduler.Scheduler.VALUE_RUN_ON_SINGLE.equals(runOn[0])) ) {
- if ( QuartzJobExecutor.DISCOVERY_AVAILABLE.get() ) {
- if ( QuartzJobExecutor.DISCOVERY_INFO_AVAILABLE.get() ) {
- if ( !QuartzJobExecutor.IS_LEADER.get() ) {
- pw.print(" (inactive: not leader)");
+ for(final Map.Entry<String, SchedulerProxy> entry : proxies.entrySet()) {
+ final Scheduler s = entry.getValue().getScheduler();
+ try {
+ pw.print ("Name : ");
+ pw.println(s.getSchedulerName());
+ pw.print ("ThreadPool: ");
+ pw.println(entry.getKey());
+ pw.print ("Id : ");
+ pw.println(s.getSchedulerInstanceId());
+ pw.println();
+ final List<String> groups = s.getJobGroupNames();
+ for(final String group : groups) {
+ final Set<JobKey> keys = s.getJobKeys(GroupMatcher.jobGroupEquals(group));
+ for(final JobKey key : keys) {
+ final JobDetail detail = s.getJobDetail(key);
+ final String jobName = (String) detail.getJobDataMap().get(QuartzScheduler.DATA_MAP_NAME);
+ final Object job = detail.getJobDataMap().get(QuartzScheduler.DATA_MAP_OBJECT);
+ // only print jobs started through the sling scheduler
+ if ( jobName != null && job != null ) {
+ pw.print("Job : ");
+ pw.print(detail.getJobDataMap().get(QuartzScheduler.DATA_MAP_NAME));
+ if ( detail.getDescription() != null && detail.getDescription().length() > 0 ) {
+ pw.print(" (");
+ pw.print(detail.getDescription());
+ pw.print(")");
+ }
+ pw.print(", class: ");
+ pw.print(job.getClass().getName());
+ pw.print(", concurrent: ");
+ pw.print(!detail.isConcurrentExectionDisallowed());
+ final String[] runOn = (String[])detail.getJobDataMap().get(QuartzScheduler.DATA_MAP_RUN_ON);
+ if ( runOn != null ) {
+ pw.print(", runOn: ");
+ pw.print(Arrays.toString(runOn));
+ // check run on information
+ if ( runOn.length == 1 &&
+ (org.apache.sling.commons.scheduler.Scheduler.VALUE_RUN_ON_LEADER.equals(runOn[0]) || org.apache.sling.commons.scheduler.Scheduler.VALUE_RUN_ON_SINGLE.equals(runOn[0])) ) {
+ if ( QuartzJobExecutor.DISCOVERY_AVAILABLE.get() ) {
+ if ( QuartzJobExecutor.DISCOVERY_INFO_AVAILABLE.get() ) {
+ if ( !QuartzJobExecutor.IS_LEADER.get() ) {
+ pw.print(" (inactive: not leader)");
+ }
+ } else {
+ pw.print(" (inactive: no discovery info)");
}
} else {
- pw.print(" (inactive: no discovery info)");
+ pw.print(" (inactive: no discovery)");
}
- } else {
- pw.print(" (inactive: no discovery)");
- }
- } else { // sling IDs
- final String myId = QuartzJobExecutor.SLING_ID;
- if ( myId == null ) {
- pw.print(" (inactive: no Sling settings)");
- } else {
- boolean schedule = false;
- for(final String id : runOn ) {
- if ( myId.equals(id) ) {
- schedule = true;
- break;
+ } else { // sling IDs
+ final String myId = QuartzJobExecutor.SLING_ID;
+ if ( myId == null ) {
+ pw.print(" (inactive: no Sling settings)");
+ } else {
+ boolean schedule = false;
+ for(final String id : runOn ) {
+ if ( myId.equals(id) ) {
+ schedule = true;
+ break;
+ }
+ }
+ if ( !schedule ) {
+ pw.print(" (inactive: Sling ID)");
}
}
- if ( !schedule ) {
- pw.print(" (inactive: Sling ID)");
- }
- }
- } }
- final Long bundleId = (Long)detail.getJobDataMap().get(QuartzScheduler.DATA_MAP_BUNDLE_ID);
- if ( bundleId != null ) {
- pw.print(", bundleId: ");
- pw.print(String.valueOf(bundleId));
- }
- final Long serviceId = (Long)detail.getJobDataMap().get(QuartzScheduler.DATA_MAP_SERVICE_ID);
- if ( serviceId != null ) {
- pw.print(", serviceId: ");
- pw.print(String.valueOf(serviceId));
- }
- pw.println();
- for(final Trigger trigger : s.getTriggersOfJob(key)) {
- pw.print("Trigger : ");
- pw.print(trigger);
+ } }
+ final Long bundleId = (Long)detail.getJobDataMap().get(QuartzScheduler.DATA_MAP_BUNDLE_ID);
+ if ( bundleId != null ) {
+ pw.print(", bundleId: ");
+ pw.print(String.valueOf(bundleId));
+ }
+ final Long serviceId = (Long)detail.getJobDataMap().get(QuartzScheduler.DATA_MAP_SERVICE_ID);
+ if ( serviceId != null ) {
+ pw.print(", serviceId: ");
+ pw.print(String.valueOf(serviceId));
+ }
+ pw.println();
+ for(final Trigger trigger : s.getTriggersOfJob(key)) {
+ pw.print("Trigger : ");
+ pw.print(trigger);
+ pw.println();
+ }
pw.println();
}
- pw.println();
}
}
+ } catch ( final SchedulerException se ) {
+ pw.print ("Unable to print complete configuration: ");
+ pw.println(se.getMessage());
}
- } catch ( final SchedulerException se ) {
- pw.print ("Unable to print complete configuration: ");
- pw.println(se.getMessage());
+ pw.println();
}
} else {
pw.println("Status : not active");
diff --git a/src/test/java/org/apache/sling/commons/scheduler/impl/ActivatedQuartzSchedulerFactory.java b/src/test/java/org/apache/sling/commons/scheduler/impl/ActivatedQuartzSchedulerFactory.java
index 14316e0..f0aa0cd 100644
--- a/src/test/java/org/apache/sling/commons/scheduler/impl/ActivatedQuartzSchedulerFactory.java
+++ b/src/test/java/org/apache/sling/commons/scheduler/impl/ActivatedQuartzSchedulerFactory.java
@@ -16,21 +16,22 @@
*/
package org.apache.sling.commons.scheduler.impl;
-import org.apache.sling.commons.threads.impl.DefaultThreadPoolManager;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-
import java.lang.reflect.Field;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
+import org.apache.sling.commons.threads.impl.DefaultThreadPoolManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+
/**
* This is just a class with helper static method,
* since we need an activated QuartzScheduler in many tests.
*/
class ActivatedQuartzSchedulerFactory {
+
public static QuartzScheduler create(BundleContext context, String poolName) throws Exception {
QuartzScheduler quartzScheduler = null;
if (context != null) {
@@ -45,6 +46,9 @@
Map<String, Object> scheduleActivationProps = new HashMap<String, Object>();
scheduleActivationProps.put("poolName", poolName == null ? "testName" : poolName);
+ if ( poolName != null ) {
+ scheduleActivationProps.put("allowedPoolNames", new String[] {"testName", "allowed"});
+ }
quartzScheduler.activate(context, scheduleActivationProps);
context.registerService("scheduler", quartzScheduler, props);
diff --git a/src/test/java/org/apache/sling/commons/scheduler/impl/QuartzJobExecutorTest.java b/src/test/java/org/apache/sling/commons/scheduler/impl/QuartzJobExecutorTest.java
index c26659e..4a5bb32 100644
--- a/src/test/java/org/apache/sling/commons/scheduler/impl/QuartzJobExecutorTest.java
+++ b/src/test/java/org/apache/sling/commons/scheduler/impl/QuartzJobExecutorTest.java
@@ -23,7 +23,6 @@
import static org.mockito.Mockito.when;
import java.io.Serializable;
-import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
@@ -40,12 +39,10 @@
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
-import org.quartz.Scheduler;
import org.quartz.SchedulerException;
@RunWith(MockitoJUnitRunner.class)
public class QuartzJobExecutorTest {
- private Scheduler scheduler;
private BundleContext context;
private QuartzJobExecutor jobExecutor;
private QuartzScheduler quartzScheduler;
@@ -60,10 +57,6 @@
jobExecutor = new QuartzJobExecutor();
quartzScheduler = ActivatedQuartzSchedulerFactory.create(context, "testName");
-
- Field schedulerField = QuartzScheduler.class.getDeclaredField("scheduler");
- schedulerField.setAccessible(true);
- scheduler = ((SchedulerProxy) schedulerField.get(quartzScheduler)).getScheduler();
}
@Test
@@ -75,7 +68,7 @@
//Adding a job just to receive a JobDetail object which is needed for testing
quartzScheduler.addJob(1L, 1L, jobName, job, jobConfig, "0 * * * * ?", true);
- JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName));
+ JobDetail jobDetail = quartzScheduler.getSchedulers().get("testName").getScheduler().getJobDetail(JobKey.jobKey(jobName));
when(executionContext.getJobDetail()).thenReturn(jobDetail);
isRunnablePseudoJobCompleted = false;
@@ -99,7 +92,7 @@
//Adding a job just to receive a JobDetail object which is needed for testing
quartzScheduler.addJob(1L, 1L, jobName, job, jobConfig, "0 * * * * ?", true);
- JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName));
+ JobDetail jobDetail = quartzScheduler.getSchedulers().get("testName").getScheduler().getJobDetail(JobKey.jobKey(jobName));
when(executionContext.getJobDetail()).thenReturn(jobDetail);
isRunnablePseudoJobCompleted = false;
@@ -116,7 +109,7 @@
//Adding a job just to receive a JobDetail object which is needed for testing
quartzScheduler.addJob(1L, 1L, jobName, job, jobConfig, "0 * * * * ?", true);
- JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName));
+ JobDetail jobDetail = quartzScheduler.getSchedulers().get("testName").getScheduler().getJobDetail(JobKey.jobKey(jobName));
when(executionContext.getJobDetail()).thenReturn(jobDetail);
//Job with this config should not be executed
jobDetail.getJobDataMap().put(QuartzScheduler.DATA_MAP_RUN_ON, new String[]{VALUE_RUN_ON_LEADER});
@@ -135,7 +128,7 @@
//Adding a job just to receive a JobDetail object which is needed for testing
quartzScheduler.addJob(1L, 1L, jobName, job, jobConfig, "0 * * * * ?", true);
- JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName));
+ JobDetail jobDetail = quartzScheduler.getSchedulers().get("testName").getScheduler().getJobDetail(JobKey.jobKey(jobName));
when(executionContext.getJobDetail()).thenReturn(jobDetail);
//Job with this config should not be executed
jobDetail.getJobDataMap().put(QuartzScheduler.DATA_MAP_RUN_ON,
@@ -157,7 +150,7 @@
//Adding a job just to receive a JobDetail object which is needed for testing
quartzScheduler.addJob(1L, 1L, jobName, job, jobConfig, "0 * * * * ?", true);
- JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName));
+ JobDetail jobDetail = quartzScheduler.getSchedulers().get("testName").getScheduler().getJobDetail(JobKey.jobKey(jobName));
when(executionContext.getJobDetail()).thenReturn(jobDetail);
//Job with this config should not be executed
jobDetail.getJobDataMap().put(QuartzScheduler.DATA_MAP_RUN_ON,
@@ -181,6 +174,11 @@
assertTrue(underTest.getName().equals(testName));
}
+ @Test
+ public void testLazyScheduler() {
+ assertTrue(quartzScheduler.getSchedulers().isEmpty());
+ }
+
@After
public void deactivateScheduler() {
quartzScheduler.deactivate(context);
diff --git a/src/test/java/org/apache/sling/commons/scheduler/impl/QuartzSchedulerTest.java b/src/test/java/org/apache/sling/commons/scheduler/impl/QuartzSchedulerTest.java
index 5949d90..afc4715 100644
--- a/src/test/java/org/apache/sling/commons/scheduler/impl/QuartzSchedulerTest.java
+++ b/src/test/java/org/apache/sling/commons/scheduler/impl/QuartzSchedulerTest.java
@@ -16,6 +16,19 @@
*/
package org.apache.sling.commons.scheduler.impl;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
import org.apache.sling.commons.scheduler.Job;
import org.apache.sling.testing.mock.osgi.MockOsgi;
import org.junit.After;
@@ -30,23 +43,13 @@
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.quartz.JobKey;
-import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
-import java.io.Serializable;
-import java.lang.reflect.Field;
-import java.util.Date;
-import java.util.HashMap;
-
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.when;
-
@RunWith(MockitoJUnitRunner.class)
public class QuartzSchedulerTest {
-
- private Scheduler s;
- private SchedulerProxy proxy;
+
+ private Map<String, SchedulerProxy> proxies;
private BundleContext context;
private QuartzScheduler quartzScheduler;
@@ -56,14 +59,14 @@
@Rule
public ExpectedException thrown = ExpectedException.none();
+ @SuppressWarnings("unchecked")
@Before
public void setUp() throws Exception {
context = MockOsgi.newBundleContext();
quartzScheduler = ActivatedQuartzSchedulerFactory.create(context, "testName");
- s = quartzScheduler.getScheduler();
- Field sField = QuartzScheduler.class.getDeclaredField("scheduler");
+ Field sField = QuartzScheduler.class.getDeclaredField("schedulers");
sField.setAccessible(true);
- this.proxy = (SchedulerProxy) sField.get(quartzScheduler);
+ this.proxies = (Map<String, SchedulerProxy>) sField.get(quartzScheduler);
}
@Test
@@ -110,39 +113,28 @@
}
@Test
- public void testWithoutScheduler() throws Exception {
- setInternalSchedulerToNull();
-
- thrown.expect(IllegalStateException.class);
- thrown.expectMessage("Scheduler is not available anymore.");
- quartzScheduler.addJob(1L, 1L, "testName", new Thread(), new HashMap<String, Serializable>(), "0 * * * * ?", true);
-
- returnInternalSchedulerBack();
- }
-
- @Test
public void testAddJob() throws SchedulerException {
quartzScheduler.addJob(1L, 1L, "testName", new Thread(), new HashMap<String, Serializable>(), "0 * * * * ?", true);
- assertTrue(s.checkExists(JobKey.jobKey("testName")));
- assertFalse(s.checkExists(JobKey.jobKey("wrongName")));
+ assertTrue(proxies.get("testName").getScheduler().checkExists(JobKey.jobKey("testName")));
+ assertFalse(proxies.get("testName").getScheduler().checkExists(JobKey.jobKey("wrongName")));
}
@Test
public void testAddJobTwice() throws SchedulerException {
quartzScheduler.addJob(1L, 1L, "testName", new Thread(), new HashMap<String, Serializable>(), "0 * * * * ?", true);
- assertTrue(s.checkExists(JobKey.jobKey("testName")));
+ assertTrue(proxies.get("testName").getScheduler().checkExists(JobKey.jobKey("testName")));
//Add very same job twice to check that there is no conflicts and previous
quartzScheduler.addJob(1L, 1L, "testName", new Thread(), new HashMap<String, Serializable>(), "0 * * * * ?", true);
- assertTrue(s.checkExists(JobKey.jobKey("testName")));
+ assertTrue(proxies.get("testName").getScheduler().checkExists(JobKey.jobKey("testName")));
}
@Test
public void testRemoveJob() throws SchedulerException {
String jobName = "testName";
quartzScheduler.addJob(1L, 1L, jobName, new Thread(), new HashMap<String, Serializable>(), "0 * * * * ?", true);
- assertTrue(s.checkExists(JobKey.jobKey(jobName)));
+ assertTrue(proxies.get("testName").getScheduler().checkExists(JobKey.jobKey(jobName)));
quartzScheduler.removeJob(1L, jobName);
- assertFalse(s.checkExists(JobKey.jobKey(jobName)));
+ assertFalse(proxies.get("testName").getScheduler().checkExists(JobKey.jobKey(jobName)));
}
@Test
@@ -190,10 +182,10 @@
String otherJobName = "anyOtherName";
quartzScheduler.addPeriodicJob(4L, 4L, jobName, new Thread(), new HashMap(), 2L, true, true);
- assertTrue("Job must exists", s.checkExists(JobKey.jobKey(jobName)));
+ assertTrue("Job must exists", proxies.get("testName").getScheduler().checkExists(JobKey.jobKey(jobName)));
quartzScheduler.addPeriodicJob(5L, 5L, otherJobName, new Thread(), new HashMap(), 2L, true, false);
- assertTrue("Job must exists", s.checkExists(JobKey.jobKey(otherJobName)));
+ assertTrue("Job must exists", proxies.get("testName").getScheduler().checkExists(JobKey.jobKey(otherJobName)));
}
@Test
@@ -229,8 +221,8 @@
BundleEvent event = new BundleEvent(BundleEvent.STOPPED, bundle);
quartzScheduler.bundleChanged(event);
- assertTrue(s.checkExists(JobKey.jobKey(firstJob)));
- assertFalse(s.checkExists(JobKey.jobKey(secondJob)));
+ assertTrue(proxies.get("testName").getScheduler().checkExists(JobKey.jobKey(firstJob)));
+ assertFalse(proxies.get("testName").getScheduler().checkExists(JobKey.jobKey(secondJob)));
}
@Test
@@ -245,8 +237,8 @@
BundleEvent event = new BundleEvent(BundleEvent.STARTED, bundle);
quartzScheduler.bundleChanged(event);
- assertTrue(s.checkExists(JobKey.jobKey(firstJob)));
- assertTrue(s.checkExists(JobKey.jobKey(secondJob)));
+ assertTrue(proxies.get("testName").getScheduler().checkExists(JobKey.jobKey(firstJob)));
+ assertTrue(proxies.get("testName").getScheduler().checkExists(JobKey.jobKey(secondJob)));
}
@Test
@@ -264,31 +256,65 @@
BundleEvent event = new BundleEvent(BundleEvent.STOPPED, bundle);
quartzScheduler.bundleChanged(event);
- assertTrue(s.checkExists(JobKey.jobKey(firstJob)));
- assertTrue(s.checkExists(JobKey.jobKey(secondJob)));
+ assertTrue(proxies.get("testName").getScheduler().checkExists(JobKey.jobKey(firstJob)));
+ assertTrue(proxies.get("testName").getScheduler().checkExists(JobKey.jobKey(secondJob)));
returnInternalSchedulerBack();
}
+ @Test
+ public void testThreadPools() throws SchedulerException {
+ quartzScheduler.schedule(1L, 1L, new Thread(), quartzScheduler.NOW().name("j1").threadPoolName("tp1"));
+ quartzScheduler.schedule(1L, 2L, new Thread(), quartzScheduler.NOW().name("j2").threadPoolName("tp2"));
+ quartzScheduler.schedule(1L, 2L, new Thread(), quartzScheduler.NOW().name("j3").threadPoolName("allowed"));
+
+ assertNull(proxies.get("tp1"));
+ assertNull(proxies.get("tp2"));
+ assertNotNull(proxies.get("allowed"));
+
+ assertTrue(proxies.get("testName").getScheduler().checkExists(JobKey.jobKey("j1")));
+ assertTrue(proxies.get("testName").getScheduler().checkExists(JobKey.jobKey("j2")));
+ assertTrue(proxies.get("allowed").getScheduler().checkExists(JobKey.jobKey("j3")));
+ }
+
+ @Test
+ public void testNameAcrossPools() throws SchedulerException {
+ quartzScheduler.schedule(1L, 1L, new Thread(), quartzScheduler.NOW().name("j1").threadPoolName("tp1"));
+ assertNull(proxies.get("tp1"));
+ assertTrue(proxies.get("testName").getScheduler().checkExists(JobKey.jobKey("j1")));
+
+ quartzScheduler.schedule(1L, 1L, new Thread(), quartzScheduler.NOW().name("j1").threadPoolName("allowed"));
+ assertFalse(proxies.get("testName").getScheduler().checkExists(JobKey.jobKey("j1")));
+ assertTrue(proxies.get("allowed").getScheduler().checkExists(JobKey.jobKey("j1")));
+
+ quartzScheduler.schedule(1L, 1L, new Thread(), quartzScheduler.NOW().name("j1"));
+ assertTrue(proxies.get("testName").getScheduler().checkExists(JobKey.jobKey("j1")));
+ assertFalse(proxies.get("allowed").getScheduler().checkExists(JobKey.jobKey("j1")));
+
+ quartzScheduler.schedule(1L, 1L, new Thread(), quartzScheduler.NOW().name("j1").threadPoolName("tp1"));
+ assertNull(proxies.get("tp1"));
+ assertTrue(proxies.get("testName").getScheduler().checkExists(JobKey.jobKey("j1")));
+ }
+
@After
public void deactivateScheduler() throws NoSuchFieldException, IllegalAccessException {
- if (quartzScheduler.getScheduler() == null) {
+ if (quartzScheduler.getSchedulers().isEmpty()) {
returnInternalSchedulerBack();
}
quartzScheduler.deactivate(context);
}
private void setInternalSchedulerToNull() throws NoSuchFieldException, IllegalAccessException {
- Field sField = QuartzScheduler.class.getDeclaredField("scheduler");
+ Field sField = QuartzScheduler.class.getDeclaredField("schedulers");
sField.setAccessible(true);
- sField.set(quartzScheduler, null);
+ sField.set(quartzScheduler, new HashMap<String, SchedulerProxy>());
}
private void returnInternalSchedulerBack() throws NoSuchFieldException, IllegalAccessException {
- Field sField = QuartzScheduler.class.getDeclaredField("scheduler");
+ Field sField = QuartzScheduler.class.getDeclaredField("schedulers");
sField.setAccessible(true);
- if (quartzScheduler.getScheduler() == null && proxy != null) {
- sField.set(quartzScheduler, proxy);
+ if (quartzScheduler.getSchedulers().isEmpty() && this.proxies != null) {
+ sField.set(quartzScheduler, this.proxies);
}
}
}
diff --git a/src/test/java/org/apache/sling/commons/scheduler/impl/WebConsolePrinterTest.java b/src/test/java/org/apache/sling/commons/scheduler/impl/WebConsolePrinterTest.java
index 171ccf5..8ff225d 100644
--- a/src/test/java/org/apache/sling/commons/scheduler/impl/WebConsolePrinterTest.java
+++ b/src/test/java/org/apache/sling/commons/scheduler/impl/WebConsolePrinterTest.java
@@ -16,6 +16,18 @@
*/
package org.apache.sling.commons.scheduler.impl;
+import static org.junit.Assert.assertTrue;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Serializable;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.regex.Pattern;
+
import org.apache.sling.testing.mock.osgi.MockOsgi;
import org.junit.After;
import org.junit.Before;
@@ -23,13 +35,6 @@
import org.osgi.framework.BundleContext;
import org.quartz.SchedulerException;
-import java.io.*;
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.regex.Pattern;
-
-import static org.junit.Assert.assertTrue;
-
public class WebConsolePrinterTest {
private WebConsolePrinter consolePrinter;
private QuartzScheduler quartzScheduler;
@@ -64,6 +69,7 @@
reader.readLine();
assertRegexp(reader.readLine(), ".*Status.*active.*");
assertRegexp(reader.readLine(), ".*Name.*ApacheSling.*");
+ assertRegexp(reader.readLine(), ".*ThreadPool.*testName.*");
assertRegexp(reader.readLine(), ".*Id.*");
reader.readLine();
assertRegexp(reader.readLine(), "^Job.*testName[123].*");
@@ -78,7 +84,7 @@
reader.close();
}
}
-
+
private void assertRegexp(String input, String regexp) {
assertTrue("Expecting regexp match: '" + input + "' / '" + regexp + "'", Pattern.matches(regexp, input));
}
diff --git a/src/test/java/org/apache/sling/commons/scheduler/impl/WhiteboardHandlerTest.java b/src/test/java/org/apache/sling/commons/scheduler/impl/WhiteboardHandlerTest.java
index 02f0a41..942deaf 100644
--- a/src/test/java/org/apache/sling/commons/scheduler/impl/WhiteboardHandlerTest.java
+++ b/src/test/java/org/apache/sling/commons/scheduler/impl/WhiteboardHandlerTest.java
@@ -16,6 +16,13 @@
*/
package org.apache.sling.commons.scheduler.impl;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.lang.reflect.Field;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
import org.apache.sling.commons.scheduler.Scheduler;
import org.apache.sling.testing.mock.osgi.MockOsgi;
import org.junit.After;
@@ -29,13 +36,6 @@
import org.quartz.JobKey;
import org.quartz.SchedulerException;
-import java.lang.reflect.Field;
-import java.util.Dictionary;
-import java.util.Hashtable;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
public class WhiteboardHandlerTest {
private WhiteboardHandler handler;
private BundleContext context;
@@ -71,7 +71,6 @@
@Test
public void testAddingService() throws SchedulerException {
- org.quartz.Scheduler s = quartzScheduler.getScheduler();
Thread service = new Thread();
String serviceName = "serviceName";
String schedulerName = "testScheduler";
@@ -94,14 +93,13 @@
ServiceReference reference = context.getServiceReference(serviceName);
JobKey jobKey = JobKey.jobKey(schedulerName + "." + reference.getProperty(Constants.SERVICE_ID));
- assertNull(s.getJobDetail(jobKey));
+ assertNull(quartzScheduler.getSchedulers().get("testName"));
customizer.addingService(reference);
- assertNotNull(s.getJobDetail(jobKey));
+ assertNotNull(quartzScheduler.getSchedulers().get("testName").getScheduler().getJobDetail(jobKey));
}
@Test
public void testUnregisterService() throws SchedulerException {
- org.quartz.Scheduler s = quartzScheduler.getScheduler();
Thread service = new Thread();
String serviceName = "serviceName";
String schedulerName = "testScheduler";
@@ -125,11 +123,11 @@
ServiceReference reference = context.getServiceReference(serviceName);
JobKey jobKey = JobKey.jobKey(schedulerName + "." + reference.getProperty(Constants.SERVICE_ID));
- assertNull(s.getJobDetail(jobKey));
+ assertNull(quartzScheduler.getSchedulers().get("testName"));
customizer.addingService(reference);
- assertNotNull(s.getJobDetail(jobKey));
+ assertNotNull(quartzScheduler.getSchedulers().get("testName").getScheduler().getJobDetail(jobKey));
customizer.removedService(reference, service);
- assertNull(s.getJobDetail(jobKey));
+ assertNull(quartzScheduler.getSchedulers().get("testName").getScheduler().getJobDetail(jobKey));
}
@After