You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2014/05/13 13:03:21 UTC
[2/2] git commit: [KARAF-751] Provide a quartz based scheduler
[KARAF-751] Provide a quartz based scheduler
Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/eb61abc8
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/eb61abc8
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/eb61abc8
Branch: refs/heads/master
Commit: eb61abc898f3ceac3a98e59fe26ee94eb7ad1f65
Parents: 176e3a7
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Tue May 13 12:26:36 2014 +0200
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Tue May 13 12:26:36 2014 +0200
----------------------------------------------------------------------
scheduler/NOTICE | 71 +++++
scheduler/core/NOTICE | 71 -----
scheduler/core/pom.xml | 82 ------
.../apache/karaf/scheduler/core/Activator.java | 49 ----
.../karaf/scheduler/core/KarafTimerTask.java | 82 ------
.../scheduler/core/RunnableServiceListener.java | 85 ------
.../karaf/scheduler/core/TaskScheduler.java | 54 ----
.../src/main/resources/OSGI-INF/bundle.info | 20 --
scheduler/pom.xml | 95 +++++-
.../java/org/apache/karaf/scheduler/Job.java | 34 +++
.../org/apache/karaf/scheduler/JobContext.java | 38 +++
.../apache/karaf/scheduler/ScheduleOptions.java | 59 ++++
.../org/apache/karaf/scheduler/Scheduler.java | 130 ++++++++
.../apache/karaf/scheduler/command/List.java | 48 +++
.../apache/karaf/scheduler/core/Activator.java | 51 ++++
.../scheduler/core/InternalScheduleOptions.java | 160 ++++++++++
.../core/NonParallelQuartzJobExecutor.java | 31 ++
.../karaf/scheduler/core/QuartzJobExecutor.java | 89 ++++++
.../karaf/scheduler/core/QuartzScheduler.java | 294 +++++++++++++++++++
.../karaf/scheduler/core/WhiteboardHandler.java | 143 +++++++++
.../src/main/resources/OSGI-INF/bundle.info | 23 ++
.../karaf/util/tracker/BaseActivator.java | 1 +
22 files changed, 1259 insertions(+), 451 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/NOTICE
----------------------------------------------------------------------
diff --git a/scheduler/NOTICE b/scheduler/NOTICE
new file mode 100644
index 0000000..b70f1f9
--- /dev/null
+++ b/scheduler/NOTICE
@@ -0,0 +1,71 @@
+Apache Karaf
+Copyright 2010-2014 The Apache Software Foundation
+
+
+I. Included Software
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2010).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+OW2 (http://www.ow2.org/).
+Licensed under the BSD License.
+
+This product includes software developed at
+OPS4J (http://www.ops4j.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Eclipse Foundation (http://www.eclipse.org/).
+Licensed under the EPL.
+
+This product includes software written by
+Antony Lesuisse.
+Licensed under Public Domain.
+
+
+II. Used Software
+
+This product uses software developed at
+FUSE Source (http://www.fusesource.org/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+AOP Alliance (http://aopalliance.sourceforge.net/).
+Licensed under the Public Domain.
+
+This product uses software developed at
+Tanuki Software (http://www.tanukisoftware.com/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+Jasypt (http://jasypt.sourceforge.net/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+JLine (http://jline.sourceforge.net).
+Licensed under the BSD License.
+
+This product uses software developed at
+SLF4J (http://www.slf4j.org/).
+Licensed under the MIT License.
+
+This product uses software developed at
+SpringSource (http://www.springsource.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software from http://www.json.org.
+Copyright (c) 2002 JSON.org
+
+
+III. License Summary
+- Apache License 2.0
+- BSD License
+- EPL License
+- MIT License
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/core/NOTICE
----------------------------------------------------------------------
diff --git a/scheduler/core/NOTICE b/scheduler/core/NOTICE
deleted file mode 100644
index b70f1f9..0000000
--- a/scheduler/core/NOTICE
+++ /dev/null
@@ -1,71 +0,0 @@
-Apache Karaf
-Copyright 2010-2014 The Apache Software Foundation
-
-
-I. Included Software
-
-This product includes software developed at
-The Apache Software Foundation (http://www.apache.org/).
-Licensed under the Apache License 2.0.
-
-This product uses software developed at
-The OSGi Alliance (http://www.osgi.org/).
-Copyright (c) OSGi Alliance (2000, 2010).
-Licensed under the Apache License 2.0.
-
-This product includes software developed at
-OW2 (http://www.ow2.org/).
-Licensed under the BSD License.
-
-This product includes software developed at
-OPS4J (http://www.ops4j.org/).
-Licensed under the Apache License 2.0.
-
-This product includes software developed at
-Eclipse Foundation (http://www.eclipse.org/).
-Licensed under the EPL.
-
-This product includes software written by
-Antony Lesuisse.
-Licensed under Public Domain.
-
-
-II. Used Software
-
-This product uses software developed at
-FUSE Source (http://www.fusesource.org/).
-Licensed under the Apache License 2.0.
-
-This product uses software developed at
-AOP Alliance (http://aopalliance.sourceforge.net/).
-Licensed under the Public Domain.
-
-This product uses software developed at
-Tanuki Software (http://www.tanukisoftware.com/).
-Licensed under the Apache License 2.0.
-
-This product uses software developed at
-Jasypt (http://jasypt.sourceforge.net/).
-Licensed under the Apache License 2.0.
-
-This product uses software developed at
-JLine (http://jline.sourceforge.net).
-Licensed under the BSD License.
-
-This product uses software developed at
-SLF4J (http://www.slf4j.org/).
-Licensed under the MIT License.
-
-This product uses software developed at
-SpringSource (http://www.springsource.org/).
-Licensed under the Apache License 2.0.
-
-This product includes software from http://www.json.org.
-Copyright (c) 2002 JSON.org
-
-
-III. License Summary
-- Apache License 2.0
-- BSD License
-- EPL License
-- MIT License
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/core/pom.xml
----------------------------------------------------------------------
diff --git a/scheduler/core/pom.xml b/scheduler/core/pom.xml
deleted file mode 100644
index 8d06015..0000000
--- a/scheduler/core/pom.xml
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
- <!--
-
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <artifactId>scheduler</artifactId>
- <groupId>org.apache.karaf</groupId>
- <version>4.0.0-SNAPSHOT</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
-
- <groupId>org.apache.karaf.scheduler</groupId>
- <artifactId>org.apache.karaf.scheduler.core</artifactId>
- <packaging>bundle</packaging>
- <name>Apache Karaf :: Scheduler :: Core</name>
- <description>This bundle provides a Service Listener which listnes for Runnable Services and schedules their execution, based on the service properties</description>
-
- <build>
- <resources>
- <resource>
- <directory>${project.basedir}/src/main/resources</directory>
- <includes>
- <include>**/*</include>
- </includes>
- </resource>
- <resource>
- <directory>${project.basedir}/src/main/resources</directory>
- <filtering>true</filtering>
- <includes>
- <include>**/*.info</include>
- </includes>
- </resource>
- </resources>
- <plugins>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <configuration>
- <instructions>
- <Bundle-Activator>org.apache.karaf.scheduler.core.Activator</Bundle-Activator>
- <Import-Package>
- *
- </Import-Package>
- </instructions>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
- <dependencies>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.core</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <scope>provided</scope>
- </dependency>
- </dependencies>
-
-</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/core/src/main/java/org/apache/karaf/scheduler/core/Activator.java
----------------------------------------------------------------------
diff --git a/scheduler/core/src/main/java/org/apache/karaf/scheduler/core/Activator.java b/scheduler/core/src/main/java/org/apache/karaf/scheduler/core/Activator.java
deleted file mode 100644
index 30d310e..0000000
--- a/scheduler/core/src/main/java/org/apache/karaf/scheduler/core/Activator.java
+++ /dev/null
@@ -1,49 +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 ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.karaf.scheduler.core;
-
-import org.osgi.framework.*;
-
-import java.util.Dictionary;
-import java.util.Properties;
-
-public class Activator implements BundleActivator {
-
- private TaskScheduler scheduler = new TaskScheduler();
- private RunnableServiceListener listener;
-
- private static final String filter = "(&(objectclass=%s)(&(%s >= 0)(%s >= 0)))";
-
- @Override
- public void start(BundleContext context) throws Exception {
- listener = new RunnableServiceListener(context, scheduler);
-
- //register scheduler service
- context.registerService(scheduler.getClass().getName(), scheduler, (Dictionary) new Properties());
-
- //register service listener
- context.addServiceListener(listener, String.format(filter, Runnable.class.getName(), KarafTimerTask.ID_PROPERTY, KarafTimerTask.PERIOD_PROPERTY));
-
- }
-
- @Override
- public void stop(BundleContext context) throws Exception {
-
- }
-
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/core/src/main/java/org/apache/karaf/scheduler/core/KarafTimerTask.java
----------------------------------------------------------------------
diff --git a/scheduler/core/src/main/java/org/apache/karaf/scheduler/core/KarafTimerTask.java b/scheduler/core/src/main/java/org/apache/karaf/scheduler/core/KarafTimerTask.java
deleted file mode 100644
index 5e1af7d..0000000
--- a/scheduler/core/src/main/java/org/apache/karaf/scheduler/core/KarafTimerTask.java
+++ /dev/null
@@ -1,82 +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 ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.karaf.scheduler.core;
-
-import java.util.TimerTask;
-
-public class KarafTimerTask<R extends Runnable> extends TimerTask {
-
- public static final String ID_PROPERTY = "org.apache.karaf.scheduler.task.id";
- public static final String PERIOD_PROPERTY = "org.apache.karaf.scheduler.task.period";
-
- protected String id;
- protected R task;
- protected Long schedulePeriod = 0L;
-
- /**
- * Constructor
- * @param id the id of the task. Used for reference.
- * @param task the task to be scheduled
- */
- public KarafTimerTask(String id, R task) {
- this.id = id;
- this.task = task;
- }
-
- /**
- * Constructor
- * @param id the id of the task. Used for reference.
- * @param task the task to be scheduled
- * @param schedulePeriod the schedule period.
- */
- public KarafTimerTask(String id, R task, Long schedulePeriod) {
- this.id = id;
- this.task = task;
- this.schedulePeriod = schedulePeriod;
- }
-
- @Override
- public void run() {
- task.run();
- }
-
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- public R getTask() {
- return task;
- }
-
- public void setTask(R task) {
- this.task = task;
- }
-
- public Long getSchedulePeriod() {
- return schedulePeriod;
- }
-
- public void setSchedulePeriod(Long schedulePeriod) {
- this.schedulePeriod = schedulePeriod;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/core/src/main/java/org/apache/karaf/scheduler/core/RunnableServiceListener.java
----------------------------------------------------------------------
diff --git a/scheduler/core/src/main/java/org/apache/karaf/scheduler/core/RunnableServiceListener.java b/scheduler/core/src/main/java/org/apache/karaf/scheduler/core/RunnableServiceListener.java
deleted file mode 100644
index a1e8822..0000000
--- a/scheduler/core/src/main/java/org/apache/karaf/scheduler/core/RunnableServiceListener.java
+++ /dev/null
@@ -1,85 +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 ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.karaf.scheduler.core;
-
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceEvent;
-import org.osgi.framework.ServiceListener;
-import org.osgi.framework.ServiceReference;
-
-public class RunnableServiceListener implements ServiceListener {
-
- private final BundleContext bundleContext;
- private final TaskScheduler scheduler;
-
- /**
- * Constructor
- *
- * @param bundleContext
- * @param scheduler
- */
- public RunnableServiceListener(BundleContext bundleContext, TaskScheduler scheduler) {
- this.bundleContext = bundleContext;
- this.scheduler = scheduler;
- }
-
- @Override
- public void serviceChanged(ServiceEvent event) {
- switch (event.getType()) {
- case ServiceEvent.REGISTERED:
- scheduleRunnableService(event);
- break;
- case ServiceEvent.UNREGISTERING: {
- unscheduleRunnableService(event);
- }
- break;
- default:
- break;
- }
- }
-
- /**
- * Schedules the execution of the Runnable Service of the {@link ServiceEvent}.
- *
- * @param event
- */
- protected void scheduleRunnableService(ServiceEvent event) {
- ServiceReference reference = event.getServiceReference();
- Runnable service = (Runnable) bundleContext.getService(reference);
- String id = (String) reference.getProperty(KarafTimerTask.ID_PROPERTY);
- String periodValue = (String) reference.getProperty(KarafTimerTask.PERIOD_PROPERTY);
-
- if (periodValue != null) {
- Long period = Long.parseLong(periodValue);
- KarafTimerTask task = new KarafTimerTask(id, service, period);
- scheduler.schedule(task);
- }
- }
-
- /**
- * Unschedules the execution of the Runnable Service of the {@link ServiceEvent}.
- *
- * @param event
- */
- protected void unscheduleRunnableService(ServiceEvent event) {
- ServiceReference reference = event.getServiceReference();
- String id = (String) reference.getProperty(KarafTimerTask.ID_PROPERTY);
- scheduler.unschedule(id);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/core/src/main/java/org/apache/karaf/scheduler/core/TaskScheduler.java
----------------------------------------------------------------------
diff --git a/scheduler/core/src/main/java/org/apache/karaf/scheduler/core/TaskScheduler.java b/scheduler/core/src/main/java/org/apache/karaf/scheduler/core/TaskScheduler.java
deleted file mode 100644
index adddc56..0000000
--- a/scheduler/core/src/main/java/org/apache/karaf/scheduler/core/TaskScheduler.java
+++ /dev/null
@@ -1,54 +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 ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.karaf.scheduler.core;
-
-import java.util.Map;
-import java.util.Timer;
-import java.util.concurrent.ConcurrentHashMap;
-
-public class TaskScheduler {
-
- private final Timer timer = new Timer();
- private final Map<String,KarafTimerTask> tasks = new ConcurrentHashMap<String, KarafTimerTask>();
-
- /**
- * Schedule a {@link KarafTimerTask}
- * @param task
- */
- public void schedule(KarafTimerTask task) {
- if(task != null) {
- tasks.put(task.getId(),task);
- timer.schedule(task,0,task.getSchedulePeriod());
- }
-
- }
-
- /**
- * Unschedule a {@link KarafTimerTask}
- * @param id The id of the task to unschedule.
- */
- public void unschedule(String id) {
- if(id != null) {
- KarafTimerTask task = tasks.remove(id);
- if(task != null) {
- task.cancel();
- }
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/core/src/main/resources/OSGI-INF/bundle.info
----------------------------------------------------------------------
diff --git a/scheduler/core/src/main/resources/OSGI-INF/bundle.info b/scheduler/core/src/main/resources/OSGI-INF/bundle.info
deleted file mode 100644
index a45c2c5..0000000
--- a/scheduler/core/src/main/resources/OSGI-INF/bundle.info
+++ /dev/null
@@ -1,20 +0,0 @@
-h1. Synopsis
-
-${project.name}
-
-${project.description}
-
-Maven URL:
-[mvn:${project.groupId}/${project.artifactId}/${project.version}]
-
-h1. Description
-
-This bundle registers a service listener, which listens from service events related to java.lang.Runnable interface.
-Each service with such interface may be added the following properties:
-
-org.apache.karaf.scheduler.task.id
-org.apache.karaf.scheduler.task.period
-
-If both properties are found, then the Runnable is decorated by a TimerTask and scheduled for repeated execution using the period property.
-
-
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/pom.xml
----------------------------------------------------------------------
diff --git a/scheduler/pom.xml b/scheduler/pom.xml
index 53551b9..6b061dd 100644
--- a/scheduler/pom.xml
+++ b/scheduler/pom.xml
@@ -22,19 +22,98 @@
<modelVersion>4.0.0</modelVersion>
<parent>
- <artifactId>karaf</artifactId>
<groupId>org.apache.karaf</groupId>
+ <artifactId>karaf</artifactId>
<version>4.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
- <groupId>org.apache.karaf</groupId>
- <artifactId>scheduler</artifactId>
- <packaging>pom</packaging>
- <name>Apache Karaf :: Scheduler</name>
+ <groupId>org.apache.karaf.scheduler</groupId>
+ <artifactId>org.apache.karaf.scheduler.core</artifactId>
+ <packaging>bundle</packaging>
+ <name>Apache Karaf :: Scheduler :: Core</name>
+ <description>This bundle provides a Service Listener which listnes for Runnable Services and schedules their execution, based on the service properties</description>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>${project.basedir}/src/main/resources</directory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ <resource>
+ <directory>${project.basedir}/src/main/resources</directory>
+ <filtering>true</filtering>
+ <includes>
+ <include>**/*.info</include>
+ </includes>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Activator>org.apache.karaf.scheduler.core.Activator</Bundle-Activator>
+ <Karaf-Commands>org.apache.karaf.scheduler.command</Karaf-Commands>
+ <Export-Package>
+ org.apache.karaf.scheduler;version=${project.version};-noimport:=true
+ </Export-Package>
+ <Import-Package>
+ !com.mchange.*,
+ !oracle.*,
+ !org.quartz.*,
+ !weblogic.*,
+ !javax.transaction,
+ *
+ </Import-Package>
+ <Private-Package>
+ org.apache.karaf.scheduler.core,
+ org.apache.karaf.scheduler.command,
+ org.apache.karaf.util.tracker,
+ org.quartz,
+ org.quartz.core.*,
+ org.quartz.listeners.*,
+ org.quartz.impl.*,
+ org.quartz.spi.*,
+ org.quartz.simpl.*,
+ org.quartz.utils.*,
+ </Private-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
- <modules>
- <module>core</module>
- </modules>
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.quartz-scheduler</groupId>
+ <artifactId>quartz</artifactId>
+ <version>2.2.1</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf</groupId>
+ <artifactId>org.apache.karaf.util</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.core</artifactId>
+ <optional>true</optional>
+ </dependency>
+ </dependencies>
</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/src/main/java/org/apache/karaf/scheduler/Job.java
----------------------------------------------------------------------
diff --git a/scheduler/src/main/java/org/apache/karaf/scheduler/Job.java b/scheduler/src/main/java/org/apache/karaf/scheduler/Job.java
new file mode 100644
index 0000000..1d10682
--- /dev/null
+++ b/scheduler/src/main/java/org/apache/karaf/scheduler/Job.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.scheduler;
+
+/**
+ * A job is executed by the {@link Scheduler} service.
+ * If the implementation of the job requires certain environment information
+ * it can implement this interface to get additional information
+ * through the provided {@link JobContext}.
+ * If no additional information is required, implementing {@link Runnable} is
+ * sufficient.
+ */
+public interface Job {
+
+ /**
+ * Execute this job.
+ * @param context The context of the job.
+ */
+ void execute(JobContext context);
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/src/main/java/org/apache/karaf/scheduler/JobContext.java
----------------------------------------------------------------------
diff --git a/scheduler/src/main/java/org/apache/karaf/scheduler/JobContext.java b/scheduler/src/main/java/org/apache/karaf/scheduler/JobContext.java
new file mode 100644
index 0000000..0660e29
--- /dev/null
+++ b/scheduler/src/main/java/org/apache/karaf/scheduler/JobContext.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.scheduler;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * The context for a {@link Job}.
+ */
+public interface JobContext {
+
+ /**
+ * Get the name of the scheduled job.
+ * @return The name of the job.
+ */
+ String getName();
+
+ /**
+ * Get the configuration provided when the job was scheduled.
+ * @return A non-null map of values.
+ */
+ Map<String, Serializable> getConfiguration();
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/src/main/java/org/apache/karaf/scheduler/ScheduleOptions.java
----------------------------------------------------------------------
diff --git a/scheduler/src/main/java/org/apache/karaf/scheduler/ScheduleOptions.java b/scheduler/src/main/java/org/apache/karaf/scheduler/ScheduleOptions.java
new file mode 100644
index 0000000..695f471
--- /dev/null
+++ b/scheduler/src/main/java/org/apache/karaf/scheduler/ScheduleOptions.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.scheduler;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * Scheduler options provide an extensible way of defining how to schedule a job.
+ * An option can be created via the scheduler.
+ *
+ * @since 2.3
+ */
+public interface ScheduleOptions {
+
+ /**
+ * Add optional configuration for the job.
+ *
+ * @param config An optional configuration object - this configuration is only passed to the job the job implements {@link Job}.
+ */
+ ScheduleOptions config(final Map<String, Serializable> config);
+
+ /**
+ * Sets the name of the job.
+ * A job only needs a name if it is scheduled and should be cancelled later on. The name can then be used to cancel the job.
+ * If a second job with the same name is started, the second one replaces the first one.
+ *
+ * @param name The job name
+ */
+ ScheduleOptions name(final String name);
+
+ /**
+ * Flag indicating whether the job can be run concurrently.
+ * This defaults to false.
+ *
+ * @param flag Whether this job can run even if previous scheduled runs are still running.
+ */
+ ScheduleOptions canRunConcurrently(final boolean flag);
+
+ String name();
+
+ boolean canRunConcurrently();
+
+ String schedule();
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/src/main/java/org/apache/karaf/scheduler/Scheduler.java
----------------------------------------------------------------------
diff --git a/scheduler/src/main/java/org/apache/karaf/scheduler/Scheduler.java b/scheduler/src/main/java/org/apache/karaf/scheduler/Scheduler.java
new file mode 100644
index 0000000..e267c28
--- /dev/null
+++ b/scheduler/src/main/java/org/apache/karaf/scheduler/Scheduler.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.scheduler;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.quartz.SchedulerException;
+
+/**
+ * A scheduler to schedule time/cron based jobs.
+ * A job is an object that is executed/fired by the scheduler. The object
+ * should either implement the {@link Job} interface or the {@link Runnable}
+ * interface.
+ *
+ * A job can be scheduled either by creating a {@link ScheduleOptions} instance
+ * through one of the scheduler methods and then calling {@link #schedule(Object, ScheduleOptions)}
+ * or
+ * by using the whiteboard pattern and registering a Runnable service with either
+ * the {@link #PROPERTY_SCHEDULER_EXPRESSION} or {@link #PROPERTY_SCHEDULER_PERIOD}
+ * property. Services registered by the whiteboard pattern can by default run concurrently,
+ * which usually is not wanted. Therefore it is advisable to also set the
+ * {@link #PROPERTY_SCHEDULER_CONCURRENT} property with Boolean.FALSE.
+ */
+public interface Scheduler {
+
+ /**
+ * Name of the configuration property to define the period for a job.
+ * The period is expressed in seconds.
+ * This property needs to be of type Long.
+ */
+ String PROPERTY_SCHEDULER_PERIOD = "scheduler.period";
+
+ /**
+ * Name of the configuration property to define if a periodically job should be scheduled immediate.
+ * Default is to not startup immediate, the job is started the first time after the period has expired.
+ * This property needs to be of type Boolean.
+ */
+ String PROPERTY_SCHEDULER_IMMEDIATE = "scheduler.immediate";
+
+ /** Name of the configuration property to define the cron expression for a job. */
+ String PROPERTY_SCHEDULER_EXPRESSION = "scheduler.expression";
+
+ /** Name of the configuration property to define if the job can be run concurrently. */
+ String PROPERTY_SCHEDULER_CONCURRENT = "scheduler.concurrent";
+
+ /** Name of the configuration property to define the job name. */
+ String PROPERTY_SCHEDULER_NAME = "scheduler.name";
+
+
+ /**
+ * Schedule a job based on the options.
+ *
+ * Note that if a job with the same name has already been added, the old job is cancelled and this new job replaces
+ * the old job.
+ *
+ * The job object needs either to be a {@link Job} or a {@link Runnable}. The options have to be created
+ * by one of the provided methods from this scheduler.
+ *
+ * @param job The job to execute (either {@link Job} or {@link Runnable}).
+ * @param options Required options defining how to schedule the job
+ * @throws SchedulerException if the job can't be scheduled
+ * @throws IllegalArgumentException If the preconditions are not met
+ * @see #NOW()
+ * @see #NOW(int, long)
+ * @see #AT(Date)
+ * @see #AT(Date, int, long)
+ * @see #EXPR(String)
+ */
+ void schedule(Object job, ScheduleOptions options) throws IllegalArgumentException, SchedulerException;
+
+ /**
+ * Remove a scheduled job by name.
+ *
+ * @param jobName The name of the job.
+ * @return <code>true</code> if the job existed and could be stopped, <code>false</code> otherwise.
+ */
+ boolean unschedule(String jobName);
+
+ Map<Object, ScheduleOptions> getJobs() throws SchedulerException;
+
+ /**
+ * Create a schedule options to fire a job immediately and only once.
+ */
+ ScheduleOptions NOW();
+
+ /**
+ * Create a schedule options to fire a job immediately more than once.
+ * @param times The number of times this job should be started (must be higher than 1 or
+ * -1 for endless)
+ * @param period Every period seconds this job is started (must be at higher than 0).
+ */
+ ScheduleOptions NOW(int times, long period);
+
+ /**
+ * Create a schedule options to fire a job once at a specific date
+ * @param date The date this job should be run.
+ */
+ ScheduleOptions AT(final Date date);
+
+ /**
+ * Create a schedule options to fire a job period starting at a specific date
+ * @param date The date this job should be run.
+ * @param times The number of times this job should be started (must be higher than 1 or
+ * -1 for endless)
+ * @param period Every period seconds this job is started (must be at higher than 0).
+ */
+ ScheduleOptions AT(final Date date, int times, long period);
+
+ /**
+ * Create a schedule options to schedule the job based on the expression
+ * @param expression The cron exception
+ */
+ ScheduleOptions EXPR(final String expression);
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/src/main/java/org/apache/karaf/scheduler/command/List.java
----------------------------------------------------------------------
diff --git a/scheduler/src/main/java/org/apache/karaf/scheduler/command/List.java b/scheduler/src/main/java/org/apache/karaf/scheduler/command/List.java
new file mode 100644
index 0000000..1078bb4
--- /dev/null
+++ b/scheduler/src/main/java/org/apache/karaf/scheduler/command/List.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.scheduler.command;
+
+import java.util.Map;
+
+import org.apache.karaf.scheduler.ScheduleOptions;
+import org.apache.karaf.scheduler.Scheduler;
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.support.table.ShellTable;
+
+@Command(scope = "scheduler", name = "list", description = "List scheduled jobs")
+@Service
+public class List implements Action {
+
+ @Reference
+ Scheduler scheduler;
+
+ @Override
+ public Object execute() throws Exception {
+ ShellTable table = new ShellTable();
+ table.column("Name");
+ table.column("Schedule");
+ Map<Object, ScheduleOptions> jobs = scheduler.getJobs();
+ for (Map.Entry<Object, ScheduleOptions> entry : jobs.entrySet()) {
+ table.addRow().addContent(entry.getValue().name(), entry.getValue().schedule());
+ }
+ table.print(System.out);
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/src/main/java/org/apache/karaf/scheduler/core/Activator.java
----------------------------------------------------------------------
diff --git a/scheduler/src/main/java/org/apache/karaf/scheduler/core/Activator.java b/scheduler/src/main/java/org/apache/karaf/scheduler/core/Activator.java
new file mode 100644
index 0000000..925d35b
--- /dev/null
+++ b/scheduler/src/main/java/org/apache/karaf/scheduler/core/Activator.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.scheduler.core;
+
+import org.apache.karaf.scheduler.Scheduler;
+import org.apache.karaf.util.tracker.BaseActivator;
+import org.quartz.simpl.SimpleThreadPool;
+import org.quartz.spi.ThreadPool;
+
+public class Activator extends BaseActivator {
+
+ private ThreadPool threadPool;
+ private QuartzScheduler scheduler;
+ private WhiteboardHandler whiteboardHandler;
+
+ @Override
+ protected void doStart() throws Exception {
+ threadPool = new SimpleThreadPool(4, Thread.NORM_PRIORITY);
+ scheduler = new QuartzScheduler(threadPool);
+ whiteboardHandler = new WhiteboardHandler(bundleContext, scheduler);
+ register(Scheduler.class, scheduler);
+ }
+
+ @Override
+ protected void doStop() {
+ if (whiteboardHandler != null) {
+ whiteboardHandler.deactivate();
+ whiteboardHandler = null;
+ }
+ if (scheduler != null) {
+ scheduler.deactivate();
+ scheduler = null;
+ }
+ super.doStop();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/src/main/java/org/apache/karaf/scheduler/core/InternalScheduleOptions.java
----------------------------------------------------------------------
diff --git a/scheduler/src/main/java/org/apache/karaf/scheduler/core/InternalScheduleOptions.java b/scheduler/src/main/java/org/apache/karaf/scheduler/core/InternalScheduleOptions.java
new file mode 100644
index 0000000..6651ea8
--- /dev/null
+++ b/scheduler/src/main/java/org/apache/karaf/scheduler/core/InternalScheduleOptions.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.scheduler.core;
+
+import java.io.Serializable;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.apache.karaf.scheduler.ScheduleOptions;
+import org.quartz.CronExpression;
+import org.quartz.CronScheduleBuilder;
+import org.quartz.SimpleScheduleBuilder;
+import org.quartz.Trigger;
+import org.quartz.TriggerBuilder;
+
+/**
+ * Scheduler options provide an extensible way of defining how to schedule a job.
+ * @since 2.3
+ */
+public class InternalScheduleOptions implements ScheduleOptions {
+
+ public String name;
+
+ public boolean canRunConcurrently = false;
+
+ public Map<String, Serializable> configuration;
+
+ public final String schedule;
+
+ public final TriggerBuilder<? extends Trigger> trigger;
+
+ public final IllegalArgumentException argumentException;
+
+ public InternalScheduleOptions(Date date) {
+ if ( date == null ) {
+ this.trigger = null;
+ this.argumentException = new IllegalArgumentException("Date can't be null");
+ } else {
+ this.trigger = TriggerBuilder.newTrigger().startAt(date);
+ this.argumentException = null;
+ }
+ this.schedule = "at(" + formatDate(date) + ")";
+ }
+
+ public InternalScheduleOptions(Date date, int times, long period) {
+ TriggerBuilder<? extends Trigger> trigger = null;
+ IllegalArgumentException argumentException = null;
+ try {
+ if (date == null) {
+ throw new IllegalArgumentException("Date can't be null");
+ }
+ if (times < 2 && times != -1) {
+ throw new IllegalArgumentException("Times argument must be higher than 1 or -1");
+ }
+ if (period < 1) {
+ throw new IllegalArgumentException("Period argument must be higher than 0");
+ }
+ final SimpleScheduleBuilder sb;
+ if (times == -1) {
+ sb = SimpleScheduleBuilder.simpleSchedule().repeatForever();
+ } else {
+ sb = SimpleScheduleBuilder.simpleSchedule().withRepeatCount(times - 1);
+ }
+ trigger = TriggerBuilder.newTrigger()
+ .startAt(date)
+ .withSchedule(sb.withIntervalInMilliseconds(period * 1000));
+ } catch (IllegalArgumentException e) {
+ argumentException = e;
+ }
+ this.trigger = trigger;
+ this.argumentException = argumentException;
+ this.schedule = "at(" + formatDate(date) + ", " + times + ", " + period + ")";
+ }
+
+ public InternalScheduleOptions(String expression) {
+ TriggerBuilder<? extends Trigger> trigger = null;
+ IllegalArgumentException argumentException = null;
+ try {
+ if ( expression == null ) {
+ throw new IllegalArgumentException("Expression can't be null");
+ }
+ if ( !CronExpression.isValidExpression(expression) ) {
+ throw new IllegalArgumentException("Expression is invalid : " + expression);
+ }
+ trigger = TriggerBuilder.newTrigger()
+ .withSchedule(CronScheduleBuilder.cronSchedule(expression));
+ } catch (IllegalArgumentException e) {
+ argumentException = e;
+ }
+ this.trigger = trigger;
+ this.argumentException = argumentException;
+ this.schedule = "cron(" + expression + ")";
+ }
+
+ /**
+ * @see org.apache.karaf.scheduler.ScheduleOptions#config(java.util.Map)
+ */
+ public ScheduleOptions config(final Map<String, Serializable> config) {
+ this.configuration = config;
+ return this;
+ }
+
+ /**
+ * @see org.apache.karaf.scheduler.ScheduleOptions#name(java.lang.String)
+ */
+ public ScheduleOptions name(final String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * @see org.apache.karaf.scheduler.ScheduleOptions#canRunConcurrently(boolean)
+ */
+ public ScheduleOptions canRunConcurrently(final boolean flag) {
+ this.canRunConcurrently = flag;
+ return this;
+ }
+
+ @Override
+ public String name() {
+ return this.name;
+ }
+
+ @Override
+ public boolean canRunConcurrently() {
+ return this.canRunConcurrently;
+ }
+
+ @Override
+ public String schedule() {
+ return schedule;
+ }
+
+ private String formatDate(Date date) {
+ if (date == null) {
+ return "null";
+ }
+ TimeZone tz = TimeZone.getTimeZone("UTC");
+ DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'");
+ df.setTimeZone(tz);
+ return df.format(date);
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/src/main/java/org/apache/karaf/scheduler/core/NonParallelQuartzJobExecutor.java
----------------------------------------------------------------------
diff --git a/scheduler/src/main/java/org/apache/karaf/scheduler/core/NonParallelQuartzJobExecutor.java b/scheduler/src/main/java/org/apache/karaf/scheduler/core/NonParallelQuartzJobExecutor.java
new file mode 100644
index 0000000..ec22506
--- /dev/null
+++ b/scheduler/src/main/java/org/apache/karaf/scheduler/core/NonParallelQuartzJobExecutor.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.scheduler.core;
+
+import org.quartz.DisallowConcurrentExecution;
+
+
+/**
+ * This component is responsible to launch a {@link org.apache.karaf.scheduler.Job}
+ * or {@link Runnable} in a Quartz Scheduler but non concurrently.
+ *
+ */
+@DisallowConcurrentExecution
+public class NonParallelQuartzJobExecutor extends QuartzJobExecutor {
+
+ // nothing to code here
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/src/main/java/org/apache/karaf/scheduler/core/QuartzJobExecutor.java
----------------------------------------------------------------------
diff --git a/scheduler/src/main/java/org/apache/karaf/scheduler/core/QuartzJobExecutor.java b/scheduler/src/main/java/org/apache/karaf/scheduler/core/QuartzJobExecutor.java
new file mode 100644
index 0000000..9a07c1f
--- /dev/null
+++ b/scheduler/src/main/java/org/apache/karaf/scheduler/core/QuartzJobExecutor.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.scheduler.core;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.apache.karaf.scheduler.JobContext;
+import org.quartz.Job;
+import org.quartz.JobDataMap;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+
+/**
+ * This component is responsible to launch a {@link org.apache.karaf.scheduler.Job}
+ * or {@link Runnable} in a Quartz Scheduler.
+ *
+ */
+public class QuartzJobExecutor implements Job {
+
+ /**
+ * @see org.quartz.Job#execute(org.quartz.JobExecutionContext)
+ */
+ public void execute(final JobExecutionContext context) throws JobExecutionException {
+
+ final JobDataMap data = context.getJobDetail().getJobDataMap();
+ final Object job = data.get(QuartzScheduler.DATA_MAP_OBJECT);
+ final Logger logger = (Logger)data.get(QuartzScheduler.DATA_MAP_LOGGER);
+
+ try {
+ logger.debug("Executing job {} with name {}", job, data.get(QuartzScheduler.DATA_MAP_NAME));
+ if (job instanceof org.apache.karaf.scheduler.Job) {
+ @SuppressWarnings("unchecked")
+ final InternalScheduleOptions options = (InternalScheduleOptions) data.get(QuartzScheduler.DATA_MAP_OPTIONS);
+ final String name = (String) data.get(QuartzScheduler.DATA_MAP_NAME);
+
+ final JobContext jobCtx = new JobContextImpl(name, options.configuration);
+ ((org.apache.karaf.scheduler.Job) job).execute(jobCtx);
+ } else if (job instanceof Runnable) {
+ ((Runnable) job).run();
+ } else {
+ logger.error("Scheduled job {} is neither a job nor a runnable.", job);
+ }
+ } catch (final Throwable t) {
+ // there is nothing we can do here, so we just log
+ logger.error("Exception during job execution of " + job + " : " + t.getMessage(), t);
+ }
+ }
+
+ public static final class JobContextImpl implements JobContext {
+
+ protected final Map<String, Serializable> configuration;
+ protected final String name;
+
+ public JobContextImpl(String name, Map<String, Serializable> config) {
+ this.name = name;
+ this.configuration = config;
+ }
+
+ /**
+ * @see org.apache.karaf.scheduler.JobContext#getConfiguration()
+ */
+ public Map<String, Serializable> getConfiguration() {
+ return this.configuration;
+ }
+
+ /**
+ * @see org.apache.karaf.scheduler.JobContext#getName()
+ */
+ public String getName() {
+ return this.name;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/src/main/java/org/apache/karaf/scheduler/core/QuartzScheduler.java
----------------------------------------------------------------------
diff --git a/scheduler/src/main/java/org/apache/karaf/scheduler/core/QuartzScheduler.java b/scheduler/src/main/java/org/apache/karaf/scheduler/core/QuartzScheduler.java
new file mode 100644
index 0000000..a25d4e4
--- /dev/null
+++ b/scheduler/src/main/java/org/apache/karaf/scheduler/core/QuartzScheduler.java
@@ -0,0 +1,294 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.scheduler.core;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.karaf.scheduler.Job;
+import org.apache.karaf.scheduler.ScheduleOptions;
+import org.apache.karaf.scheduler.Scheduler;
+import org.quartz.JobBuilder;
+import org.quartz.JobDataMap;
+import org.quartz.JobDetail;
+import org.quartz.JobKey;
+import org.quartz.SchedulerException;
+import org.quartz.Trigger;
+import org.quartz.impl.DirectSchedulerFactory;
+import org.quartz.impl.matchers.GroupMatcher;
+import org.quartz.simpl.RAMJobStore;
+import org.quartz.spi.ThreadPool;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The quartz based implementation of the scheduler.
+ *
+ */
+public class QuartzScheduler implements Scheduler {
+
+ /** Default logger. */
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private static final String PREFIX = "Apache Karaf Quartz Scheduler ";
+
+ private static final String QUARTZ_SCHEDULER_NAME = "ApacheKaraf";
+
+ /** Map key for the job object */
+ static final String DATA_MAP_OBJECT = "QuartzJobScheduler.Object";
+
+ /** Map key for the job name */
+ static final String DATA_MAP_NAME = "QuartzJobScheduler.JobName";
+
+ /** Map key for the scheduling options. */
+ static final String DATA_MAP_OPTIONS = "QuartzJobScheduler.Options";
+
+ /** Map key for the logger. */
+ static final String DATA_MAP_LOGGER = "QuartzJobScheduler.Logger";
+
+ /** The quartz scheduler. */
+ private volatile org.quartz.Scheduler scheduler;
+
+ public QuartzScheduler(ThreadPool threadPool) throws SchedulerException {
+ // SLING-2261 Prevent Quartz from checking for updates
+ System.setProperty("org.terracotta.quartz.skipUpdateCheck", Boolean.TRUE.toString());
+
+ final DirectSchedulerFactory factory = DirectSchedulerFactory.getInstance();
+ // unique run id
+ final String runID = new Date().toString().replace(' ', '_');
+ factory.createScheduler(QUARTZ_SCHEDULER_NAME, runID, threadPool, new RAMJobStore());
+ // quartz does not provide a way to get the scheduler by name AND runID, so we have to iterate!
+ final Iterator<org.quartz.Scheduler> allSchedulersIter = factory.getAllSchedulers().iterator();
+ while ( scheduler == null && allSchedulersIter.hasNext() ) {
+ final org.quartz.Scheduler current = allSchedulersIter.next();
+ if ( QUARTZ_SCHEDULER_NAME.equals(current.getSchedulerName())
+ && runID.equals(current.getSchedulerInstanceId()) ) {
+ scheduler = current;
+ }
+ }
+ if ( scheduler == null ) {
+ throw new SchedulerException("Unable to find new scheduler with name " + QUARTZ_SCHEDULER_NAME + " and run ID " + runID);
+ }
+
+ scheduler.start();
+ if ( this.logger.isDebugEnabled() ) {
+ this.logger.debug(PREFIX + "started.");
+ }
+ }
+
+ /**
+ * Deactivate this component.
+ * Stop the scheduler.
+ */
+ public void deactivate() {
+ final org.quartz.Scheduler s = this.scheduler;
+ this.scheduler = null;
+ this.dispose(s);
+ }
+
+ /**
+ * Dispose the quartz scheduler
+ * @param s The scheduler.
+ */
+ private void dispose(final org.quartz.Scheduler s) {
+ if ( s != null ) {
+ try {
+ s.shutdown();
+ } catch (SchedulerException e) {
+ this.logger.debug("Exception during shutdown of scheduler.", e);
+ }
+ if ( this.logger.isDebugEnabled() ) {
+ this.logger.debug(PREFIX + "stopped.");
+ }
+ }
+ }
+
+ /**
+ * Initialize the data map for the job executor.
+ */
+ private JobDataMap initDataMap(final String jobName,
+ final Object job,
+ final InternalScheduleOptions options) {
+ final JobDataMap jobDataMap = new JobDataMap();
+
+ jobDataMap.put(DATA_MAP_OBJECT, job);
+
+ jobDataMap.put(DATA_MAP_NAME, jobName);
+ jobDataMap.put(DATA_MAP_LOGGER, this.logger);
+ jobDataMap.put(DATA_MAP_OPTIONS, options);
+
+ return jobDataMap;
+ }
+
+ /**
+ * Create the job detail.
+ */
+ private JobDetail createJobDetail(final String name,
+ final JobDataMap jobDataMap,
+ final boolean concurrent) {
+ return JobBuilder.newJob((concurrent ? QuartzJobExecutor.class : NonParallelQuartzJobExecutor.class))
+ .withIdentity(name)
+ .usingJobData(jobDataMap)
+ .build();
+ }
+
+ /**
+ * Check the job object, either runnable or job is allowed
+ */
+ private void checkJob(final Object job)
+ throws IllegalArgumentException {
+ if (!(job instanceof Runnable) && !(job instanceof Job)) {
+ throw new IllegalArgumentException("Job object is neither an instance of " + Runnable.class.getName() + " nor " + Job.class.getName());
+ }
+ }
+
+ /** Used by the web console plugin. */
+ org.quartz.Scheduler getScheduler() {
+ return this.scheduler;
+ }
+
+ /**
+ * @see org.apache.karaf.scheduler.Scheduler#NOW()
+ */
+ public ScheduleOptions NOW() {
+ return AT(new Date());
+ }
+
+ /**
+ * @see org.apache.karaf.scheduler.Scheduler#NOW(int, long)
+ */
+ public ScheduleOptions NOW(int times, long period) {
+ return AT(new Date(), times, period);
+ }
+
+ /**
+ * @see org.apache.karaf.scheduler.Scheduler#AT(java.util.Date)
+ */
+ public ScheduleOptions AT(Date date) {
+ return new InternalScheduleOptions(date);
+ }
+
+ /**
+ * @see org.apache.karaf.scheduler.Scheduler#AT(java.util.Date, int, long)
+ */
+ public ScheduleOptions AT(Date date, int times, long period) {
+ return new InternalScheduleOptions(date, times, period);
+ }
+
+ /**
+ * @see org.apache.karaf.scheduler.Scheduler#EXPR(java.lang.String)
+ */
+ public ScheduleOptions EXPR(String expression) {
+ return new InternalScheduleOptions(expression);
+ }
+
+ /**
+ * Schedule a job
+ * @see org.apache.karaf.scheduler.Scheduler#schedule(java.lang.Object, org.apache.karaf.scheduler.ScheduleOptions)
+ * @throws SchedulerException if the job can't be scheduled
+ * @throws IllegalArgumentException If the preconditions are not met
+ */
+ public void schedule(final Object job, final ScheduleOptions options) throws IllegalArgumentException, SchedulerException {
+ this.checkJob(job);
+
+ if ( !(options instanceof InternalScheduleOptions)) {
+ throw new IllegalArgumentException("Options has not been created via schedule or is null.");
+ }
+ final InternalScheduleOptions opts = (InternalScheduleOptions)options;
+
+ if ( opts.argumentException != null ) {
+ throw opts.argumentException;
+ }
+
+ // as this method might be called from unbind and during
+ // unbind a deactivate could happen, we check the scheduler first
+ final org.quartz.Scheduler s = this.scheduler;
+ if ( s == null ) {
+ throw new IllegalStateException("Scheduler is not available anymore.");
+ }
+
+ final String name;
+ if ( opts.name != null ) {
+ // if there is already a job with the name, remove it first
+ try {
+ final JobKey key = JobKey.jobKey(opts.name);
+ final JobDetail jobdetail = s.getJobDetail(key);
+ if (jobdetail != null) {
+ s.deleteJob(key);
+ this.logger.debug("Unscheduling job with name {}", opts.name);
+ }
+ } catch (final SchedulerException ignored) {
+ // ignore
+ }
+ name = opts.name;
+ } else {
+ name = job.getClass().getName() + ':' + UUID.randomUUID();
+ }
+
+ final Trigger trigger = opts.trigger.withIdentity(name).build();
+
+ // create the data map
+ final JobDataMap jobDataMap = this.initDataMap(name, job, opts);
+
+ final JobDetail detail = this.createJobDetail(name, jobDataMap, opts.canRunConcurrently);
+
+ this.logger.debug("Scheduling job {} with name {} and trigger {}", job, name, trigger);
+ s.scheduleJob(detail, trigger);
+ }
+
+ /**
+ * @see org.apache.karaf.scheduler.Scheduler#unschedule(java.lang.String)
+ */
+ public boolean unschedule(final String jobName) {
+ final org.quartz.Scheduler s = this.scheduler;
+ if ( jobName != null && s != null ) {
+ try {
+ final JobKey key = JobKey.jobKey(jobName);
+ final JobDetail jobdetail = s.getJobDetail(key);
+ if (jobdetail != null) {
+ s.deleteJob(key);
+ this.logger.debug("Unscheduling job with name {}", jobName);
+ return true;
+ }
+ } catch (final SchedulerException ignored) {
+ // ignore
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Map<Object, ScheduleOptions> getJobs() throws SchedulerException {
+ Map<Object, ScheduleOptions> jobs = new HashMap<>();
+ org.quartz.Scheduler s = this.scheduler;
+ if (s != null) {
+ for (String group : s.getJobGroupNames()) {
+ for (JobKey key : s.getJobKeys(GroupMatcher.jobGroupEquals(group))) {
+ JobDetail detail = s.getJobDetail(key);
+ ScheduleOptions options = (ScheduleOptions) detail.getJobDataMap().get(DATA_MAP_OPTIONS);
+ Object job = detail.getJobDataMap().get(DATA_MAP_OBJECT);
+ jobs.put(job, options);
+ }
+ }
+ }
+ return jobs;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/src/main/java/org/apache/karaf/scheduler/core/WhiteboardHandler.java
----------------------------------------------------------------------
diff --git a/scheduler/src/main/java/org/apache/karaf/scheduler/core/WhiteboardHandler.java b/scheduler/src/main/java/org/apache/karaf/scheduler/core/WhiteboardHandler.java
new file mode 100644
index 0000000..2d0a9df
--- /dev/null
+++ b/scheduler/src/main/java/org/apache/karaf/scheduler/core/WhiteboardHandler.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.scheduler.core;
+
+import java.util.Date;
+
+import org.apache.karaf.scheduler.Job;
+import org.apache.karaf.scheduler.Scheduler;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The quartz based implementation of the scheduler.
+ *
+ */
+public class WhiteboardHandler {
+
+ /** Default logger. */
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private Scheduler scheduler;
+
+ private ServiceTracker<?,?> serviceTracker;
+
+ public WhiteboardHandler(final BundleContext context, Scheduler scheduler) throws InvalidSyntaxException {
+ this.scheduler = scheduler;
+ this.serviceTracker = new ServiceTracker<>(context,
+ context.createFilter("(|(" + Constants.OBJECTCLASS + "=" + Runnable.class.getName() + ")" +
+ "(" + Constants.OBJECTCLASS + "=" + Job.class.getName() + "))"),
+ new ServiceTrackerCustomizer<Object,Object>() {
+
+ public synchronized void removedService(final ServiceReference reference, final Object service) {
+ context.ungetService(reference);
+ unregister(reference, service);
+ }
+
+ public synchronized void modifiedService(final ServiceReference reference, final Object service) {
+ unregister(reference, service);
+ register(reference, service);
+ }
+
+ public synchronized Object addingService(final ServiceReference reference) {
+ final Object obj = context.getService(reference);
+ if ( obj != null ) {
+ register(reference, obj);
+ }
+ return obj;
+ }
+ });
+ this.serviceTracker.open();
+ }
+
+ /**
+ * Deactivate this component.
+ */
+ public void deactivate() {
+ this.serviceTracker.close();
+ }
+
+
+ /**
+ * Create unique identifier
+ */
+ private String getServiceIdentifier(final ServiceReference ref) {
+ String name = (String)ref.getProperty(Scheduler.PROPERTY_SCHEDULER_NAME);
+ if ( name == null ) {
+ name = (String)ref.getProperty(Constants.SERVICE_PID);
+ if ( name == null ) {
+ name = "Registered Service";
+ }
+ }
+ // now append service id to create a unique identifier
+ name = name + "." + ref.getProperty(Constants.SERVICE_ID);
+ return name;
+ }
+
+ /**
+ * Register a job or task
+ */
+ private void register(final ServiceReference ref, final Object job) {
+ final String name = getServiceIdentifier(ref);
+ final Boolean concurrent = (Boolean) ref.getProperty(Scheduler.PROPERTY_SCHEDULER_CONCURRENT);
+ final String expression = (String) ref.getProperty(Scheduler.PROPERTY_SCHEDULER_EXPRESSION);
+ try {
+ if (expression != null) {
+ this.scheduler.schedule(job, this.scheduler.EXPR(expression)
+ .name(name)
+ .canRunConcurrently((concurrent != null ? concurrent : true)));
+ } else {
+ final Long period = (Long) ref.getProperty(Scheduler.PROPERTY_SCHEDULER_PERIOD);
+ if (period != null) {
+ if (period < 1) {
+ this.logger.debug("Ignoring service {} : scheduler period is less than 1.", ref);
+ } else {
+ boolean immediate = false;
+ if (ref.getProperty(Scheduler.PROPERTY_SCHEDULER_IMMEDIATE) != null) {
+ immediate = (Boolean) ref.getProperty(Scheduler.PROPERTY_SCHEDULER_IMMEDIATE);
+ }
+ final Date date = new Date();
+ if (!immediate) {
+ date.setTime(System.currentTimeMillis() + period * 1000);
+ }
+ this.scheduler.schedule(job, this.scheduler.AT(date, -1, period)
+ .name(name)
+ .canRunConcurrently((concurrent != null ? concurrent : true)));
+ }
+ } else {
+ this.logger.debug("Ignoring service {} : no scheduling property found.", ref);
+ }
+ }
+ } catch (Exception e) {
+ logger.warn("Error scheduling job", e);
+ }
+ }
+
+ /**
+ * Unregister a service.
+ */
+ private void unregister(final ServiceReference reference, final Object service) {
+ final String name = getServiceIdentifier(reference);
+ this.scheduler.unschedule(name);
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/scheduler/src/main/resources/OSGI-INF/bundle.info
----------------------------------------------------------------------
diff --git a/scheduler/src/main/resources/OSGI-INF/bundle.info b/scheduler/src/main/resources/OSGI-INF/bundle.info
new file mode 100644
index 0000000..074eef4
--- /dev/null
+++ b/scheduler/src/main/resources/OSGI-INF/bundle.info
@@ -0,0 +1,23 @@
+h1. Synopsis
+
+${project.name}
+
+${project.description}
+
+Maven URL:
+[mvn:${project.groupId}/${project.artifactId}/${project.version}]
+
+h1. Description
+
+This bundle registers a service listener, which listens from service events related to java.lang.Runnable and org.apache.karaf.scheduler.Job interface.
+Each service with such interface may be added the following properties:
+
+scheduler.name
+scheduler.period
+scheduler.expression
+scheduler.immediate
+scheduler.concurrent
+
+One of scheduler.period or scheduler.expression is mandatory.
+
+
http://git-wip-us.apache.org/repos/asf/karaf/blob/eb61abc8/util/src/main/java/org/apache/karaf/util/tracker/BaseActivator.java
----------------------------------------------------------------------
diff --git a/util/src/main/java/org/apache/karaf/util/tracker/BaseActivator.java b/util/src/main/java/org/apache/karaf/util/tracker/BaseActivator.java
index 4f34b06..0948f0b 100644
--- a/util/src/main/java/org/apache/karaf/util/tracker/BaseActivator.java
+++ b/util/src/main/java/org/apache/karaf/util/tracker/BaseActivator.java
@@ -70,6 +70,7 @@ public class BaseActivator implements BundleActivator, SingleServiceTracker.Sing
try {
doStart();
} catch (Exception e) {
+ logger.warn("Error starting activator", e);
doStop();
}
} else {