You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by gh...@apache.org on 2020/07/03 05:46:27 UTC
[felix-dev] 01/03: FELIX-6265 Remove boilerplate classes,
allow to specify 7th field year at least as '*',
use thread pool for cron scheduler itself as well
This is an automated email from the ASF dual-hosted git repository.
ghenzler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/felix-dev.git
commit da6061d2d76b5926b77b82a65b8d344b30b66346
Author: georg.henzler <ge...@netcentric.biz>
AuthorDate: Thu Jul 2 17:35:50 2020 +0200
FELIX-6265 Remove boilerplate classes, allow to specify 7th field year
at least as '*', use thread pool for cron scheduler itself as well
---
.../executor/HealthCheckExecutorThreadPool.java | 4 +-
.../hc/core/impl/executor/HealthCheckFuture.java | 36 +-
.../executor/async/AsyncHealthCheckExecutor.java | 10 +-
.../hc/core/impl/monitor/HealthCheckMonitor.java | 4 +-
...lerProviderEnabler.java => CronJobFactory.java} | 67 +-
.../scheduling/cron/HealthCheckCronScheduler.java | 30 -
.../cron/embedded/AsyncEmbeddedCronJob.java | 99 ++-
.../scheduling/cron/embedded/EmbeddedCronJob.java | 28 -
.../cron/embedded/EmbeddedCronParser.java | 13 +-
.../cron/embedded/EmbeddedCronScheduler.java | 154 +---
.../embedded/EmbeddedCronSchedulerProvider.java | 54 +-
.../cron/embedded/EmbeddedCronSchedulerTask.java | 67 --
.../scheduling/cron/quartz/AsyncQuartzCronJob.java | 22 +-
.../scheduling/cron/quartz/CronJobFactory.java | 47 --
.../cron/quartz/QuartzCronScheduler.java | 1 +
.../cron/quartz/QuartzCronSchedulerProvider.java | 33 +-
.../cron/embedded/EmbeddedCronParserTest.java | 777 ++++++++++++++++++++-
.../cron/embedded/EmbeddedCronSchedulerTest.java | 192 ++---
.../cron/embedded/EmbeddedCronTriggerTest.java | 718 -------------------
.../cron/embedded/EmbeddedSampleCronJob1.java | 60 --
.../cron/embedded/EmbeddedSampleCronJob2.java | 29 -
21 files changed, 1092 insertions(+), 1353 deletions(-)
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorThreadPool.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorThreadPool.java
index f5c65c0..aa4ddb7 100644
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorThreadPool.java
+++ b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorThreadPool.java
@@ -17,8 +17,8 @@
*/
package org.apache.felix.hc.core.impl.executor;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
@@ -104,7 +104,7 @@ public class HealthCheckExecutorThreadPool {
}
}
- public ExecutorService getExecutor() {
+ public ScheduledExecutorService getExecutor() {
return executor;
}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckFuture.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckFuture.java
index d86409a..c5083b2 100644
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckFuture.java
+++ b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckFuture.java
@@ -20,6 +20,7 @@ package org.apache.felix.hc.core.impl.executor;
import static org.apache.felix.hc.api.FormattingResultLog.msHumanReadable;
import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
@@ -126,25 +127,38 @@ public class HealthCheckFuture extends FutureTask<ExecutionResult> {
Class<?> hcClass = legacyHealthCheck.getClass();
log.debug("Running legacy HC {}, please convert to new interface org.apache.felix.hc.api.HealthCheck!",
hcClass.getName());
+
+ Object result;
try {
Method executeMethod = hcClass.getMethod("execute");
- Object result = executeMethod.invoke(legacyHealthCheck);
+ result = executeMethod.invoke(legacyHealthCheck);
+ } catch (InvocationTargetException e) {
+ log.healthCheckError("Exception during execute() of Sling HC {}: {}", hcClass.getName(), String.valueOf(e.getTargetException()), e);
+ return new Result(log);
+ } catch (ReflectiveOperationException e) {
+ log.healthCheckError("Could not call Sling HC {} from Felix Runtime: {}", hcClass.getName(), String.valueOf(e), e);
+ return new Result(log);
+ }
+
+ try {
Object resultLog = readPrivateField(result, "resultLog");
List<?> entries = (List) readPrivateField(resultLog, "entries");
- for (Object object : entries) {
- String statusLegacy = String.valueOf(readPrivateField(object, "status"));
- String message = (String) readPrivateField(object, "message");
- Exception exception = (Exception) readPrivateField(object, "exception");
- if(statusLegacy.equals("DEBUG")) {
- log.add(new ResultLog.Entry(message, true, exception));
- } else {
- statusLegacy = statusLegacy.replace("INFO", Result.Status.OK.name());
- log.add(new ResultLog.Entry(Result.Status.valueOf(statusLegacy), message, exception));
+ if(entries != null) {
+ for (Object object : entries) {
+ String statusLegacy = String.valueOf(readPrivateField(object, "status"));
+ String message = (String) readPrivateField(object, "message");
+ Exception exception = (Exception) readPrivateField(object, "exception");
+ if(statusLegacy.equals("DEBUG")) {
+ log.add(new ResultLog.Entry(message, true, exception));
+ } else {
+ statusLegacy = statusLegacy.replace("INFO", Result.Status.OK.name());
+ log.add(new ResultLog.Entry(Result.Status.valueOf(statusLegacy), message, exception));
+ }
}
}
} catch (ReflectiveOperationException e) {
- log.healthCheckError("Could call and convert Sling HC {} for Felix Runtime", hcClass.getName());
+ log.healthCheckError("Could convert Sling HC result of {} for Felix Runtime: {}", hcClass.getName(), String.valueOf(e), e);
}
return new Result(log);
}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/async/AsyncHealthCheckExecutor.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/async/AsyncHealthCheckExecutor.java
index 70905e3..97df0f6 100644
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/async/AsyncHealthCheckExecutor.java
+++ b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/async/AsyncHealthCheckExecutor.java
@@ -39,7 +39,7 @@ import org.apache.felix.hc.core.impl.executor.HealthCheckFuture.Callback;
import org.apache.felix.hc.core.impl.executor.HealthCheckResultCache;
import org.apache.felix.hc.core.impl.scheduling.AsyncIntervalJob;
import org.apache.felix.hc.core.impl.scheduling.AsyncJob;
-import org.apache.felix.hc.core.impl.scheduling.cron.quartz.CronJobFactory;
+import org.apache.felix.hc.core.impl.scheduling.CronJobFactory;
import org.apache.felix.hc.core.impl.util.HealthCheckFilter;
import org.apache.felix.hc.core.impl.util.lang.StringUtils;
import org.osgi.framework.BundleContext;
@@ -74,6 +74,7 @@ public class AsyncHealthCheckExecutor implements ServiceListener {
@Activate
protected final void activate(final BundleContext bundleContext) throws InvalidSyntaxException {
+ this.bundleContext = bundleContext;
this.bundleContext.addServiceListener(this, "(objectclass=" + HealthCheck.class.getName() + ")");
int count = 0;
HealthCheckFilter healthCheckFilter = new HealthCheckFilter(bundleContext);
@@ -131,7 +132,7 @@ public class AsyncHealthCheckExecutor implements ServiceListener {
try {
AsyncJob healthCheckAsyncJob = null;
if (isAsyncCron(descriptor)) {
- healthCheckAsyncJob = cronJobFactory.createNewJob(getAsyncJob(descriptor), "job-hc-" + descriptor.getServiceId(), "async-healthchecks", descriptor.getAsyncCronExpression());
+ healthCheckAsyncJob = cronJobFactory.createAsyncCronJob(getAsyncJob(descriptor), "job-hc-" + descriptor.getTitle().replaceAll("\\s+","-"), "async-healthchecks", descriptor.getAsyncCronExpression());
} else if (isAsyncInterval(descriptor)) {
healthCheckAsyncJob = new AsyncIntervalJob(getAsyncJob(descriptor), healthCheckExecutorThreadPool, descriptor.getAsyncIntervalInSec());
}
@@ -142,8 +143,9 @@ public class AsyncHealthCheckExecutor implements ServiceListener {
} else {
return false;
}
+
} catch (Exception e) {
- LOG.warn("Could not schedule job for " + descriptor + ". Exception: " + e, e);
+ LOG.warn("Could not schedule async health check for " + descriptor + ". Exception: " + e, e);
return false;
}
}
@@ -227,7 +229,7 @@ public class AsyncHealthCheckExecutor implements ServiceListener {
} else {
result = new ExecutionResult(healthCheckMetadata,
new Result(Result.Status.WARN, "Async Health Check with cron expression '" + healthCheckMetadata.getAsyncCronExpression() +
- "' is never executed because quartz bundle is missing."), 0L);
+ "' is never executed because of misconfiguration."), 0L);
}
} else {
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitor.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitor.java
index 0b28bc7..be2dc59 100644
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitor.java
+++ b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitor.java
@@ -38,7 +38,7 @@ import org.apache.felix.hc.core.impl.executor.CombinedExecutionResult;
import org.apache.felix.hc.core.impl.executor.HealthCheckExecutorThreadPool;
import org.apache.felix.hc.core.impl.scheduling.AsyncIntervalJob;
import org.apache.felix.hc.core.impl.scheduling.AsyncJob;
-import org.apache.felix.hc.core.impl.scheduling.cron.quartz.CronJobFactory;
+import org.apache.felix.hc.core.impl.scheduling.CronJobFactory;
import org.apache.felix.hc.core.impl.util.lang.StringUtils;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
@@ -171,7 +171,7 @@ public class HealthCheckMonitor implements Runnable {
this.intervalInSec = config.intervalInSec();
this.cronExpression = config.cronExpression();
if (StringUtils.isNotBlank(cronExpression)) {
- monitorJob = cronJobFactory.createNewJob(this,
+ monitorJob = cronJobFactory.createAsyncCronJob(this,
"job-hc-monitor-" + componentContext.getProperties().get(ComponentConstants.COMPONENT_ID),
"healthcheck-monitor", cronExpression);
} else if (intervalInSec > 0) {
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/CronSchedulerProviderEnabler.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/CronJobFactory.java
similarity index 50%
rename from healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/CronSchedulerProviderEnabler.java
rename to healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/CronJobFactory.java
index 6518726..cb42bc7 100644
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/CronSchedulerProviderEnabler.java
+++ b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/CronJobFactory.java
@@ -15,67 +15,58 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
-package org.apache.felix.hc.core.impl.scheduling.cron;
+package org.apache.felix.hc.core.impl.scheduling;
-import static org.apache.felix.hc.core.impl.scheduling.cron.CronSchedulerProviderEnabler.CronType.EMBEDDED;
-import static org.apache.felix.hc.core.impl.scheduling.cron.CronSchedulerProviderEnabler.CronType.QUARTZ;
-import static org.apache.felix.hc.core.impl.scheduling.cron.HealthCheckCronScheduler.CRON_TYPE_PROPERTY;
import static org.osgi.framework.namespace.PackageNamespace.PACKAGE_NAMESPACE;
-import java.util.Collection;
-
+import org.apache.felix.hc.core.impl.executor.HealthCheckExecutorThreadPool;
+import org.apache.felix.hc.core.impl.scheduling.cron.embedded.AsyncEmbeddedCronJob;
+import org.apache.felix.hc.core.impl.scheduling.cron.embedded.EmbeddedCronSchedulerProvider;
+import org.apache.felix.hc.core.impl.scheduling.cron.quartz.AsyncQuartzCronJob;
+import org.apache.felix.hc.core.impl.scheduling.cron.quartz.QuartzCronSchedulerProvider;
import org.osgi.framework.BundleContext;
import org.osgi.framework.wiring.BundleWire;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.component.runtime.ServiceComponentRuntime;
-import org.osgi.service.component.runtime.dto.ComponentDescriptionDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-@Component
-public final class CronSchedulerProviderEnabler {
-
- enum CronType {
- QUARTZ, EMBEDDED
- }
-
- private final Logger logger = LoggerFactory.getLogger(getClass());
+/** Component without direct quartz imports (can always start) that will provide a QuartzCronScheduler on demand. */
+@Component(service = CronJobFactory.class)
+public class CronJobFactory {
+ private static final Logger LOG = LoggerFactory.getLogger(CronJobFactory.class);
private static final String QUARTZ_PACKAGE_PREFIX = "org.quartz";
-
+
@Reference
- private ServiceComponentRuntime scr;
+ private HealthCheckExecutorThreadPool healthCheckExecutorThreadPool;
- private BundleContext bundleContext;
+ @Reference
+ private QuartzCronSchedulerProvider quartzCronSchedulerProvider;
+
+ @Reference
+ private EmbeddedCronSchedulerProvider embeddedCronSchedulerProvider;
+
+ boolean isQuartzAvailable;
@Activate
- void activate(final BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- if (isQuartzPackageImported()) {
- enableCronSchedulerProvider(QUARTZ);
- logger.info("Quartz cron scheduler enabled");
- } else {
- enableCronSchedulerProvider(EMBEDDED);
- logger.info("Embedded cron scheduler enabled");
- }
+ protected synchronized void activate(BundleContext bundleContext) {
+ isQuartzAvailable = isQuartzAvailable(bundleContext);
+ LOG.info("Quartz is " + (isQuartzAvailable?"":"NOT ")+"available for scheduling cron jobs");
}
- private void enableCronSchedulerProvider(final CronType cronType) {
- final Collection<ComponentDescriptionDTO> dtos = scr.getComponentDescriptionDTOs(bundleContext.getBundle());
- for (final ComponentDescriptionDTO dto : dtos) {
- if (dto.properties.containsKey(CRON_TYPE_PROPERTY)) {
- final String type = (String) dto.properties.get(CRON_TYPE_PROPERTY);
- if (type.equalsIgnoreCase(cronType.name())) {
- scr.enableComponent(dto);
- }
- }
+ public AsyncJob createAsyncCronJob(Runnable runnable, String id, String group, String cronExpression) {
+
+ if(isQuartzAvailable) {
+ return new AsyncQuartzCronJob(runnable, quartzCronSchedulerProvider, id, group, cronExpression);
+ } else {
+ return new AsyncEmbeddedCronJob(runnable, embeddedCronSchedulerProvider, id, cronExpression);
}
}
- private boolean isQuartzPackageImported() {
+ private boolean isQuartzAvailable(BundleContext bundleContext) {
final BundleWiring wiring = bundleContext.getBundle().adapt(BundleWiring.class);
for (final BundleWire wire : wiring.getRequiredWires(PACKAGE_NAMESPACE)) {
final String pkg = (String) wire.getCapability().getAttributes().get(PACKAGE_NAMESPACE);
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/HealthCheckCronScheduler.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/HealthCheckCronScheduler.java
deleted file mode 100644
index b4f6131..0000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/HealthCheckCronScheduler.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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 SF 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.felix.hc.core.impl.scheduling.cron;
-
-/**
- * A common interface for multiple implementations of cron providers
- */
-@FunctionalInterface
-public interface HealthCheckCronScheduler {
-
- String CRON_TYPE_PROPERTY = "cron.type";
-
- Object getScheduler();
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/AsyncEmbeddedCronJob.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/AsyncEmbeddedCronJob.java
index d3a7fb5..b0ca4e4 100644
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/AsyncEmbeddedCronJob.java
+++ b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/AsyncEmbeddedCronJob.java
@@ -17,50 +17,64 @@
*/
package org.apache.felix.hc.core.impl.scheduling.cron.embedded;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.RejectedExecutionException;
+
import org.apache.felix.hc.core.impl.scheduling.AsyncJob;
-import org.apache.felix.hc.core.impl.scheduling.cron.HealthCheckCronScheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Runs health checks that are configured with a cron expression for
+ * Runs health checks/monitors that are configured with a cron expression for
* asynchronous execution.
*/
public final class AsyncEmbeddedCronJob extends AsyncJob {
- private final Logger logger = LoggerFactory.getLogger(getClass());
+ private static final Logger LOG = LoggerFactory.getLogger(AsyncEmbeddedCronJob.class);
- private final EmbeddedCronJob cronJob;
+ private final String id;
+ private final String cronExpression;
+ private final EmbeddedCronParser cronParser;
+
private final EmbeddedCronScheduler scheduler;
- public AsyncEmbeddedCronJob(final Runnable runnable, final HealthCheckCronScheduler healtchCheckScheduler,
+ private volatile boolean executing = false;
+ private volatile long lastExecutingTime = 0;
+ private volatile long nextExecutingTime = 0;
+
+ public AsyncEmbeddedCronJob(final Runnable runnable, final EmbeddedCronSchedulerProvider embeddedCronSchedulerProvider,
final String id, final String cronExpression) {
super(runnable);
- cronJob = new HealthCheckJob(cronExpression, id);
- scheduler = (EmbeddedCronScheduler) healtchCheckScheduler.getScheduler();
+ this.id = id;
+
+ this.scheduler = embeddedCronSchedulerProvider.getScheduler();
+
+ this.cronExpression = cronExpression;
+ this.cronParser= new EmbeddedCronParser(cronExpression);
+ nextExecutingTime = cronParser.next(System.currentTimeMillis());
}
@Override
public boolean schedule() {
try {
- scheduler.schedule(cronJob);
- logger.info("Scheduled job {} with trigger {}", cronJob.name(), cronJob.cron());
+ scheduler.schedule(this);
+ LOG.info("Scheduled job {} with trigger {}", id, cronExpression);
return true;
} catch (final Exception e) {
- logger.error("Could not schedule job for {}", runnable, e);
+ LOG.error("Could not schedule job for {}", runnable, e);
return false;
}
}
@Override
public boolean unschedule() {
- final String name = cronJob.name();
- logger.debug("Unscheduling job {}", name);
+
+ LOG.debug("Unscheduling job {}", id);
try {
- scheduler.remove(name);
+ scheduler.remove(this);
return true;
} catch (final Exception e) {
- logger.error("Could not unschedule job for {}", name, e);
+ LOG.error("Could not unschedule job for {}", id, e);
return false;
}
}
@@ -70,30 +84,51 @@ public final class AsyncEmbeddedCronJob extends AsyncJob {
return "[Async embedded cron job for " + runnable + "]";
}
- private class HealthCheckJob implements EmbeddedCronJob {
+ public String getId() {
+ return id;
+ }
- private final String name;
- private final String cronExpression;
+ public long getLastExecutingTime() {
+ return lastExecutingTime;
+ }
- public HealthCheckJob(final String cronExpression, final String name) {
- this.name = name;
- this.cronExpression = cronExpression;
- }
+ public long getNextExecutingTime() {
+ return nextExecutingTime;
+ }
- @Override
- public String cron() {
- return cronExpression;
+ public boolean isExecuting() {
+ return executing;
+ }
+
+ public void checkAndExecute(ExecutorService tasksExecutor) {
+ final long currentTime = System.currentTimeMillis();
+ if (nextExecutingTime >= currentTime) {
+ return;
}
-
- @Override
- public String name() {
- return "job-hc-" + name;
+ if(executing) {
+ nextExecutingTime = cronParser.next(System.currentTimeMillis());
+ LOG.trace("Cron task {} would be fired but is still executing, skipping this execution, next execution: {}", id, nextExecutingTime);
+ return;
}
- @Override
- public void run() throws Exception {
- runnable.run();
+ try {
+ lastExecutingTime = currentTime;
+ executing = true;
+ tasksExecutor.execute(() -> {
+ try {
+ runnable.run();
+ } catch (final Exception e) {
+ LOG.error("Exception while executing cron task {}", id, e);
+ } finally {
+ nextExecutingTime = cronParser.next(System.currentTimeMillis());
+ executing = false;
+ }
+ });
+
+ } catch (final RejectedExecutionException e) {
+ executing = false;
+ LOG.error("Failed to start cron task: {}", id, e);
}
}
-
+
}
\ No newline at end of file
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronJob.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronJob.java
deleted file mode 100644
index e6d038b..0000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronJob.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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 SF 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.felix.hc.core.impl.scheduling.cron.embedded;
-
-public interface EmbeddedCronJob {
-
- void run() throws Exception;
-
- String cron();
-
- String name();
-
-}
\ No newline at end of file
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronParser.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronParser.java
index 332629e..a545cfd 100644
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronParser.java
+++ b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronParser.java
@@ -18,6 +18,7 @@
package org.apache.felix.hc.core.impl.scheduling.cron.embedded;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.BitSet;
import java.util.Calendar;
import java.util.Collections;
@@ -245,7 +246,10 @@ public final class EmbeddedCronParser {
* Parse the given pattern expression.
*/
private void parse(final String expression) {
- final String[] fields = SPACE_SPLITTER.split(expression);
+ String[] fields = SPACE_SPLITTER.split(expression);
+ if (fields.length == 7 && "*".equals(fields[6])) {
+ fields = Arrays.copyOfRange(fields, 0, 6);
+ }
if (fields.length != 6) {
throw new IllegalArgumentException(String.format(
"Cron expression must consist of 6 fields (found %d in \"%s\")", fields.length, expression));
@@ -391,8 +395,6 @@ public final class EmbeddedCronParser {
return "0 0 0 * * ?";
case "@hourly":
return "0 0 * * * ?";
- case "@reboot":
- return "0 0 0 1 1 ? 1900";
default:
throw new IllegalArgumentException("Unrecognized @ expression: '" + expression + "'");
}
@@ -422,4 +424,9 @@ public final class EmbeddedCronParser {
public String toString() {
return getClass().getSimpleName() + ": " + expression;
}
+
+ String getExpression() {
+ return expression;
+ }
+
}
\ No newline at end of file
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronScheduler.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronScheduler.java
index 16c29a7..72d2b8f 100644
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronScheduler.java
+++ b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronScheduler.java
@@ -17,151 +17,61 @@
*/
package org.apache.felix.hc.core.impl.scheduling.cron.embedded;
-import java.util.Collection;
-import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public final class EmbeddedCronScheduler implements AutoCloseable {
+public final class EmbeddedCronScheduler implements Runnable {
- private final Logger logger = LoggerFactory.getLogger(getClass());
-
- /* Object used for internal locking/notifications */
- private final Object monitor = new Object();
+ private static final Logger LOG = LoggerFactory.getLogger(EmbeddedCronScheduler.class);
/* List of managed tasks */
- private final Map<String, EmbeddedCronSchedulerTask> tasks = new HashMap<>();
-
- /* Task executor instance */
- public final ExecutorService tasksExecutor;
+ private final Map<String, AsyncEmbeddedCronJob> tasks = new ConcurrentHashMap<>();
- /* Thread that makes all scheduling job */
- public final SchedulerThread schedulerThread;
+ /* Task executor service */
+ public final ScheduledExecutorService scheduledExecutorService;
- /* Flag to check if the scheduler is active */
- private final AtomicBoolean isActive;
-
- public EmbeddedCronScheduler(final ExecutorService tasksExecutor, final long initialDelay, final long checkInterval,
+ private String schedulerThreadName;
+
+ public EmbeddedCronScheduler(final ScheduledExecutorService scheduledExecutorService, final long initialDelay, final long checkInterval,
final TimeUnit timeUnit, final String schedulerThreadName) {
- schedulerThread = new SchedulerThread(initialDelay, checkInterval, timeUnit, schedulerThreadName);
- this.tasksExecutor = tasksExecutor;
- isActive = new AtomicBoolean(true);
- schedulerThread.start();
+
+ this.scheduledExecutorService = scheduledExecutorService;
+ scheduledExecutorService.scheduleAtFixedRate(this, initialDelay, checkInterval, timeUnit);
+
+ this.schedulerThreadName = schedulerThreadName;
}
- public void schedule(final EmbeddedCronJob cronJob) {
- synchronized (monitor) {
- tasks.put(cronJob.name(), new EmbeddedCronSchedulerTask(cronJob, new EmbeddedCronParser(cronJob.cron())));
- }
+ public void schedule(AsyncEmbeddedCronJob cronJob) {
+ tasks.put(cronJob.getId(), cronJob);
}
- public void remove(final String name) {
- synchronized (monitor) {
- tasks.remove(name);
- }
+ public void remove(AsyncEmbeddedCronJob cronJob) {
+ tasks.remove(cronJob.getId());
}
@Override
- public void close() {
- shutdown();
- }
-
- public void shutdown() {
- isActive.set(false);
- synchronized (monitor) {
- monitor.notify();
- }
- schedulerThread.interrupt();
- }
-
- public boolean isShutdown() {
- return !isActive.get();
- }
-
- public Collection<EmbeddedCronSchedulerTask> getTasks() {
- return tasks.values();
- }
-
- public class SchedulerThread extends Thread {
- public final long initialDelay;
- public final long checkInterval;
- public final TimeUnit timeUnit;
-
- private SchedulerThread(final long initialDelay, final long checkInterval, final TimeUnit timeUnit,
- final String threadName) {
- if (initialDelay < 0) {
- throw new IllegalArgumentException("initialDelay < 0. Value: " + initialDelay);
- }
- if (checkInterval <= 0) {
- throw new IllegalArgumentException("checkInterval must be > 0. Value: " + checkInterval);
- }
- if (timeUnit == null) {
- throw new IllegalArgumentException("timeUnit is null");
+ public void run() {
+ String threadNameToRestore = Thread.currentThread().getName();
+ try {
+ Thread.currentThread().setName(schedulerThreadName);
+ LOG.trace("tasks: {}", tasks);
+
+ for (final Entry<String, AsyncEmbeddedCronJob> entry : tasks.entrySet()) {
+ final AsyncEmbeddedCronJob job = entry.getValue();
+ job.checkAndExecute(scheduledExecutorService);
}
- this.initialDelay = initialDelay;
- this.checkInterval = checkInterval;
- this.timeUnit = timeUnit;
- setName(threadName);
- setDaemon(true);
+ } catch (Exception e) {
+ // TODO: handle exception
+ } finally {
+ Thread.currentThread().setName(threadNameToRestore);
}
- @Override
- public void run() {
- if (initialDelay > 0) {
- pause(timeUnit.toMillis(initialDelay));
- }
- final long checkIntervalMillis = timeUnit.toMillis(checkInterval);
- while (isActive.get()) {
- try {
- checkAndExecute();
- pause(checkIntervalMillis);
- } catch (final Exception e) {
- logger.error("Got internal error that must never happen!", e);
- }
- }
- }
-
- private void pause(final long checkIntervalMillis) {
- try {
- synchronized (monitor) {
- monitor.wait(checkIntervalMillis);
- }
- } catch (final InterruptedException e) {
- logger.error("Got unexpected interrupted exception! Ignoring", e);
- Thread.currentThread().interrupt();
- }
- }
-
- private void checkAndExecute() {
- synchronized (monitor) {
- final long currentTime = System.currentTimeMillis();
- for (final Entry<String, EmbeddedCronSchedulerTask> entry : tasks.entrySet()) {
- final EmbeddedCronSchedulerTask task = entry.getValue();
- if (task.nextExecutingTime >= currentTime) {
- continue;
- }
- if (task.executing) {
- task.nextExecutingTime = task.cronParser.next(currentTime);
- continue;
- }
- try {
- task.lastExecutingTime = System.currentTimeMillis();
- tasksExecutor.execute(task);
- task.executing = true;
- } catch (final RejectedExecutionException e) {
- logger.error("Failed to start task: {}", task, e);
- Thread.currentThread().interrupt();
- }
- }
- }
- }
}
}
\ No newline at end of file
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronSchedulerProvider.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronSchedulerProvider.java
index 22148f6..884677a 100644
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronSchedulerProvider.java
+++ b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronSchedulerProvider.java
@@ -18,36 +18,31 @@
package org.apache.felix.hc.core.impl.scheduling.cron.embedded;
import static java.util.concurrent.TimeUnit.SECONDS;
-import static org.apache.felix.hc.core.impl.scheduling.cron.HealthCheckCronScheduler.CRON_TYPE_PROPERTY;
-import static org.apache.felix.hc.core.impl.scheduling.cron.embedded.EmbeddedCronSchedulerProvider.CRON_TYPE_EMBEDDED;
import java.util.concurrent.TimeUnit;
import org.apache.felix.hc.core.impl.executor.HealthCheckExecutorThreadPool;
-import org.apache.felix.hc.core.impl.scheduling.cron.HealthCheckCronScheduler;
import org.apache.felix.hc.core.impl.scheduling.cron.embedded.EmbeddedCronSchedulerProvider.Configuration;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
@Designate(ocd = Configuration.class)
-@Component(enabled = false, property = CRON_TYPE_PROPERTY + "=" + CRON_TYPE_EMBEDDED)
-public final class EmbeddedCronSchedulerProvider implements HealthCheckCronScheduler {
+@Component(service = EmbeddedCronSchedulerProvider.class)
+public class EmbeddedCronSchedulerProvider {
- public static final String CRON_TYPE_EMBEDDED = "embedded";
public static final String HC_SCHEDULER_NAME = "felix.hc.embedded.scheduler";
- @ObjectClassDefinition(name = "Embedded Cron Scheduler Configuration")
+ @ObjectClassDefinition(name = "Health Check: Embedded Cron Scheduler")
@interface Configuration {
- @AttributeDefinition(name = "Initial Delay", description = "The initial delay before the embedded cron scheduler thread becomes active")
- long initialDelay() default 10L;
+ @AttributeDefinition(name = "Initial Delay", description = "The initial delay before tasks are scheduled")
+ long initialDelay() default 3L;
@AttributeDefinition(name = "Check Interval Delay", description = "The delay to check for a scheduled task to satisfy the cron expression")
- long checkInterval() default 3L;
+ long checkInterval() default 1L;
@AttributeDefinition(name = "Time unit", description = "The time unit of the delays as specified in this configuration")
TimeUnit timeUnit() default SECONDS;
@@ -56,30 +51,37 @@ public final class EmbeddedCronSchedulerProvider implements HealthCheckCronSched
String schedulerName() default HC_SCHEDULER_NAME;
}
- private EmbeddedCronScheduler scheduler;
@Reference
private HealthCheckExecutorThreadPool threadPool;
+ private Configuration configuration;
+
+ private EmbeddedCronScheduler scheduler = null;
+
@Activate
void activate(final Configuration configuration) {
- scheduler = new EmbeddedCronScheduler( //
- threadPool.getExecutor(), //
- configuration.initialDelay(), //
- configuration.checkInterval(), //
- configuration.timeUnit(), //
- configuration.schedulerName());
+ this.configuration = configuration;
}
- @Deactivate
- void deactivate() {
- scheduler.shutdown();
- scheduler = null;
- }
-
- @Override
- public EmbeddedCronScheduler getScheduler() {
+ public synchronized EmbeddedCronScheduler getScheduler() {
+ if(scheduler == null) {
+ scheduler = new EmbeddedCronScheduler(
+ threadPool.getExecutor(),
+ configuration.initialDelay(),
+ configuration.checkInterval(),
+ configuration.timeUnit(),
+ configuration.schedulerName());
+ }
return scheduler;
+
}
+
+ // @Deactivate
+ // no shutdown of scheduler needed since HealthCheckExecutorThreadPool is handling the shutdown
+ // of the full ScheduledThreadPoolExecutor
+
+
+
}
\ No newline at end of file
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronSchedulerTask.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronSchedulerTask.java
deleted file mode 100644
index 7632254..0000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronSchedulerTask.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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 SF 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.felix.hc.core.impl.scheduling.cron.embedded;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-final class EmbeddedCronSchedulerTask implements Runnable {
-
- private final Logger logger = LoggerFactory.getLogger(getClass());
-
- final EmbeddedCronJob cronJob;
- final EmbeddedCronParser cronParser;
-
- volatile boolean executing;
- volatile long lastExecutingTime = 0;
- volatile long nextExecutingTime = 0;
-
- public EmbeddedCronSchedulerTask(final EmbeddedCronJob cronJob, final EmbeddedCronParser cronParser) {
- this.cronJob = cronJob;
- this.cronParser = cronParser;
- }
-
- @Override
- public void run() {
- try {
- cronJob.run();
- } catch (final Exception e) {
- logger.error("Exception while executing cron task", e);
- } finally {
- nextExecutingTime = cronParser.next(System.currentTimeMillis());
- executing = false;
- }
- }
-
- public long getLastExecutingTime() {
- return lastExecutingTime;
- }
-
- public long getNextExecutingTime() {
- return nextExecutingTime;
- }
-
- public boolean isExecuting() {
- return executing;
- }
-
- @Override
- public String toString() {
- return "EmbeddedCronSchedulerTask [" + cronJob.name() + "]";
- }
-}
\ No newline at end of file
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/AsyncQuartzCronJob.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/AsyncQuartzCronJob.java
index 69c3d22..67046cb 100644
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/AsyncQuartzCronJob.java
+++ b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/AsyncQuartzCronJob.java
@@ -22,7 +22,6 @@ import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
import org.apache.felix.hc.core.impl.scheduling.AsyncJob;
-import org.apache.felix.hc.core.impl.scheduling.cron.HealthCheckCronScheduler;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobDataMap;
@@ -35,7 +34,7 @@ import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-/** Runs health checks that are configured with a cron expression for asynchronous execution.
+/** Async job to be used by async health checks and async monitor.
*
* This implementation uses quartz to support the cron syntax (which is not supported by executors from standard java java.util.concurrent
* package) */
@@ -44,16 +43,17 @@ public class AsyncQuartzCronJob extends AsyncJob {
private static final String JOB_DATA_KEY_JOB = "asyncHcJob";
- protected final QuartzCronScheduler quartzCronScheduler;
private final String id;
private final String group;
private final String cronExpression;
- private JobKey jobKey;
+ private final Scheduler quartzScheduler;
- public AsyncQuartzCronJob(Runnable runnable, HealthCheckCronScheduler quartzCronSchedulerProvider, String id, String group, String cronExpression) {
+ private JobKey jobKey = null;
+
+ public AsyncQuartzCronJob(Runnable runnable, QuartzCronSchedulerProvider quartzCronSchedulerProvider, String id, String group, String cronExpression) {
super(runnable);
- this.quartzCronScheduler = (QuartzCronScheduler) quartzCronSchedulerProvider.getScheduler();
+ this.quartzScheduler = quartzCronSchedulerProvider.getQuartzCronScheduler().getScheduler();
this.id = id;
this.group = group;
this.cronExpression = cronExpression;
@@ -77,28 +77,28 @@ public class AsyncQuartzCronJob extends AsyncJob {
}
public boolean schedule() {
+
try {
- Scheduler scheduler = quartzCronScheduler.getScheduler();
JobDetail job = getQuartzJobDetail();
CronTrigger cronTrigger = newTrigger().withSchedule(cronSchedule(cronExpression)).forJob(job)
.build();
- scheduler.scheduleJob(job, cronTrigger);
+ quartzScheduler.scheduleJob(job, cronTrigger);
LOG.info("Scheduled job {} with trigger {}", job, cronTrigger);
return true;
} catch (SchedulerException e) {
LOG.error("Could not schedule job for " + runnable + ": " + e, e);
return false;
}
+
}
@Override
public boolean unschedule() {
- Scheduler scheduler = quartzCronScheduler.getScheduler();
LOG.debug("Unscheduling job {}", jobKey);
try {
- scheduler.deleteJob(jobKey);
+ quartzScheduler.deleteJob(jobKey);
return true;
} catch (SchedulerException e) {
LOG.error("Could not unschedule job for " + jobKey + ": " + e, e);
@@ -108,7 +108,7 @@ public class AsyncQuartzCronJob extends AsyncJob {
@Override
public String toString() {
- return "[Async quartz cron job for " + runnable + "]";
+ return "[Async quartz job for " + runnable + "]";
}
// quartz forces to pass in a class object (and not an instance), hence this helper class is needed
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/CronJobFactory.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/CronJobFactory.java
deleted file mode 100644
index d47e343..0000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/CronJobFactory.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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 SF 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.felix.hc.core.impl.scheduling.cron.quartz;
-
-import static org.apache.felix.hc.core.impl.scheduling.cron.HealthCheckCronScheduler.CRON_TYPE_PROPERTY;
-import static org.apache.felix.hc.core.impl.scheduling.cron.quartz.QuartzCronSchedulerProvider.CRON_TYPE_QUARTZ;
-
-import org.apache.felix.hc.core.impl.scheduling.AsyncJob;
-import org.apache.felix.hc.core.impl.scheduling.cron.HealthCheckCronScheduler;
-import org.apache.felix.hc.core.impl.scheduling.cron.embedded.AsyncEmbeddedCronJob;
-import org.osgi.service.component.ComponentServiceObjects;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-
-@Component(service = CronJobFactory.class)
-public final class CronJobFactory {
-
- @Reference
- private ComponentServiceObjects<HealthCheckCronScheduler> cronSchedulerProvider;
-
- public AsyncJob createNewJob(final Runnable runnable, final String id, final String group,
- final String cronExpression) {
- final String property = (String) cronSchedulerProvider.getServiceReference().getProperty(CRON_TYPE_PROPERTY);
- final HealthCheckCronScheduler cronScheduler = cronSchedulerProvider.getService();
- if (CRON_TYPE_QUARTZ.equals(property)) {
- return new AsyncQuartzCronJob(runnable, cronScheduler, id, group, cronExpression);
- } else {
- return new AsyncEmbeddedCronJob(runnable, cronScheduler, id, cronExpression);
- }
- }
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/QuartzCronScheduler.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/QuartzCronScheduler.java
index 7325910..8033984 100644
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/QuartzCronScheduler.java
+++ b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/QuartzCronScheduler.java
@@ -26,6 +26,7 @@ import org.quartz.spi.ThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+/** Wrapper around org.quartz.Scheduler */
public class QuartzCronScheduler {
private static final Logger LOG = LoggerFactory.getLogger(QuartzCronScheduler.class);
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/QuartzCronSchedulerProvider.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/QuartzCronSchedulerProvider.java
index e80c034..ef9c545 100644
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/QuartzCronSchedulerProvider.java
+++ b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/QuartzCronSchedulerProvider.java
@@ -17,42 +17,29 @@
*/
package org.apache.felix.hc.core.impl.scheduling.cron.quartz;
-import static org.apache.felix.hc.core.impl.scheduling.cron.HealthCheckCronScheduler.CRON_TYPE_PROPERTY;
-import static org.apache.felix.hc.core.impl.scheduling.cron.quartz.QuartzCronSchedulerProvider.CRON_TYPE_QUARTZ;
-
import org.apache.felix.hc.core.impl.executor.HealthCheckExecutorThreadPool;
-import org.apache.felix.hc.core.impl.scheduling.cron.HealthCheckCronScheduler;
-import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-/**
- * Component without direct quartz imports (can always start) that will provide
- * a QuartzCronScheduler on demand.
- */
-@Component(enabled = false, property = CRON_TYPE_PROPERTY + "=" + CRON_TYPE_QUARTZ)
-public class QuartzCronSchedulerProvider implements HealthCheckCronScheduler {
+/** Component without direct quartz imports (can always start) that will provide a QuartzCronScheduler on demand. */
+@Component(service = QuartzCronSchedulerProvider.class)
+public class QuartzCronSchedulerProvider {
- public static final String CRON_TYPE_QUARTZ = "quartz";
-
private static final Logger LOG = LoggerFactory.getLogger(QuartzCronSchedulerProvider.class);
- private QuartzCronScheduler quartzCronScheduler;
+ private QuartzCronScheduler quartzCronScheduler = null;
@Reference
HealthCheckExecutorThreadPool healthCheckExecutorThreadPool;
-
- @Activate
- void activate() {
- quartzCronScheduler = new QuartzCronScheduler(healthCheckExecutorThreadPool);
- LOG.info("Created quartz scheduler health check core bundle");
- }
- @Override
- public QuartzCronScheduler getScheduler() {
+ public synchronized QuartzCronScheduler getQuartzCronScheduler() {
+ if (quartzCronScheduler == null) {
+ quartzCronScheduler = new QuartzCronScheduler(healthCheckExecutorThreadPool);
+ LOG.info("Created QuartzCronScheduler");
+ }
return quartzCronScheduler;
}
@@ -61,7 +48,7 @@ public class QuartzCronSchedulerProvider implements HealthCheckCronScheduler {
if (quartzCronScheduler != null) {
quartzCronScheduler.shutdown();
quartzCronScheduler = null;
- LOG.info("QuartzCronScheduler shutdown");
+ LOG.info("Shutdown of QuartzCronScheduler");
}
}
diff --git a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronParserTest.java b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronParserTest.java
index 4cd28ed..48af03c 100644
--- a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronParserTest.java
+++ b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronParserTest.java
@@ -19,39 +19,792 @@ package org.apache.felix.hc.core.impl.scheduling.cron.embedded;
import static org.junit.Assert.assertEquals;
+import java.util.ArrayList;
+import java.util.Calendar;
import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.TimeZone;
+import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
-@SuppressWarnings("deprecation")
+@RunWith(Parameterized.class)
public class EmbeddedCronParserTest {
- @Test
- public void testAt50Seconds() {
- assertEquals(new Date(2012, 6, 2, 1, 0).getTime(),
- new EmbeddedCronParser("*/15 * 1-4 * * *").next(new Date(2012, 6, 1, 9, 53, 50).getTime()));
+ private final Calendar calendar = new GregorianCalendar();
+
+ private final Date date;
+
+ private final TimeZone timeZone;
+
+ public EmbeddedCronParserTest(final Date date, final TimeZone timeZone) {
+ this.date = date;
+ this.timeZone = timeZone;
+ }
+
+ @Parameters(name = "date [{0}], time zone [{1}]")
+ public static List<Object[]> getParameters() {
+ final List<Object[]> list = new ArrayList<>();
+ list.add(new Object[] { new Date(), TimeZone.getTimeZone("PST") });
+ list.add(new Object[] { new Date(), TimeZone.getTimeZone("CET") });
+ return list;
+ }
+
+ private static void roundup(final Calendar calendar) {
+ calendar.add(Calendar.SECOND, 1);
+ calendar.set(Calendar.MILLISECOND, 0);
+ }
+
+ @Before
+ public void setUp() {
+ calendar.setTimeZone(timeZone);
+ calendar.setTime(date);
+ roundup(calendar);
}
@Test
- public void testAt0Seconds() {
- assertEquals(new Date(2012, 6, 2, 1, 0).getTime(),
- new EmbeddedCronParser("*/15 * 1-4 * * *").next(new Date(2012, 6, 1, 9, 53).getTime()));
+ public void testAt0And15And45Seconds() {
+
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("*/15 * 1-4 * * *", timeZone);
+
+ calendar.set(Calendar.DAY_OF_MONTH, 2);
+ calendar.set(Calendar.HOUR_OF_DAY, 9);
+ calendar.set(Calendar.MINUTE, 53);
+ calendar.set(Calendar.SECOND, 50);
+
+ Date intialTime = calendar.getTime();
+ calendar.set(Calendar.DAY_OF_MONTH, 3);
+ calendar.set(Calendar.HOUR_OF_DAY, 1);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ assertEquals(calendar.getTime().getTime(), trigger.next(intialTime.getTime()));
+
+ intialTime = calendar.getTime();
+ calendar.set(Calendar.SECOND, 15);
+ assertEquals(calendar.getTime().getTime(), trigger.next(intialTime.getTime()));
+
+ intialTime = calendar.getTime();
+ calendar.set(Calendar.SECOND, 30);
+ assertEquals(calendar.getTime().getTime(), trigger.next(intialTime.getTime()));
+
+ intialTime = calendar.getTime();
+ calendar.set(Calendar.SECOND, 45);
+ assertEquals(calendar.getTime().getTime(), trigger.next(intialTime.getTime()));
+
}
+
@Test
public void testAt0Minutes() {
- assertEquals(new Date(2012, 6, 2, 1, 0).getTime(),
- new EmbeddedCronParser("0 */2 1-4 * * *").next(new Date(2012, 6, 1, 9, 0).getTime()));
+
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 */2 1-4 * * *", timeZone);
+
+ calendar.set(Calendar.DAY_OF_MONTH, 1);
+ calendar.set(Calendar.HOUR_OF_DAY, 9);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+
+ Date intialTime = calendar.getTime();
+ calendar.set(Calendar.DAY_OF_MONTH, 2);
+ calendar.set(Calendar.HOUR_OF_DAY, 1);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ long next = trigger.next(intialTime.getTime());
+ assertEquals(calendar.getTime().getTime(), next);
+
+
}
@Test(expected = IllegalArgumentException.class)
public void testWith0Increment() {
- new EmbeddedCronParser("*/0 * * * * *").next(new Date(2012, 6, 1, 9, 0).getTime());
+
+ new EmbeddedCronParser("*/0 * * * * *").next(calendar.getTime().getTime());
}
@Test(expected = IllegalArgumentException.class)
public void testWithNegativeIncrement() {
- new EmbeddedCronParser("*/-1 * * * * *").next(new Date(2012, 6, 1, 9, 0).getTime());
+ new EmbeddedCronParser("*/-1 * * * * *").next(calendar.getTime().getTime());
+ }
+
+ @Test
+ public void testMatchAll() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("* * * * * *", timeZone);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testMatchLastSecond() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("* * * * * *", timeZone);
+ calendar.set(Calendar.SECOND, 58);
+ assertMatchesNextSecond(trigger, calendar);
+ }
+
+ @Test
+ public void testMatchSpecificSecond() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("10 * * * * *", timeZone);
+ calendar.set(Calendar.SECOND, 9);
+ assertMatchesNextSecond(trigger, calendar);
+ }
+
+ @Test
+ public void testIncrementSecondByOne() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("11 * * * * *", timeZone);
+ calendar.set(Calendar.SECOND, 10);
+ final Date date = calendar.getTime();
+ calendar.add(Calendar.SECOND, 1);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testIncrementSecondWithPreviousExecutionTooEarly() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("11 * * * * *", timeZone);
+ calendar.set(Calendar.SECOND, 11);
+ final Date date = calendar.getTime();
+
+ calendar.add(Calendar.MINUTE, 1);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testIncrementSecondAndRollover() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("10 * * * * *", timeZone);
+ calendar.set(Calendar.SECOND, 11);
+ final Date date = calendar.getTime();
+ calendar.add(Calendar.SECOND, 59);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testSecondRange() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("10-15 * * * * *", timeZone);
+ calendar.set(Calendar.SECOND, 9);
+ assertMatchesNextSecond(trigger, calendar);
+ calendar.set(Calendar.SECOND, 14);
+ assertMatchesNextSecond(trigger, calendar);
+ }
+
+ @Test
+ public void testIncrementMinute() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 * * * * *", timeZone);
+ calendar.set(Calendar.MINUTE, 10);
+ Date date = calendar.getTime();
+ calendar.add(Calendar.MINUTE, 1);
+ calendar.set(Calendar.SECOND, 0);
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+ calendar.add(Calendar.MINUTE, 1);
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+ }
+
+ @Test
+ public void testIncrementMinuteByOne() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 11 * * * *", timeZone);
+ calendar.set(Calendar.MINUTE, 10);
+ final Date date = calendar.getTime();
+
+ calendar.add(Calendar.MINUTE, 1);
+ calendar.set(Calendar.SECOND, 0);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testIncrementMinuteAndRollover() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 10 * * * *", timeZone);
+ calendar.set(Calendar.MINUTE, 11);
+ calendar.set(Calendar.SECOND, 0);
+ final Date date = calendar.getTime();
+ calendar.add(Calendar.MINUTE, 59);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testIncrementHour() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 * * * *", timeZone);
+ calendar.set(Calendar.MONTH, 9);
+ calendar.set(Calendar.DAY_OF_MONTH, 30);
+ calendar.set(Calendar.HOUR_OF_DAY, 11);
+ calendar.set(Calendar.MINUTE, 1);
+ calendar.set(Calendar.SECOND, 0);
+ Date date = calendar.getTime();
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.HOUR_OF_DAY, 12);
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+ calendar.set(Calendar.HOUR_OF_DAY, 13);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testIncrementHourAndRollover() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 * * * *", timeZone);
+ calendar.set(Calendar.MONTH, 9);
+ calendar.set(Calendar.DAY_OF_MONTH, 10);
+ calendar.set(Calendar.HOUR_OF_DAY, 23);
+ calendar.set(Calendar.MINUTE, 1);
+ calendar.set(Calendar.SECOND, 0);
+ Date date = calendar.getTime();
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.DAY_OF_MONTH, 11);
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+ calendar.set(Calendar.HOUR_OF_DAY, 1);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testIncrementDayOfMonth() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 * * *", timeZone);
+ calendar.set(Calendar.DAY_OF_MONTH, 1);
+ Date date = calendar.getTime();
+ calendar.add(Calendar.DAY_OF_MONTH, 1);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+ assertEquals(2, calendar.get(Calendar.DAY_OF_MONTH));
+ calendar.add(Calendar.DAY_OF_MONTH, 1);
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+ assertEquals(3, calendar.get(Calendar.DAY_OF_MONTH));
+ }
+
+ @Test
+ public void testIncrementDayOfMonthByOne() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("* * * 10 * *", timeZone);
+ calendar.set(Calendar.DAY_OF_MONTH, 9);
+ final Date date = calendar.getTime();
+ calendar.add(Calendar.DAY_OF_MONTH, 1);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testIncrementDayOfMonthAndRollover() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("* * * 10 * *", timeZone);
+ calendar.set(Calendar.DAY_OF_MONTH, 11);
+ final Date date = calendar.getTime();
+ calendar.add(Calendar.MONTH, 1);
+ calendar.set(Calendar.DAY_OF_MONTH, 10);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testDailyTriggerInShortMonth() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 * * *", timeZone);
+ calendar.set(Calendar.MONTH, 8); // September: 30 days
+ calendar.set(Calendar.DAY_OF_MONTH, 30);
+ Date date = calendar.getTime();
+ calendar.set(Calendar.MONTH, 9); // October
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.DAY_OF_MONTH, 1);
+
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+
+ calendar.set(Calendar.DAY_OF_MONTH, 2);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testDailyTriggerInLongMonth() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 * * *", timeZone);
+ calendar.set(Calendar.MONTH, 7); // August: 31 days and not a daylight saving boundary
+ calendar.set(Calendar.DAY_OF_MONTH, 30);
+ Date date = calendar.getTime();
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.DAY_OF_MONTH, 31);
+
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+
+ calendar.set(Calendar.MONTH, 8); // September
+ calendar.set(Calendar.DAY_OF_MONTH, 1);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testDailyTriggerOnDaylightSavingBoundary() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 * * *", timeZone);
+ calendar.set(Calendar.MONTH, 9); // October: 31 days and a daylight saving boundary in CET
+ calendar.set(Calendar.DAY_OF_MONTH, 30);
+ Date date = calendar.getTime();
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.DAY_OF_MONTH, 31);
+
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+
+ calendar.set(Calendar.MONTH, 10); // November
+ calendar.set(Calendar.DAY_OF_MONTH, 1);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testIncrementMonth() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 1 * *", timeZone);
+ calendar.set(Calendar.MONTH, 9);
+ calendar.set(Calendar.DAY_OF_MONTH, 30);
+ Date date = calendar.getTime();
+ calendar.set(Calendar.DAY_OF_MONTH, 1);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MONTH, 10);
+
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+
+ calendar.set(Calendar.MONTH, 11);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testIncrementMonthAndRollover() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 1 * *", timeZone);
+ calendar.set(Calendar.MONTH, 11);
+ calendar.set(Calendar.DAY_OF_MONTH, 31);
+ calendar.set(Calendar.YEAR, 2010);
+ Date date = calendar.getTime();
+ calendar.set(Calendar.DAY_OF_MONTH, 1);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MONTH, 0);
+ calendar.set(Calendar.YEAR, 2011);
+
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+
+ calendar.set(Calendar.MONTH, 1);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testMonthlyTriggerInLongMonth() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 31 * *", timeZone);
+ calendar.set(Calendar.MONTH, 9);
+ calendar.set(Calendar.DAY_OF_MONTH, 30);
+ final Date date = calendar.getTime();
+ calendar.set(Calendar.DAY_OF_MONTH, 31);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testMonthlyTriggerInShortMonth() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 1 * *", timeZone);
+ calendar.set(Calendar.MONTH, 9);
+ calendar.set(Calendar.DAY_OF_MONTH, 30);
+ final Date date = calendar.getTime();
+ calendar.set(Calendar.MONTH, 10);
+ calendar.set(Calendar.DAY_OF_MONTH, 1);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testIncrementDayOfWeekByOne() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("* * * * * 2", timeZone);
+ calendar.set(Calendar.DAY_OF_WEEK, 2);
+ final Date date = calendar.getTime();
+ calendar.add(Calendar.DAY_OF_WEEK, 1);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ assertEquals(Calendar.TUESDAY, calendar.get(Calendar.DAY_OF_WEEK));
+ }
+
+ @Test
+ public void testIncrementDayOfWeekAndRollover() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("* * * * * 2", timeZone);
+ calendar.set(Calendar.DAY_OF_WEEK, 4);
+ final Date date = calendar.getTime();
+ calendar.add(Calendar.DAY_OF_MONTH, 6);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ assertEquals(Calendar.TUESDAY, calendar.get(Calendar.DAY_OF_WEEK));
+ }
+
+ @Test
+ public void testSpecificMinuteSecond() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("55 5 * * * *", timeZone);
+ calendar.set(Calendar.MINUTE, 4);
+ calendar.set(Calendar.SECOND, 54);
+ Date date = calendar.getTime();
+
+ calendar.add(Calendar.MINUTE, 1);
+ calendar.set(Calendar.SECOND, 55);
+
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+
+ calendar.add(Calendar.HOUR, 1);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testSpecificHourSecond() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("55 * 10 * * *", timeZone);
+ calendar.set(Calendar.HOUR_OF_DAY, 9);
+ calendar.set(Calendar.SECOND, 54);
+ Date date = calendar.getTime();
+
+ calendar.add(Calendar.HOUR_OF_DAY, 1);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 55);
+
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+
+ calendar.add(Calendar.MINUTE, 1);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testSpecificMinuteHour() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("* 5 10 * * *", timeZone);
+ calendar.set(Calendar.MINUTE, 4);
+ calendar.set(Calendar.HOUR_OF_DAY, 9);
+ Date date = calendar.getTime();
+ calendar.add(Calendar.MINUTE, 1);
+ calendar.add(Calendar.HOUR_OF_DAY, 1);
+ calendar.set(Calendar.SECOND, 0);
+
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+
+ // next trigger is in one second because second is wildcard
+ calendar.add(Calendar.SECOND, 1);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testSpecificDayOfMonthSecond() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("55 * * 3 * *", timeZone);
+ calendar.set(Calendar.DAY_OF_MONTH, 2);
+ calendar.set(Calendar.SECOND, 54);
+ Date date = calendar.getTime();
+ calendar.add(Calendar.DAY_OF_MONTH, 1);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 55);
+
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+
+ calendar.add(Calendar.MINUTE, 1);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void testSpecificDate() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("* * * 3 11 *", timeZone);
+ calendar.set(Calendar.DAY_OF_MONTH, 2);
+ calendar.set(Calendar.MONTH, 9);
+ Date date = calendar.getTime();
+
+ calendar.add(Calendar.DAY_OF_MONTH, 1);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MONTH, 10); // 10=November
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+
+ calendar.add(Calendar.SECOND, 1);
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNonExistentSpecificDate() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 31 6 *", timeZone);
+ calendar.set(Calendar.DAY_OF_MONTH, 10);
+ calendar.set(Calendar.MONTH, 2);
+ final Date date = calendar.getTime();
+ trigger.next(date.getTime());
+ }
+
+ @Test
+ public void testLeapYearSpecificDate() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 29 2 *", timeZone);
+ calendar.set(Calendar.YEAR, 2007);
+ calendar.set(Calendar.DAY_OF_MONTH, 10);
+ calendar.set(Calendar.MONTH, 1); // 2=February
+ Date date = calendar.getTime();
+
+ calendar.set(Calendar.YEAR, 2008);
+ calendar.set(Calendar.DAY_OF_MONTH, 29);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+ calendar.add(Calendar.YEAR, 4);
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+ }
+
+ @Test
+ public void testWeekDaySequence() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 7 ? * MON-FRI", timeZone);
+ // This is a Saturday
+ calendar.set(2009, Calendar.SEPTEMBER, 26);
+ Date date = calendar.getTime();
+ // 7 am is the trigger time
+ calendar.set(Calendar.HOUR_OF_DAY, 7);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ // Add two days because we start on Saturday
+ calendar.add(Calendar.DAY_OF_MONTH, 2);
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+ // Next day is a week day so add one
+ calendar.add(Calendar.DAY_OF_MONTH, 1);
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+ calendar.add(Calendar.DAY_OF_MONTH, 1);
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTimeInMillis(), date.getTime());
+ }
+
+ @Test
+ public void testDayOfWeekIndifferent() {
+ final EmbeddedCronParser trigger1 = new EmbeddedCronParser("* * * 2 * *", timeZone);
+ final EmbeddedCronParser trigger2 = new EmbeddedCronParser("* * * 2 * ?", timeZone);
+ assertEquals(trigger1, trigger2);
+ }
+
+ @Test
+ public void testSecondIncrementer() {
+ final EmbeddedCronParser trigger1 = new EmbeddedCronParser("57,59 * * * * *", timeZone);
+ final EmbeddedCronParser trigger2 = new EmbeddedCronParser("57/2 * * * * *", timeZone);
+ assertEquals(trigger1, trigger2);
+ }
+
+ @Test
+ public void testSecondIncrementerWithRange() {
+ final EmbeddedCronParser trigger1 = new EmbeddedCronParser("1,3,5 * * * * *", timeZone);
+ final EmbeddedCronParser trigger2 = new EmbeddedCronParser("1-6/2 * * * * *", timeZone);
+ assertEquals(trigger1, trigger2);
+ }
+
+ @Test
+ public void testHourIncrementer() {
+ final EmbeddedCronParser trigger1 = new EmbeddedCronParser("* * 4,8,12,16,20 * * *", timeZone);
+ final EmbeddedCronParser trigger2 = new EmbeddedCronParser("* * 4/4 * * *", timeZone);
+ assertEquals(trigger1, trigger2);
+ }
+
+ @Test
+ public void testDayNames() {
+ final EmbeddedCronParser trigger1 = new EmbeddedCronParser("* * * * * 0-6", timeZone);
+ final EmbeddedCronParser trigger2 = new EmbeddedCronParser("* * * * * TUE,WED,THU,FRI,SAT,SUN,MON", timeZone);
+ assertEquals(trigger1, trigger2);
+ }
+
+ @Test
+ public void testSundayIsZero() {
+ final EmbeddedCronParser trigger1 = new EmbeddedCronParser("* * * * * 0", timeZone);
+ final EmbeddedCronParser trigger2 = new EmbeddedCronParser("* * * * * SUN", timeZone);
+ assertEquals(trigger1, trigger2);
+ }
+
+ @Test
+ public void testSundaySynonym() {
+ final EmbeddedCronParser trigger1 = new EmbeddedCronParser("* * * * * 0", timeZone);
+ final EmbeddedCronParser trigger2 = new EmbeddedCronParser("* * * * * 7", timeZone);
+ assertEquals(trigger1, trigger2);
+ }
+
+ @Test
+ public void testMonthNames() {
+ final EmbeddedCronParser trigger1 = new EmbeddedCronParser("* * * * 1-12 *", timeZone);
+ final EmbeddedCronParser trigger2 = new EmbeddedCronParser(
+ "* * * * FEB,JAN,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC *", timeZone);
+ assertEquals(trigger1, trigger2);
+ }
+
+ @Test
+ public void testMonthNamesMixedCase() {
+ final EmbeddedCronParser trigger1 = new EmbeddedCronParser("* * * * 2 *", timeZone);
+ final EmbeddedCronParser trigger2 = new EmbeddedCronParser("* * * * Feb *", timeZone);
+ assertEquals(trigger1, trigger2);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSecondInvalid() {
+ new EmbeddedCronParser("77 * * * * *", timeZone);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSecondRangeInvalid() {
+ new EmbeddedCronParser("44-77 * * * * *", timeZone);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testMinuteInvalid() {
+ new EmbeddedCronParser("* 77 * * * *", timeZone);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testMinuteRangeInvalid() {
+ new EmbeddedCronParser("* 44-77 * * * *", timeZone);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testHourInvalid() {
+ new EmbeddedCronParser("* * 27 * * *", timeZone);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testHourRangeInvalid() {
+ new EmbeddedCronParser("* * 23-28 * * *", timeZone);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDayInvalid() {
+ new EmbeddedCronParser("* * * 45 * *", timeZone);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDayRangeInvalid() {
+ new EmbeddedCronParser("* * * 28-45 * *", timeZone);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testMonthInvalid() {
+ new EmbeddedCronParser("0 0 0 25 13 ?", timeZone);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testMonthInvalidTooSmall() {
+ new EmbeddedCronParser("0 0 0 25 0 ?", timeZone);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDayOfMonthInvalid() {
+ new EmbeddedCronParser("0 0 0 32 12 ?", timeZone);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testMonthRangeInvalid() {
+ new EmbeddedCronParser("* * * * 11-13 *", timeZone);
+ }
+
+ @Test
+ public void testWhitespace() {
+ final EmbeddedCronParser trigger1 = new EmbeddedCronParser("* * * * 1 *", timeZone);
+ final EmbeddedCronParser trigger2 = new EmbeddedCronParser("* * * * 1 *", timeZone);
+ assertEquals(trigger1, trigger2);
+ }
+
+ @Test
+ public void testMonthSequence() {
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 30 23 30 1/3 ?", timeZone);
+ calendar.set(2010, Calendar.DECEMBER, 30);
+ Date date = calendar.getTime();
+ // set expected next trigger time
+ calendar.set(Calendar.HOUR_OF_DAY, 23);
+ calendar.set(Calendar.MINUTE, 30);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.add(Calendar.MONTH, 1);
+
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTime(), date);
+
+ // Next trigger is 3 months latter
+ calendar.add(Calendar.MONTH, 3);
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTime(), date);
+
+ // Next trigger is 3 months latter
+ calendar.add(Calendar.MONTH, 3);
+ date = new Date(trigger.next(date.getTime()));
+ assertEquals(calendar.getTime(), date);
+ }
+
+ @Test
+ public void testDaylightSavingMissingHour() {
+ // This trigger has to be somewhere in between 2am and 3am
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("0 10 2 * * *", timeZone);
+ calendar.set(Calendar.DAY_OF_MONTH, 31);
+ calendar.set(Calendar.MONTH, Calendar.MARCH);
+ calendar.set(Calendar.YEAR, 2013);
+ calendar.set(Calendar.HOUR_OF_DAY, 1);
+ calendar.set(Calendar.SECOND, 54);
+ final Date date = calendar.getTime();
+ if (timeZone.equals(TimeZone.getTimeZone("CET"))) {
+ // Clocks go forward an hour so 2am doesn't exist in CET for this date
+ calendar.add(Calendar.DAY_OF_MONTH, 1);
+ }
+ calendar.add(Calendar.HOUR_OF_DAY, 1);
+ calendar.set(Calendar.MINUTE, 10);
+ calendar.set(Calendar.SECOND, 0);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
+ }
+
+ @Test
+ public void test7FieldsWithYearSetToStar() {
+
+ final EmbeddedCronParser trigger = new EmbeddedCronParser("30 30 7 * * * *", timeZone);
+
+ calendar.set(Calendar.DAY_OF_MONTH, 1);
+ calendar.set(Calendar.HOUR_OF_DAY, 2);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+
+ Date intialTime = calendar.getTime();
+ calendar.set(Calendar.DAY_OF_MONTH, 1);
+ calendar.set(Calendar.HOUR_OF_DAY, 7);
+ calendar.set(Calendar.MINUTE, 30);
+ calendar.set(Calendar.SECOND, 30);
+ long next = trigger.next(intialTime.getTime());
+ assertEquals(calendar.getTime().getTime(), next);
+
+ }
+
+ @Test
+ public void testPreDeclared() {
+ assertEquals("0 0 0 1 1 *", new EmbeddedCronParser("@annually", timeZone).getExpression());
+ assertEquals("0 0 0 1 1 *", new EmbeddedCronParser("@yearly", timeZone).getExpression());
+ assertEquals("0 0 0 1 * *", new EmbeddedCronParser("@monthly", timeZone).getExpression());
+ assertEquals("0 0 0 ? * MON", new EmbeddedCronParser("@weekly", timeZone).getExpression());
+ assertEquals("0 0 0 * * ?", new EmbeddedCronParser("@daily", timeZone).getExpression());
+ assertEquals("0 0 * * * ?", new EmbeddedCronParser("@hourly", timeZone).getExpression());
+ }
+
+ private void assertMatchesNextSecond(final EmbeddedCronParser trigger, final Calendar calendar) {
+ final Date date = calendar.getTime();
+ roundup(calendar);
+ assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
}
}
\ No newline at end of file
diff --git a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronSchedulerTest.java b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronSchedulerTest.java
index f8ba8f1..e222da8 100644
--- a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronSchedulerTest.java
+++ b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronSchedulerTest.java
@@ -18,114 +18,130 @@
package org.apache.felix.hc.core.impl.scheduling.cron.embedded;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.MockitoAnnotations.initMocks;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
-import org.apache.felix.hc.core.impl.scheduling.cron.embedded.EmbeddedSampleCronJob1.Callback;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
+import org.mockito.Spy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class EmbeddedCronSchedulerTest {
+ private static final Logger LOG = LoggerFactory.getLogger(EmbeddedCronScheduler.class);
+
+ @Spy
+ EmbeddedCronSchedulerProvider embeddedCronSchedulerProvider = new EmbeddedCronSchedulerProvider();
+
+ EmbeddedCronScheduler cronScheduler;
+
+ ScheduledExecutorService scheduledExecutorService;
+
+ @Before
+ public void setup() {
+ initMocks(this);
+
+ scheduledExecutorService = Executors.newScheduledThreadPool(10);
+ cronScheduler = new EmbeddedCronScheduler(scheduledExecutorService, 0, 50, TimeUnit.MILLISECONDS /* for exact fires in JUnit */, "T1");
+ doReturn(cronScheduler).when(embeddedCronSchedulerProvider).getScheduler();
+ }
- private static final Object SYNC_OBJECT = new Object();
-
- @Test
- public void checkSchedulerCheckSingleThread() throws InterruptedException {
- try (final EmbeddedCronScheduler scheduler = new EmbeddedCronScheduler(Executors.newFixedThreadPool(1), 0, 1,
- TimeUnit.SECONDS, "T1")) {
- final int nIterations = 3;
-
- final EmbeddedSampleCronJob1 service = new EmbeddedSampleCronJob2(s -> {
- if (s.counter == nIterations) {
- scheduler.shutdown();
- synchronized (SYNC_OBJECT) {
- SYNC_OBJECT.notify();
- }
- }
- });
- assertEquals(0, service.counter);
- final long t0 = System.currentTimeMillis();
- scheduler.schedule(service);
-
- synchronized (SYNC_OBJECT) {
- SYNC_OBJECT.wait(5_000L);
- }
- final int counter = service.counter;
- final long totalTime = System.currentTimeMillis() - t0;
-
- assertEquals(nIterations, counter);
- assertTrue(scheduler.isShutdown());
- assertTrue(totalTime < 4_000);
+ @After
+ public void teardown() {
+ scheduledExecutorService.shutdown();
+ }
+
+ public static class SampleCounterJob implements Runnable {
+
+ public volatile int counter = 0;
+
+ private final String name;
+
+ public SampleCounterJob(String name) {
+ this.name = name;
+ }
- Thread.sleep(2_000L);
- assertEquals(counter, service.counter);
+ @Override
+ public void run() {
+ counter++;
+ LOG.info("{} Name: {} counter: {}", new SimpleDateFormat("HH:mm:ss.SSS").format(new Date()), name, counter);
}
+
}
+
@Test
public void checkSchedulerCheckMultipleThreads() throws InterruptedException {
- try (EmbeddedCronScheduler scheduler = new EmbeddedCronScheduler(Executors.newFixedThreadPool(10), 0, 1,
- TimeUnit.SECONDS, "T2")) {
- EmbeddedSampleCronJob1.staticCounter = 0;
- final int nIterations = 3;
-
- final Callback c = s -> {
- try {
- Thread.sleep(3000);
- } catch (final InterruptedException e) {
- throw new RuntimeException(e);
- }
- if (EmbeddedSampleCronJob1.staticCounter == nIterations) {
- synchronized (SYNC_OBJECT) {
- SYNC_OBJECT.notify();
- }
- }
- };
- synchronized (SYNC_OBJECT) {
- for (int i = 0; i < 10; i++) {
- scheduler.schedule(new EmbeddedSampleCronJob1(c, "abc" + i));
- }
- SYNC_OBJECT.wait(5 * 1000L);
- }
- scheduler.shutdown();
- assertTrue("Number of iterations: " + EmbeddedSampleCronJob1.staticCounter + ">=" + nIterations,
- EmbeddedSampleCronJob1.staticCounter >= nIterations);
+
+ List<SampleCounterJob> counterRunnables = new ArrayList<>();
+ for (int i = 1; i <= 5; i++) {
+
+ String name = "abc" + i;
+ SampleCounterJob counterRunnable = new SampleCounterJob(name);
+ AsyncEmbeddedCronJob job = new AsyncEmbeddedCronJob(counterRunnable, embeddedCronSchedulerProvider, name, "* * * * * *");
+ job.schedule();
+ counterRunnables.add(counterRunnable);
+ }
+
+ int waitForSeconds = 3;
+ try {
+ Thread.sleep(waitForSeconds * 1000);
+ } catch (final InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+
+ for (SampleCounterJob counterRunnable : counterRunnables) {
+ // expect each to fire waitForSeconds times for cron "* * * * * *"
+ // no matter at what exact milliseconds we are
+ assertEquals(waitForSeconds, counterRunnable.counter);
}
+
+
+
}
+
@Test
public void checkScheduledPeriodsOverlap() throws InterruptedException {
- final int callCostInSeconds = 3;
+ final int callCostInMs = 1500;
final AtomicInteger count = new AtomicInteger();
- try (EmbeddedCronScheduler scheduler = new EmbeddedCronScheduler(Executors.newFixedThreadPool(1), 0, 200,
- TimeUnit.MILLISECONDS, "T10")) {
- final EmbeddedCronJob scheduled = new EmbeddedCronJob() {
- @Override
- public void run() throws Exception {
- count.incrementAndGet();
- Thread.sleep(callCostInSeconds * 1000);
- }
-
- @Override
- public String cron() {
- return "* * * * * *";
- }
-
- @Override
- public String name() {
- return "test";
- }
- };
- scheduler.schedule(scheduled);
-
- final int nCyclesToCheck = 5;
- Thread.sleep(nCyclesToCheck * callCostInSeconds * 1000L);
- final int minCyclesExpected = nCyclesToCheck - 1;
- assertTrue(
- "At least " + minCyclesExpected + " cycles must be started at this point, actual: " + count.get(),
- count.get() == nCyclesToCheck || count.get() == nCyclesToCheck - 1);
- }
+
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
+ LOG.info("Test Start {}", simpleDateFormat.format(new Date()));
+
+ Runnable jobRunnable = () -> {
+ count.incrementAndGet();
+ LOG.info("Execution Start {}", simpleDateFormat.format(new Date()));
+ try {
+ Thread.sleep(callCostInMs);
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e);
+ }
+ LOG.info("Execution End {}", simpleDateFormat.format(new Date()));
+ };
+
+ AsyncEmbeddedCronJob job = new AsyncEmbeddedCronJob(jobRunnable, embeddedCronSchedulerProvider, "test", "* * * * * *");
+ job.schedule();
+ // it can take up to 1000ms until first fire (depending on start time of JUnit test)
+ // as job runs 1500ms the second fire has to be cancelled
+ // the third fire has to run again
+ // a potential 4th fire (with 3000ms) has too be cancelled (as third is still running)
+ // => exact two executions
+ Thread.sleep(3000);
+ LOG.info("Test Assert {}", simpleDateFormat.format(new Date()));
+ assertEquals("A job running "+callCostInMs+"ms in one execution fired every second can only be executed twice", 2, count.get());
+
}
+
+
}
\ No newline at end of file
diff --git a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronTriggerTest.java b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronTriggerTest.java
deleted file mode 100644
index ce18fac..0000000
--- a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronTriggerTest.java
+++ /dev/null
@@ -1,718 +0,0 @@
-/*
- * 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 SF 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.felix.hc.core.impl.scheduling.cron.embedded;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.List;
-import java.util.TimeZone;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class EmbeddedCronTriggerTest {
-
- private final Calendar calendar = new GregorianCalendar();
-
- private final Date date;
-
- private final TimeZone timeZone;
-
- public EmbeddedCronTriggerTest(final Date date, final TimeZone timeZone) {
- this.date = date;
- this.timeZone = timeZone;
- }
-
- @Parameters(name = "date [{0}], time zone [{1}]")
- public static List<Object[]> getParameters() {
- final List<Object[]> list = new ArrayList<>();
- list.add(new Object[] { new Date(), TimeZone.getTimeZone("PST") });
- list.add(new Object[] { new Date(), TimeZone.getTimeZone("CET") });
- return list;
- }
-
- private static void roundup(final Calendar calendar) {
- calendar.add(Calendar.SECOND, 1);
- calendar.set(Calendar.MILLISECOND, 0);
- }
-
- @Before
- public void setUp() {
- calendar.setTimeZone(timeZone);
- calendar.setTime(date);
- roundup(calendar);
- }
-
- @Test
- public void testMatchAll() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("* * * * * *", timeZone);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testMatchLastSecond() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("* * * * * *", timeZone);
- final GregorianCalendar calendar = new GregorianCalendar();
- calendar.set(Calendar.SECOND, 58);
- assertMatchesNextSecond(trigger, calendar);
- }
-
- @Test
- public void testMatchSpecificSecond() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("10 * * * * *", timeZone);
- final GregorianCalendar calendar = new GregorianCalendar();
- calendar.set(Calendar.SECOND, 9);
- assertMatchesNextSecond(trigger, calendar);
- }
-
- @Test
- public void testIncrementSecondByOne() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("11 * * * * *", timeZone);
- calendar.set(Calendar.SECOND, 10);
- final Date date = calendar.getTime();
- calendar.add(Calendar.SECOND, 1);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testIncrementSecondWithPreviousExecutionTooEarly() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("11 * * * * *", timeZone);
- calendar.set(Calendar.SECOND, 11);
- final Date date = calendar.getTime();
-
- calendar.add(Calendar.MINUTE, 1);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testIncrementSecondAndRollover() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("10 * * * * *", timeZone);
- calendar.set(Calendar.SECOND, 11);
- final Date date = calendar.getTime();
- calendar.add(Calendar.SECOND, 59);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testSecondRange() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("10-15 * * * * *", timeZone);
- calendar.set(Calendar.SECOND, 9);
- assertMatchesNextSecond(trigger, calendar);
- calendar.set(Calendar.SECOND, 14);
- assertMatchesNextSecond(trigger, calendar);
- }
-
- @Test
- public void testIncrementMinute() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 * * * * *", timeZone);
- calendar.set(Calendar.MINUTE, 10);
- Date date = calendar.getTime();
- calendar.add(Calendar.MINUTE, 1);
- calendar.set(Calendar.SECOND, 0);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
- calendar.add(Calendar.MINUTE, 1);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
- }
-
- @Test
- public void testIncrementMinuteByOne() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 11 * * * *", timeZone);
- calendar.set(Calendar.MINUTE, 10);
- final Date date = calendar.getTime();
-
- calendar.add(Calendar.MINUTE, 1);
- calendar.set(Calendar.SECOND, 0);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testIncrementMinuteAndRollover() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 10 * * * *", timeZone);
- calendar.set(Calendar.MINUTE, 11);
- calendar.set(Calendar.SECOND, 0);
- final Date date = calendar.getTime();
- calendar.add(Calendar.MINUTE, 59);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testIncrementHour() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 * * * *", timeZone);
- calendar.set(Calendar.MONTH, 9);
- calendar.set(Calendar.DAY_OF_MONTH, 30);
- calendar.set(Calendar.HOUR_OF_DAY, 11);
- calendar.set(Calendar.MINUTE, 1);
- calendar.set(Calendar.SECOND, 0);
- Date date = calendar.getTime();
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.HOUR_OF_DAY, 12);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
- calendar.set(Calendar.HOUR_OF_DAY, 13);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testIncrementHourAndRollover() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 * * * *", timeZone);
- calendar.set(Calendar.MONTH, 9);
- calendar.set(Calendar.DAY_OF_MONTH, 10);
- calendar.set(Calendar.HOUR_OF_DAY, 23);
- calendar.set(Calendar.MINUTE, 1);
- calendar.set(Calendar.SECOND, 0);
- Date date = calendar.getTime();
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.DAY_OF_MONTH, 11);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
- calendar.set(Calendar.HOUR_OF_DAY, 1);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testIncrementDayOfMonth() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 * * *", timeZone);
- calendar.set(Calendar.DAY_OF_MONTH, 1);
- Date date = calendar.getTime();
- calendar.add(Calendar.DAY_OF_MONTH, 1);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
- assertEquals(2, calendar.get(Calendar.DAY_OF_MONTH));
- calendar.add(Calendar.DAY_OF_MONTH, 1);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
- assertEquals(3, calendar.get(Calendar.DAY_OF_MONTH));
- }
-
- @Test
- public void testIncrementDayOfMonthByOne() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("* * * 10 * *", timeZone);
- calendar.set(Calendar.DAY_OF_MONTH, 9);
- final Date date = calendar.getTime();
- calendar.add(Calendar.DAY_OF_MONTH, 1);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testIncrementDayOfMonthAndRollover() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("* * * 10 * *", timeZone);
- calendar.set(Calendar.DAY_OF_MONTH, 11);
- final Date date = calendar.getTime();
- calendar.add(Calendar.MONTH, 1);
- calendar.set(Calendar.DAY_OF_MONTH, 10);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testDailyTriggerInShortMonth() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 * * *", timeZone);
- calendar.set(Calendar.MONTH, 8); // September: 30 days
- calendar.set(Calendar.DAY_OF_MONTH, 30);
- Date date = calendar.getTime();
- calendar.set(Calendar.MONTH, 9); // October
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- calendar.set(Calendar.DAY_OF_MONTH, 1);
-
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
-
- calendar.set(Calendar.DAY_OF_MONTH, 2);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testDailyTriggerInLongMonth() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 * * *", timeZone);
- calendar.set(Calendar.MONTH, 7); // August: 31 days and not a daylight saving boundary
- calendar.set(Calendar.DAY_OF_MONTH, 30);
- Date date = calendar.getTime();
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- calendar.set(Calendar.DAY_OF_MONTH, 31);
-
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
-
- calendar.set(Calendar.MONTH, 8); // September
- calendar.set(Calendar.DAY_OF_MONTH, 1);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testDailyTriggerOnDaylightSavingBoundary() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 * * *", timeZone);
- calendar.set(Calendar.MONTH, 9); // October: 31 days and a daylight saving boundary in CET
- calendar.set(Calendar.DAY_OF_MONTH, 30);
- Date date = calendar.getTime();
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- calendar.set(Calendar.DAY_OF_MONTH, 31);
-
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
-
- calendar.set(Calendar.MONTH, 10); // November
- calendar.set(Calendar.DAY_OF_MONTH, 1);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testIncrementMonth() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 1 * *", timeZone);
- calendar.set(Calendar.MONTH, 9);
- calendar.set(Calendar.DAY_OF_MONTH, 30);
- Date date = calendar.getTime();
- calendar.set(Calendar.DAY_OF_MONTH, 1);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- calendar.set(Calendar.MONTH, 10);
-
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
-
- calendar.set(Calendar.MONTH, 11);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testIncrementMonthAndRollover() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 1 * *", timeZone);
- calendar.set(Calendar.MONTH, 11);
- calendar.set(Calendar.DAY_OF_MONTH, 31);
- calendar.set(Calendar.YEAR, 2010);
- Date date = calendar.getTime();
- calendar.set(Calendar.DAY_OF_MONTH, 1);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- calendar.set(Calendar.MONTH, 0);
- calendar.set(Calendar.YEAR, 2011);
-
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
-
- calendar.set(Calendar.MONTH, 1);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testMonthlyTriggerInLongMonth() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 31 * *", timeZone);
- calendar.set(Calendar.MONTH, 9);
- calendar.set(Calendar.DAY_OF_MONTH, 30);
- final Date date = calendar.getTime();
- calendar.set(Calendar.DAY_OF_MONTH, 31);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testMonthlyTriggerInShortMonth() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 1 * *", timeZone);
- calendar.set(Calendar.MONTH, 9);
- calendar.set(Calendar.DAY_OF_MONTH, 30);
- final Date date = calendar.getTime();
- calendar.set(Calendar.MONTH, 10);
- calendar.set(Calendar.DAY_OF_MONTH, 1);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testIncrementDayOfWeekByOne() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("* * * * * 2", timeZone);
- calendar.set(Calendar.DAY_OF_WEEK, 2);
- final Date date = calendar.getTime();
- calendar.add(Calendar.DAY_OF_WEEK, 1);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- assertEquals(Calendar.TUESDAY, calendar.get(Calendar.DAY_OF_WEEK));
- }
-
- @Test
- public void testIncrementDayOfWeekAndRollover() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("* * * * * 2", timeZone);
- calendar.set(Calendar.DAY_OF_WEEK, 4);
- final Date date = calendar.getTime();
- calendar.add(Calendar.DAY_OF_MONTH, 6);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- assertEquals(Calendar.TUESDAY, calendar.get(Calendar.DAY_OF_WEEK));
- }
-
- @Test
- public void testSpecificMinuteSecond() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("55 5 * * * *", timeZone);
- calendar.set(Calendar.MINUTE, 4);
- calendar.set(Calendar.SECOND, 54);
- Date date = calendar.getTime();
-
- calendar.add(Calendar.MINUTE, 1);
- calendar.set(Calendar.SECOND, 55);
-
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
-
- calendar.add(Calendar.HOUR, 1);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testSpecificHourSecond() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("55 * 10 * * *", timeZone);
- calendar.set(Calendar.HOUR_OF_DAY, 9);
- calendar.set(Calendar.SECOND, 54);
- Date date = calendar.getTime();
-
- calendar.add(Calendar.HOUR_OF_DAY, 1);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 55);
-
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
-
- calendar.add(Calendar.MINUTE, 1);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testSpecificMinuteHour() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("* 5 10 * * *", timeZone);
- calendar.set(Calendar.MINUTE, 4);
- calendar.set(Calendar.HOUR_OF_DAY, 9);
- Date date = calendar.getTime();
- calendar.add(Calendar.MINUTE, 1);
- calendar.add(Calendar.HOUR_OF_DAY, 1);
- calendar.set(Calendar.SECOND, 0);
-
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
-
- // next trigger is in one second because second is wildcard
- calendar.add(Calendar.SECOND, 1);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testSpecificDayOfMonthSecond() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("55 * * 3 * *", timeZone);
- calendar.set(Calendar.DAY_OF_MONTH, 2);
- calendar.set(Calendar.SECOND, 54);
- Date date = calendar.getTime();
- calendar.add(Calendar.DAY_OF_MONTH, 1);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 55);
-
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
-
- calendar.add(Calendar.MINUTE, 1);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testSpecificDate() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("* * * 3 11 *", timeZone);
- calendar.set(Calendar.DAY_OF_MONTH, 2);
- calendar.set(Calendar.MONTH, 9);
- Date date = calendar.getTime();
-
- calendar.add(Calendar.DAY_OF_MONTH, 1);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MONTH, 10); // 10=November
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
-
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
-
- calendar.add(Calendar.SECOND, 1);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testNonExistentSpecificDate() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 31 6 *", timeZone);
- calendar.set(Calendar.DAY_OF_MONTH, 10);
- calendar.set(Calendar.MONTH, 2);
- final Date date = calendar.getTime();
- trigger.next(date.getTime());
- }
-
- @Test
- public void testLeapYearSpecificDate() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 29 2 *", timeZone);
- calendar.set(Calendar.YEAR, 2007);
- calendar.set(Calendar.DAY_OF_MONTH, 10);
- calendar.set(Calendar.MONTH, 1); // 2=February
- Date date = calendar.getTime();
-
- calendar.set(Calendar.YEAR, 2008);
- calendar.set(Calendar.DAY_OF_MONTH, 29);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
-
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
- calendar.add(Calendar.YEAR, 4);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
- }
-
- @Test
- public void testWeekDaySequence() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 7 ? * MON-FRI", timeZone);
- // This is a Saturday
- calendar.set(2009, Calendar.SEPTEMBER, 26);
- Date date = calendar.getTime();
- // 7 am is the trigger time
- calendar.set(Calendar.HOUR_OF_DAY, 7);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- // Add two days because we start on Saturday
- calendar.add(Calendar.DAY_OF_MONTH, 2);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
- // Next day is a week day so add one
- calendar.add(Calendar.DAY_OF_MONTH, 1);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
- calendar.add(Calendar.DAY_OF_MONTH, 1);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
- }
-
- @Test
- public void testDayOfWeekIndifferent() {
- final EmbeddedCronParser trigger1 = new EmbeddedCronParser("* * * 2 * *", timeZone);
- final EmbeddedCronParser trigger2 = new EmbeddedCronParser("* * * 2 * ?", timeZone);
- assertEquals(trigger1, trigger2);
- }
-
- @Test
- public void testSecondIncrementer() {
- final EmbeddedCronParser trigger1 = new EmbeddedCronParser("57,59 * * * * *", timeZone);
- final EmbeddedCronParser trigger2 = new EmbeddedCronParser("57/2 * * * * *", timeZone);
- assertEquals(trigger1, trigger2);
- }
-
- @Test
- public void testSecondIncrementerWithRange() {
- final EmbeddedCronParser trigger1 = new EmbeddedCronParser("1,3,5 * * * * *", timeZone);
- final EmbeddedCronParser trigger2 = new EmbeddedCronParser("1-6/2 * * * * *", timeZone);
- assertEquals(trigger1, trigger2);
- }
-
- @Test
- public void testHourIncrementer() {
- final EmbeddedCronParser trigger1 = new EmbeddedCronParser("* * 4,8,12,16,20 * * *", timeZone);
- final EmbeddedCronParser trigger2 = new EmbeddedCronParser("* * 4/4 * * *", timeZone);
- assertEquals(trigger1, trigger2);
- }
-
- @Test
- public void testDayNames() {
- final EmbeddedCronParser trigger1 = new EmbeddedCronParser("* * * * * 0-6", timeZone);
- final EmbeddedCronParser trigger2 = new EmbeddedCronParser("* * * * * TUE,WED,THU,FRI,SAT,SUN,MON", timeZone);
- assertEquals(trigger1, trigger2);
- }
-
- @Test
- public void testSundayIsZero() {
- final EmbeddedCronParser trigger1 = new EmbeddedCronParser("* * * * * 0", timeZone);
- final EmbeddedCronParser trigger2 = new EmbeddedCronParser("* * * * * SUN", timeZone);
- assertEquals(trigger1, trigger2);
- }
-
- @Test
- public void testSundaySynonym() {
- final EmbeddedCronParser trigger1 = new EmbeddedCronParser("* * * * * 0", timeZone);
- final EmbeddedCronParser trigger2 = new EmbeddedCronParser("* * * * * 7", timeZone);
- assertEquals(trigger1, trigger2);
- }
-
- @Test
- public void testMonthNames() {
- final EmbeddedCronParser trigger1 = new EmbeddedCronParser("* * * * 1-12 *", timeZone);
- final EmbeddedCronParser trigger2 = new EmbeddedCronParser(
- "* * * * FEB,JAN,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC *", timeZone);
- assertEquals(trigger1, trigger2);
- }
-
- @Test
- public void testMonthNamesMixedCase() {
- final EmbeddedCronParser trigger1 = new EmbeddedCronParser("* * * * 2 *", timeZone);
- final EmbeddedCronParser trigger2 = new EmbeddedCronParser("* * * * Feb *", timeZone);
- assertEquals(trigger1, trigger2);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testSecondInvalid() {
- new EmbeddedCronParser("77 * * * * *", timeZone);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testSecondRangeInvalid() {
- new EmbeddedCronParser("44-77 * * * * *", timeZone);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testMinuteInvalid() {
- new EmbeddedCronParser("* 77 * * * *", timeZone);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testMinuteRangeInvalid() {
- new EmbeddedCronParser("* 44-77 * * * *", timeZone);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testHourInvalid() {
- new EmbeddedCronParser("* * 27 * * *", timeZone);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testHourRangeInvalid() {
- new EmbeddedCronParser("* * 23-28 * * *", timeZone);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testDayInvalid() {
- new EmbeddedCronParser("* * * 45 * *", timeZone);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testDayRangeInvalid() {
- new EmbeddedCronParser("* * * 28-45 * *", timeZone);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testMonthInvalid() {
- new EmbeddedCronParser("0 0 0 25 13 ?", timeZone);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testMonthInvalidTooSmall() {
- new EmbeddedCronParser("0 0 0 25 0 ?", timeZone);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testDayOfMonthInvalid() {
- new EmbeddedCronParser("0 0 0 32 12 ?", timeZone);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testMonthRangeInvalid() {
- new EmbeddedCronParser("* * * * 11-13 *", timeZone);
- }
-
- @Test
- public void testWhitespace() {
- final EmbeddedCronParser trigger1 = new EmbeddedCronParser("* * * * 1 *", timeZone);
- final EmbeddedCronParser trigger2 = new EmbeddedCronParser("* * * * 1 *", timeZone);
- assertEquals(trigger1, trigger2);
- }
-
- @Test
- public void testMonthSequence() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 30 23 30 1/3 ?", timeZone);
- calendar.set(2010, Calendar.DECEMBER, 30);
- Date date = calendar.getTime();
- // set expected next trigger time
- calendar.set(Calendar.HOUR_OF_DAY, 23);
- calendar.set(Calendar.MINUTE, 30);
- calendar.set(Calendar.SECOND, 0);
- calendar.add(Calendar.MONTH, 1);
-
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTime(), date);
-
- // Next trigger is 3 months latter
- calendar.add(Calendar.MONTH, 3);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTime(), date);
-
- // Next trigger is 3 months latter
- calendar.add(Calendar.MONTH, 3);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTime(), date);
- }
-
- @Test
- public void testDaylightSavingMissingHour() {
- // This trigger has to be somewhere in between 2am and 3am
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 10 2 * * *", timeZone);
- calendar.set(Calendar.DAY_OF_MONTH, 31);
- calendar.set(Calendar.MONTH, Calendar.MARCH);
- calendar.set(Calendar.YEAR, 2013);
- calendar.set(Calendar.HOUR_OF_DAY, 1);
- calendar.set(Calendar.SECOND, 54);
- final Date date = calendar.getTime();
- if (timeZone.equals(TimeZone.getTimeZone("CET"))) {
- // Clocks go forward an hour so 2am doesn't exist in CET for this date
- calendar.add(Calendar.DAY_OF_MONTH, 1);
- }
- calendar.add(Calendar.HOUR_OF_DAY, 1);
- calendar.set(Calendar.MINUTE, 10);
- calendar.set(Calendar.SECOND, 0);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- private void assertMatchesNextSecond(final EmbeddedCronParser trigger, final Calendar calendar) {
- final Date date = calendar.getTime();
- roundup(calendar);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
-}
\ No newline at end of file
diff --git a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedSampleCronJob1.java b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedSampleCronJob1.java
deleted file mode 100644
index 271a0b5..0000000
--- a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedSampleCronJob1.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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 SF 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.felix.hc.core.impl.scheduling.cron.embedded;
-
-public class EmbeddedSampleCronJob1 implements EmbeddedCronJob {
-
- public static volatile int staticCounter = 0;
- public volatile int counter = 0;
-
- private final String name;
- private final Callback callback;
-
- public EmbeddedSampleCronJob1(final Callback callback) {
- this.callback = callback;
- name = "SampleEmbeddedCronJob1";
- }
-
- public EmbeddedSampleCronJob1(final Callback callback, final String name) {
- this.callback = callback;
- this.name = name;
- }
-
- @Override
- public void run() throws Exception {
- synchronized (EmbeddedSampleCronJob1.class) {
- counter++;
- staticCounter++;
- }
- callback.callback(this);
- }
-
- public interface Callback {
- void callback(EmbeddedSampleCronJob1 service);
- }
-
- @Override
- public String cron() {
- return "* * * * * *";
- }
-
- @Override
- public String name() {
- return name;
- }
-}
\ No newline at end of file
diff --git a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedSampleCronJob2.java b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedSampleCronJob2.java
deleted file mode 100644
index 42deea3..0000000
--- a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedSampleCronJob2.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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 SF 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.felix.hc.core.impl.scheduling.cron.embedded;
-
-public class EmbeddedSampleCronJob2 extends EmbeddedSampleCronJob1 {
- public EmbeddedSampleCronJob2(final Callback callback) {
- super(callback);
- }
-
- @Override
- public String name() {
- return "SampleEmbeddedCronJob2";
- }
-}
\ No newline at end of file