ACE-533: Replaced the org.apache.ace.scheduler with Amdatu Scheduling

Updated all tasks to so the interval can be configured using a ManagedService / ManagedServiceFactory

Removed the org.apache.ace.scheduler project

This closes #11



git-svn-id: https://svn.apache.org/repos/asf/ace/trunk@1732893 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/build/bnd.bnd b/build/bnd.bnd
index 6ba6944..a0c55bc 100644
--- a/build/bnd.bnd
+++ b/build/bnd.bnd
@@ -35,7 +35,6 @@
 	org.apache.ace.repository,\
 	org.apache.ace.repository.itest,\
 	org.apache.ace.resourceprocessor.useradmin,\
-	org.apache.ace.scheduler,\
 	org.apache.ace.tageditor,\
 	org.apache.ace.target.mgmt.ui,\
 	org.apache.ace.test,\
diff --git a/cnf/localrepo/index.xml b/cnf/localrepo/index.xml
index b89751b..ce29495 100644
--- a/cnf/localrepo/index.xml
+++ b/cnf/localrepo/index.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<repository increment="1456402641000" name="Local" xmlns="http://www.osgi.org/xmlns/repository/v1.0.0">
+<repository increment="1456435126000" name="Local" xmlns="http://www.osgi.org/xmlns/repository/v1.0.0">
   <resource>
     <capability namespace="osgi.identity">
       <attribute name="osgi.identity" value="bcpkix"/>
@@ -3279,6 +3279,168 @@
   </resource>
   <resource>
     <capability namespace="osgi.identity">
+      <attribute name="osgi.identity" value="org.amdatu.scheduling.api"/>
+      <attribute name="type" value="osgi.bundle"/>
+      <attribute name="version" type="Version" value="1.1.0"/>
+    </capability>
+    <capability namespace="osgi.content">
+      <attribute name="osgi.content" value="1952c3bd69db1dd350be853dec02fc2e4b0df01ee99d739bf93384ae581b7cf5"/>
+      <attribute name="url" value="org.amdatu.scheduling.api/org.amdatu.scheduling.api-1.1.0.jar"/>
+      <attribute name="size" type="Long" value="25551"/>
+      <attribute name="mime" value="application/vnd.osgi.bundle"/>
+    </capability>
+    <capability namespace="osgi.wiring.bundle">
+      <attribute name="osgi.wiring.bundle" value="org.amdatu.scheduling.api"/>
+      <attribute name="bundle-version" type="Version" value="1.1.0"/>
+    </capability>
+    <capability namespace="osgi.wiring.host">
+      <attribute name="osgi.wiring.host" value="org.amdatu.scheduling.api"/>
+      <attribute name="bundle-version" type="Version" value="1.1.0"/>
+    </capability>
+    <capability namespace="osgi.wiring.package">
+      <attribute name="osgi.wiring.package" value="org.amdatu.scheduling"/>
+      <attribute name="version" type="Version" value="1.1.0"/>
+      <attribute name="bundle-symbolic-name" value="org.amdatu.scheduling.api"/>
+      <attribute name="bundle-version" type="Version" value="1.1.0"/>
+    </capability>
+    <capability namespace="osgi.wiring.package">
+      <attribute name="osgi.wiring.package" value="org.amdatu.scheduling.annotations"/>
+      <attribute name="version" type="Version" value="1.0.0"/>
+      <attribute name="bundle-symbolic-name" value="org.amdatu.scheduling.api"/>
+      <attribute name="bundle-version" type="Version" value="1.1.0"/>
+    </capability>
+    <capability namespace="osgi.wiring.package">
+      <attribute name="osgi.wiring.package" value="org.amdatu.scheduling.annotations.timeinterval"/>
+      <attribute name="version" type="Version" value="1.0.0"/>
+      <attribute name="bundle-symbolic-name" value="org.amdatu.scheduling.api"/>
+      <attribute name="bundle-version" type="Version" value="1.1.0"/>
+    </capability>
+    <capability namespace="osgi.wiring.package">
+      <attribute name="osgi.wiring.package" value="org.amdatu.scheduling.constants"/>
+      <attribute name="version" type="Version" value="1.0.0"/>
+      <attribute name="bundle-symbolic-name" value="org.amdatu.scheduling.api"/>
+      <attribute name="bundle-version" type="Version" value="1.1.0"/>
+    </capability>
+    <requirement namespace="osgi.ee">
+      <directive name="filter" value="(&amp;(osgi.ee=JavaSE)(version=1.8))"/>
+    </requirement>
+  </resource>
+  <resource>
+    <capability namespace="osgi.identity">
+      <attribute name="osgi.identity" value="org.amdatu.scheduling.quartz"/>
+      <attribute name="type" value="osgi.bundle"/>
+      <attribute name="version" type="Version" value="1.0.4"/>
+    </capability>
+    <capability namespace="osgi.content">
+      <attribute name="osgi.content" value="5c84f7cc01ac2a5b3cdeeaa17fa9421e24281470bc1c8b8e281b3cadb5a9e3ac"/>
+      <attribute name="url" value="org.amdatu.scheduling.quartz/org.amdatu.scheduling.quartz-1.0.4.jar"/>
+      <attribute name="size" type="Long" value="629229"/>
+      <attribute name="mime" value="application/vnd.osgi.bundle"/>
+    </capability>
+    <capability namespace="osgi.wiring.bundle">
+      <attribute name="osgi.wiring.bundle" value="org.amdatu.scheduling.quartz"/>
+      <attribute name="bundle-version" type="Version" value="1.0.4"/>
+    </capability>
+    <capability namespace="osgi.wiring.host">
+      <attribute name="osgi.wiring.host" value="org.amdatu.scheduling.quartz"/>
+      <attribute name="bundle-version" type="Version" value="1.0.4"/>
+    </capability>
+    <capability namespace="osgi.wiring.package">
+      <attribute name="osgi.wiring.package" value="org.quartz"/>
+      <attribute name="version" type="Version" value="1.0.4"/>
+      <attribute name="bundle-symbolic-name" value="org.amdatu.scheduling.quartz"/>
+      <attribute name="bundle-version" type="Version" value="1.0.4"/>
+    </capability>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=com.mchange.v2.c3p0)"/>
+      <directive name="resolution" value="optional"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=javax.ejb)"/>
+      <directive name="resolution" value="optional"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=javax.mail)"/>
+      <directive name="resolution" value="optional"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=javax.mail.internet)"/>
+      <directive name="resolution" value="optional"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=javax.jms)"/>
+      <directive name="resolution" value="optional"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=javax.rmi)"/>
+      <directive name="resolution" value="optional"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=javax.naming)"/>
+      <directive name="resolution" value="optional"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=javax.servlet)"/>
+      <directive name="resolution" value="optional"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=javax.servlet.http)"/>
+      <directive name="resolution" value="optional"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=javax.management)"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=javax.management.openmbean)"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=javax.sql)"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=javax.transaction)"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=javax.xml.bind)"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=javax.xml.namespace)"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=javax.xml.parsers)"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=javax.xml.xpath)"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=org.amdatu.scheduling)"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(&amp;(osgi.wiring.package=org.amdatu.scheduling.annotations)(version&gt;=1.0.0)(!(version&gt;=2.0.0)))"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(&amp;(osgi.wiring.package=org.amdatu.scheduling.annotations.timeinterval)(version&gt;=1.0.0)(!(version&gt;=2.0.0)))"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(&amp;(osgi.wiring.package=org.apache.felix.dm)(version&gt;=4.0.0)(!(version&gt;=5.0.0)))"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(&amp;(osgi.wiring.package=org.osgi.framework)(version&gt;=1.6.0)(!(version&gt;=2.0.0)))"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(&amp;(osgi.wiring.package=org.osgi.service.log)(version&gt;=1.3.0)(!(version&gt;=2.0.0)))"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=org.w3c.dom)"/>
+    </requirement>
+    <requirement namespace="osgi.wiring.package">
+      <directive name="filter" value="(osgi.wiring.package=org.xml.sax)"/>
+    </requirement>
+    <requirement namespace="osgi.ee">
+      <directive name="filter" value="(&amp;(osgi.ee=JavaSE)(version=1.8))"/>
+    </requirement>
+  </resource>
+  <resource>
+    <capability namespace="osgi.identity">
       <attribute name="osgi.identity" value="org.apache.commons.cli"/>
       <attribute name="type" value="osgi.bundle"/>
       <attribute name="version" type="Version" value="1.2.0"/>
diff --git a/cnf/localrepo/index.xml.sha b/cnf/localrepo/index.xml.sha
index 84c1f82..d227bdf 100644
--- a/cnf/localrepo/index.xml.sha
+++ b/cnf/localrepo/index.xml.sha
@@ -1 +1 @@
-4ead2da8675a8fd5eaae16d020d3a301198fa9ce892374828661f51091ae6f11
\ No newline at end of file
+12e0829683c24b79a9343db8aa3a17d08b7d0b614587f0967a4c4a100ab3ed57
\ No newline at end of file
diff --git a/cnf/localrepo/org.amdatu.scheduling.api/org.amdatu.scheduling.api-1.1.0.jar b/cnf/localrepo/org.amdatu.scheduling.api/org.amdatu.scheduling.api-1.1.0.jar
new file mode 100644
index 0000000..f076640
--- /dev/null
+++ b/cnf/localrepo/org.amdatu.scheduling.api/org.amdatu.scheduling.api-1.1.0.jar
Binary files differ
diff --git a/cnf/localrepo/org.amdatu.scheduling.quartz/org.amdatu.scheduling.quartz-1.0.4.jar b/cnf/localrepo/org.amdatu.scheduling.quartz/org.amdatu.scheduling.quartz-1.0.4.jar
new file mode 100644
index 0000000..488829b
--- /dev/null
+++ b/cnf/localrepo/org.amdatu.scheduling.quartz/org.amdatu.scheduling.quartz-1.0.4.jar
Binary files differ
diff --git a/org.apache.ace.authentication.itest/bnd.bnd b/org.apache.ace.authentication.itest/bnd.bnd
index a3883da..5462ec7 100644
--- a/org.apache.ace.authentication.itest/bnd.bnd
+++ b/org.apache.ace.authentication.itest/bnd.bnd
@@ -8,6 +8,7 @@
 	org.apache.ace.test;version=latest,\
 	org.apache.felix.dependencymanager,\
 	org.apache.felix.http.api,\
+	org.amdatu.scheduling.api,\
 	org.apache.ace.connectionfactory;version=latest,\
 	org.apache.ace.repository.api;version=latest,\
 	org.apache.ace.log.server.store.api;version=latest,\
@@ -32,6 +33,8 @@
 	org.apache.felix.http.servlet-api,\
 	org.apache.felix.http.jetty,\
 	org.apache.felix.useradmin,\
+	org.amdatu.scheduling.api,\
+	org.amdatu.scheduling.quartz,\
 	org.apache.ace.authentication.api;version=latest,\
 	org.apache.ace.authentication.impl;version=latest,\
 	org.apache.ace.authentication.processor.basicauth;version=latest,\
diff --git a/org.apache.ace.authentication.itest/src/org/apache/ace/it/authentication/LogAuthenticationTest.java b/org.apache.ace.authentication.itest/src/org/apache/ace/it/authentication/LogAuthenticationTest.java
index 8a5a874..6b0caff 100644
--- a/org.apache.ace.authentication.itest/src/org/apache/ace/it/authentication/LogAuthenticationTest.java
+++ b/org.apache.ace.authentication.itest/src/org/apache/ace/it/authentication/LogAuthenticationTest.java
@@ -29,6 +29,7 @@
 import java.util.Properties;
 import java.util.concurrent.TimeUnit;
 
+import org.amdatu.scheduling.Job;
 import org.apache.ace.client.repository.SessionFactory;
 import org.apache.ace.connectionfactory.ConnectionFactory;
 import org.apache.ace.discovery.DiscoveryConstants;
@@ -42,7 +43,6 @@
 import org.apache.ace.test.constants.TestConstants;
 import org.apache.ace.test.utils.NetUtils;
 import org.apache.felix.dm.Component;
-import org.osgi.framework.Constants;
 import org.osgi.service.http.HttpService;
 import org.osgi.service.log.LogReaderService;
 import org.osgi.service.useradmin.UserAdmin;
@@ -61,7 +61,7 @@
 
     private volatile Log m_auditLog;
     private volatile LogStore m_serverStore;
-    private volatile Runnable m_auditLogSyncTask;
+    private volatile Job m_auditLogSyncTask;
     private volatile Repository m_userRepository;
     private volatile UserAdmin m_userAdmin;
     private volatile ConnectionFactory m_connectionFactory;
@@ -79,9 +79,9 @@
                     .setRequired(true))
                 .add(createServiceDependency().setService(ConnectionFactory.class).setRequired(true))
                 .add(createServiceDependency().setService(HttpService.class).setRequired(true))
-                .add(createServiceDependency().setService(Log.class, "(&(" + Constants.OBJECTCLASS + "=" + Log.class.getName() + ")(name=auditlog))").setRequired(true))
-                .add(createServiceDependency().setService(LogStore.class, "(&(" + Constants.OBJECTCLASS + "=" + LogStore.class.getName() + ")(name=auditlog))").setRequired(true))
-                .add(createServiceDependency().setService(Runnable.class, "(&(" + Constants.OBJECTCLASS + "=" + Runnable.class.getName() + ")(taskName=auditlog))").setRequired(true))
+                .add(createServiceDependency().setService(Log.class, "(name=auditlog)").setRequired(true))
+                .add(createServiceDependency().setService(LogStore.class, "(name=auditlog)").setRequired(true))
+                .add(createServiceDependency().setService(Job.class, "(taskName=auditlog)").setRequired(true))
         };
     }
 
@@ -139,7 +139,8 @@
         configureFactory("org.apache.ace.target.log.factory",
             "name", "auditlog");
         configureFactory("org.apache.ace.target.log.sync.factory",
-            "name", "auditlog");
+            "name", "auditlog",
+            "syncInterval", "1000");
         configureFactory("org.apache.ace.log.server.servlet.factory",
             "name", "auditlog", "endpoint", AUDITLOG_ENDPOINT);
         configureFactory("org.apache.ace.log.server.store.factory",
@@ -218,7 +219,7 @@
 
             while (!found && ((System.currentTimeMillis() - startTime) < waitTime)) {
                 // synchronize again
-                m_auditLogSyncTask.run();
+                m_auditLogSyncTask.execute();
 
                 // get and evaluate results (note that there is some concurrency that might interfere with this test)
                 List<Descriptor> ranges2 = m_serverStore.getDescriptors();
diff --git a/org.apache.ace.client.automation/bnd.bnd b/org.apache.ace.client.automation/bnd.bnd
index 963904d..000941b 100644
--- a/org.apache.ace.client.automation/bnd.bnd
+++ b/org.apache.ace.client.automation/bnd.bnd
@@ -6,7 +6,7 @@
 	osgi.core;version=6.0.0,\
 	osgi.cmpn,\
 	org.apache.felix.dependencymanager,\
-	org.apache.ace.scheduler.api;version=latest,\
+	org.amdatu.scheduling.api,\
 	org.apache.ace.client.repository.api;version=latest
 
 Private-Package: org.apache.ace.client.automation
diff --git a/org.apache.ace.client.automation/src/org/apache/ace/client/automation/AutoTargetOperator.java b/org.apache.ace.client.automation/src/org/apache/ace/client/automation/AutoTargetOperator.java
index 28a7fa5..2c125ff 100644
--- a/org.apache.ace.client.automation/src/org/apache/ace/client/automation/AutoTargetOperator.java
+++ b/org.apache.ace.client.automation/src/org/apache/ace/client/automation/AutoTargetOperator.java
@@ -24,12 +24,13 @@
 import java.util.Hashtable;
 import java.util.List;
 
+import org.amdatu.scheduling.Job;
+import org.amdatu.scheduling.constants.Constants;
 import org.apache.ace.client.repository.RepositoryAdmin;
 import org.apache.ace.client.repository.RepositoryAdminLoginContext;
 import org.apache.ace.client.repository.object.TargetObject;
 import org.apache.ace.client.repository.stateful.StatefulTargetObject;
 import org.apache.ace.client.repository.stateful.StatefulTargetRepository;
-import org.apache.ace.scheduler.constants.SchedulerConstants;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceRegistration;
@@ -39,6 +40,7 @@
 import org.osgi.service.useradmin.User;
 import org.osgi.service.useradmin.UserAdmin;
 
+
 /**
  * Automatic target operator, when configured will automatically register, approve, auto-approve and commit targets to
  * the repository. An LDAP filter can be used to filter for the correct targets.
@@ -54,7 +56,7 @@
     private volatile BundleContext m_bundleContext;
     private volatile LogService m_log;
     private volatile Dictionary<String, ?> m_settings;
-    private volatile ServiceRegistration<Runnable> m_serviceReg;
+    private volatile ServiceRegistration<Job> m_serviceReg;
 
     // used for processing the auditlog (tell the repository about that)
     private final AuditLogProcessTask m_task = new AuditLogProcessTask();
@@ -81,8 +83,8 @@
 
             // start refresh task
             Dictionary<String, Object> props = new Hashtable<>();
-            props.put(SchedulerConstants.SCHEDULER_NAME_KEY, SCHEDULER_NAME);
-            m_serviceReg = m_bundleContext.registerService(Runnable.class, m_task, props);
+            props.put("name", SCHEDULER_NAME);
+            m_serviceReg = m_bundleContext.registerService(Job.class, m_task, props);
         }
         catch (IOException ioe) {
             m_log.log(LogService.LOG_ERROR, "Unable to login at repository admin.", ioe);
@@ -111,11 +113,12 @@
      * Runnable that will synchronize audit log data with the server and tell the repository about the changes if
      * applicable.
      */
-    private final class AuditLogProcessTask implements Runnable {
+    private final class AuditLogProcessTask implements Job {
 
         private final Object m_lock = new Object();
-
-        public void process() {
+        
+        @Override
+        public void execute() {
             // perform synchronous model actions
             synchronized (m_lock) {
                 m_statefulTargetRepos.refresh();
@@ -144,10 +147,6 @@
                 }
             }
         }
-
-        public void run() {
-            process();
-        }
     }
 
     private void checkoutModel() throws IOException {
@@ -206,6 +205,30 @@
             }
             // store configuration
             m_settings = settings;
+            
+            Long interval = null;
+            
+            Object value = settings.get("interval");
+            if (value != null) {
+                try {
+                    interval = Long.valueOf(value.toString());
+                }catch (NumberFormatException e) {
+                    throw new ConfigurationException("interval", "Interval must be a valid Long value", e);
+                }
+            
+                Dictionary<String, Object> serviceProps = new Hashtable<>();
+                serviceProps.put("name", SCHEDULER_NAME);
+                if (interval == null) {
+                    serviceProps.remove(Constants.REPEAT_FOREVER);
+                    serviceProps.remove(Constants.REPEAT_INTERVAL_PERIOD);
+                    serviceProps.remove(Constants.REPEAT_INTERVAL_VALUE);
+                } else {
+                    serviceProps.put(Constants.REPEAT_FOREVER, true);
+                    serviceProps.put(Constants.REPEAT_INTERVAL_PERIOD, "milisecond");
+                    serviceProps.put(Constants.REPEAT_INTERVAL_VALUE, interval);
+                }
+                m_serviceReg.setProperties(serviceProps);
+            }
         }
     }
 
diff --git a/org.apache.ace.client.repository.itest/bnd.bnd b/org.apache.ace.client.repository.itest/bnd.bnd
index 6851461..a55df13 100644
--- a/org.apache.ace.client.repository.itest/bnd.bnd
+++ b/org.apache.ace.client.repository.itest/bnd.bnd
@@ -8,12 +8,12 @@
 	org.apache.felix.http.api,\
 	org.apache.felix.http.servlet-api,\
 	org.apache.felix.dependencymanager,\
+	org.amdatu.scheduling.api,\
 	org.apache.ace.test;version=latest,\
 	org.apache.ace.discovery.api;version=latest,\
 	org.apache.ace.discovery.property;version=latest,\
 	org.apache.ace.identification.api;version=latest,\
 	org.apache.ace.identification.property;version=latest,\
-	org.apache.ace.scheduler.api;version=latest,\
 	org.apache.ace.deployment.servlet;version=latest,\
 	org.apache.ace.obr.storage;version=latest,\
 	org.apache.ace.repository.api;version=latest,\
@@ -35,11 +35,11 @@
 	org.apache.felix.useradmin,\
 	org.apache.felix.useradmin.filestore,\
 	org.apache.felix.log,\
+	org.amdatu.scheduling.api,\
+	org.amdatu.scheduling.quartz,\
 	org.apache.ace.test;version=latest,\
 	org.apache.ace.authentication.api;version=latest,\
 	org.apache.ace.connectionfactory;version=latest,\
-	org.apache.ace.scheduler.impl;version=latest,\
-	org.apache.ace.scheduler.api;version=latest,\
 	org.apache.ace.deployment.provider.api;version=latest,\
 	org.apache.ace.discovery.api;version=latest,\
 	org.apache.ace.discovery.property;version=latest,\
diff --git a/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/ClientAutomationTest.java b/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/ClientAutomationTest.java
index accb6d6..001e176 100644
--- a/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/ClientAutomationTest.java
+++ b/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/ClientAutomationTest.java
@@ -25,11 +25,11 @@
 import java.util.List;
 import java.util.concurrent.Callable;
 
+import org.amdatu.scheduling.Job;
 import org.apache.ace.client.repository.RepositoryAdmin;
 import org.apache.ace.client.repository.stateful.StatefulTargetObject;
 import org.apache.ace.feedback.AuditEvent;
 import org.apache.ace.feedback.Event;
-import org.apache.ace.scheduler.constants.SchedulerConstants;
 import org.osgi.framework.Constants;
 import org.osgi.service.cm.Configuration;
 import org.osgi.util.tracker.ServiceTracker;
@@ -95,18 +95,18 @@
         int initRepoSize = m_statefulTargetRepository.get().size();
 
         // Get the processauditlog task and run it
-        ServiceTracker<Runnable, Runnable> tracker = new ServiceTracker<>(
+        ServiceTracker<Job, Job> tracker = new ServiceTracker<>(
             m_bundleContext, m_bundleContext.createFilter("(&(" + Constants.OBJECTCLASS + "="
-                + Runnable.class.getName() + ")(" + SchedulerConstants.SCHEDULER_NAME_KEY + "="
+                + Job.class.getName() + ")(name="
                 + "org.apache.ace.client.processauditlog" + "))"), null);
         tracker.open();
 
-        final Runnable processAuditlog = tracker.waitForService(2000);
+        final Job processAuditlog = tracker.waitForService(2000);
         if (processAuditlog != null) {
             // commit should be called
             runAndWaitForEvent(new Callable<Object>() {
                 public Object call() throws Exception {
-                    processAuditlog.run();
+                    processAuditlog.execute();
                     return null;
                 }
             }, false, RepositoryAdmin.TOPIC_REFRESH);
@@ -127,7 +127,7 @@
             m_auditLogStore.put(events);
 
             // do auto target action
-            processAuditlog.run();
+            processAuditlog.execute();
             assertEquals("After refresh, we expect an additional target based on auditlogdata;", initRepoSize + 2, m_statefulTargetRepository.get().size());
 
             sgoList = m_statefulTargetRepository.get(m_bundleContext.createFilter("(id=second*)"));
diff --git a/org.apache.ace.client.rest.itest/bnd.bnd b/org.apache.ace.client.rest.itest/bnd.bnd
index 5c3db99..fc5ae6f 100644
--- a/org.apache.ace.client.rest.itest/bnd.bnd
+++ b/org.apache.ace.client.rest.itest/bnd.bnd
@@ -15,7 +15,6 @@
 	org.apache.ace.identification.api;version=latest,\
 	org.apache.ace.identification.property;version=latest,\
 	org.apache.ace.log.server.store.api;version=latest,\
-	org.apache.ace.scheduler.api;version=latest,\
 	org.apache.ace.deployment.servlet;version=latest,\
 	org.apache.ace.obr.storage;version=latest,\
 	org.apache.ace.repository.api;version=latest,\
@@ -41,8 +40,6 @@
 	org.apache.ace.test;version=latest,\
 	org.apache.ace.authentication.api;version=latest,\
 	org.apache.ace.connectionfactory;version=latest,\
-	org.apache.ace.scheduler.impl;version=latest,\
-	org.apache.ace.scheduler.api;version=latest,\
 	org.apache.ace.deployment.provider.api;version=latest,\
 	org.apache.ace.discovery.api;version=latest,\
 	org.apache.ace.discovery.property;version=latest,\
diff --git a/org.apache.ace.deployment.itest/bnd.bnd b/org.apache.ace.deployment.itest/bnd.bnd
index 2b65ee3..b8b7ead 100644
--- a/org.apache.ace.deployment.itest/bnd.bnd
+++ b/org.apache.ace.deployment.itest/bnd.bnd
@@ -15,7 +15,6 @@
 	org.apache.ace.identification.api;version=latest,\
 	org.apache.ace.identification.property;version=latest,\
 	org.apache.ace.log.server.store.api;version=latest,\
-	org.apache.ace.scheduler.api;version=latest,\
 	org.apache.ace.deployment.servlet;version=latest,\
 	org.apache.ace.deployment.provider.api;version=latest,\
 	org.apache.ace.deployment.util.test;version=latest,\
@@ -51,9 +50,6 @@
 	org.apache.ace.repository.ext;version=latest,\
 	org.apache.ace.repository.impl;version=latest,\
 	org.apache.ace.repository.servlets;version=latest,\
-	org.apache.ace.repository.task;version=latest,\
-	org.apache.ace.scheduler.impl;version=latest,\
-	org.apache.ace.scheduler.api;version=latest,\
 	org.apache.ace.test;version=latest,\
 	org.apache.ace.range.api;version=latest,\
 	org.apache.ace.http.context;version=latest
diff --git a/org.apache.ace.deployment/bnd.bnd b/org.apache.ace.deployment/bnd.bnd
index 8608dc8..bf78134 100644
--- a/org.apache.ace.deployment/bnd.bnd
+++ b/org.apache.ace.deployment/bnd.bnd
@@ -19,7 +19,6 @@
 	org.apache.ace.connectionfactory;version=latest,\
 	org.apache.ace.repository.api;version=latest,\
 	org.apache.ace.repository.ext;version=latest,\
-	org.apache.ace.scheduler.api;version=latest,\
 	org.apache.ace.test;version=latest,\
 	org.apache.ace.range.api;version=latest,\
 	org.apache.ace.http.api;version=latest,\
diff --git a/org.apache.ace.log.itest/bnd.bnd b/org.apache.ace.log.itest/bnd.bnd
index eb5e268..67c2216 100644
--- a/org.apache.ace.log.itest/bnd.bnd
+++ b/org.apache.ace.log.itest/bnd.bnd
@@ -7,6 +7,7 @@
 	osgi.cmpn,\
 	org.apache.felix.dependencymanager,\
 	org.apache.felix.http.api,\
+	org.amdatu.scheduling.api,\
 	org.apache.ace.test;version=latest,\
 	org.apache.ace.discovery.api;version=latest,\
 	org.apache.ace.discovery.property;version=latest,\
@@ -23,6 +24,8 @@
 	org.apache.felix.http.servlet-api,\
 	org.apache.felix.http.jetty,\
 	org.apache.felix.eventadmin,\
+	org.amdatu.scheduling.api,\
+	org.amdatu.scheduling.quartz,\
 	org.apache.ace.authentication.api;version=latest,\
 	org.apache.ace.authentication.processor.basicauth;version=latest,\
 	org.apache.ace.client.repository.api;version=latest,\
diff --git a/org.apache.ace.log.itest/src/org/apache/ace/it/log/LogIntegrationTest.java b/org.apache.ace.log.itest/src/org/apache/ace/it/log/LogIntegrationTest.java
index f6db050..7ca07e2 100644
--- a/org.apache.ace.log.itest/src/org/apache/ace/it/log/LogIntegrationTest.java
+++ b/org.apache.ace.log.itest/src/org/apache/ace/it/log/LogIntegrationTest.java
@@ -23,6 +23,7 @@
 import java.util.Properties;
 import java.util.concurrent.TimeUnit;
 
+import org.amdatu.scheduling.Job;
 import org.apache.ace.discovery.DiscoveryConstants;
 import org.apache.ace.feedback.Descriptor;
 import org.apache.ace.feedback.Event;
@@ -32,7 +33,6 @@
 import org.apache.ace.log.server.store.LogStore;
 import org.apache.ace.test.constants.TestConstants;
 import org.apache.felix.dm.Component;
-import org.osgi.framework.Constants;
 import org.osgi.service.http.HttpService;
 
 /**
@@ -49,7 +49,7 @@
     private volatile Log m_auditLog;
     private volatile LogStore m_serverStore;
 
-    private volatile Runnable m_auditLogSyncTask;
+    private volatile Job m_auditLogSyncTask;
     
     public void testLog() throws Exception {
     	// XXX there appears to be a dependency between both these test-methods!!!
@@ -87,9 +87,9 @@
             createComponent()
                 .setImplementation(this)
                 .add(createServiceDependency().setService(HttpService.class).setRequired(true))
-                .add(createServiceDependency().setService(Log.class, "(&("+ Constants.OBJECTCLASS+"="+Log.class.getName()+")(name=auditlog))").setRequired(true))
-                .add(createServiceDependency().setService(LogStore.class, "(&("+Constants.OBJECTCLASS+"="+LogStore.class.getName()+")(name=auditlog))").setRequired(true))
-                .add(createServiceDependency().setService(Runnable.class, "(&("+Constants.OBJECTCLASS+"="+Runnable.class.getName()+")(taskName=auditlog))").setRequired(true))
+                .add(createServiceDependency().setService(Log.class, "(name=auditlog)").setRequired(true))
+                .add(createServiceDependency().setService(LogStore.class, "(name=auditlog)").setRequired(true))
+                .add(createServiceDependency().setService(Job.class, "(taskName=auditlog)").setRequired(true))
         };
     }
 
@@ -104,7 +104,7 @@
         long startTime = System.currentTimeMillis();
         while ((!found) && (System.currentTimeMillis() - startTime < 5000)) {
             // synchronize again
-            m_auditLogSyncTask.run();
+            m_auditLogSyncTask.execute();
 
             // get and evaluate results (note that there is some concurrency that might interfere with this test)
             List<Descriptor> ranges2 = m_serverStore.getDescriptors();
diff --git a/org.apache.ace.log/bnd.bnd b/org.apache.ace.log/bnd.bnd
index 30abf9d..58dada3 100644
--- a/org.apache.ace.log/bnd.bnd
+++ b/org.apache.ace.log/bnd.bnd
@@ -8,11 +8,11 @@
 	org.apache.felix.http.api,\
 	org.apache.felix.http.servlet-api,\
 	org.apache.felix.dependencymanager,\
+	org.amdatu.scheduling.api,\
 	org.apache.ace.range.api;version=latest,\
 	org.apache.ace.test;version=latest,\
 	org.apache.ace.identification.api;version=latest,\
 	org.apache.ace.discovery.api;version=latest,\
-	org.apache.ace.scheduler.api;version=latest,\
 	org.apache.ace.connectionfactory;version=latest,\
 	org.apache.ace.authentication.api;version=latest,\
 	org.apache.ace.feedback.common;version=latest,\
diff --git a/org.apache.ace.log/src/org/apache/ace/log/server/task/Activator.java b/org.apache.ace.log/src/org/apache/ace/log/server/task/Activator.java
index e4c1d18..cd8c3d8 100644
--- a/org.apache.ace.log/src/org/apache/ace/log/server/task/Activator.java
+++ b/org.apache.ace.log/src/org/apache/ace/log/server/task/Activator.java
@@ -18,11 +18,17 @@
  */
 package org.apache.ace.log.server.task;
 
+import static org.amdatu.scheduling.constants.Constants.DESCRIPTION;
+import static org.amdatu.scheduling.constants.Constants.REPEAT_FOREVER;
+import static org.amdatu.scheduling.constants.Constants.REPEAT_INTERVAL_PERIOD;
+import static org.amdatu.scheduling.constants.Constants.REPEAT_INTERVAL_VALUE;
+
 import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
 
+import org.amdatu.scheduling.Job;
 import org.apache.ace.connectionfactory.ConnectionFactory;
 import org.apache.ace.discovery.Discovery;
 import org.apache.ace.log.LogSync;
@@ -42,6 +48,7 @@
     private static final String KEY_MODE = "mode";
     private static final String KEY_MODE_LOWEST_IDS = "mode-lowest-ids";
     private static final String KEY_TARGETID = "tid";
+    private static final String KEY_SYNC_INTERVAL = "syncInterval";
     
     private final Map<String, Component> m_instances = new HashMap<>();
     private volatile DependencyManager m_manager;
@@ -70,6 +77,18 @@
         if ((name == null) || "".equals(name)) {
             throw new ConfigurationException(KEY_LOG_NAME, "Log name has to be specified.");
         }
+        Long syncInterval = 2000L;
+        String interval = (String) dict.get(KEY_SYNC_INTERVAL);
+        if (interval != null) {
+            try {
+                syncInterval = Long.valueOf(interval);
+            } catch (NumberFormatException e) {
+                throw new ConfigurationException(KEY_SYNC_INTERVAL, "Log sync interval has to be a valid long value.");
+            }
+        } else {
+            throw new ConfigurationException(KEY_SYNC_INTERVAL, "Log sync interval has to be specified.");
+        }
+        
         Mode dataTransferMode = Mode.PUSH;
         String modeValue = (String) dict.get(KEY_MODE);
         if ("pull".equals(modeValue)) {
@@ -98,12 +117,15 @@
         
         Properties props = new Properties();
         props.put(KEY_LOG_NAME, name);
+        props.put(REPEAT_FOREVER, true);
+        props.put(REPEAT_INTERVAL_PERIOD, "millisecond");
+        props.put(REPEAT_INTERVAL_VALUE, syncInterval);
         props.put("taskName", LogSyncTask.class.getName());
-        props.put("description", "Syncs log (name=" + name + ", mode=" + dataTransferMode.toString() + (targetID == null ? "" : ", targetID=" + targetID) + ") with a server.");
+        props.put(DESCRIPTION, "Syncs log (name=" + name + ", mode=" + dataTransferMode.toString() + (targetID == null ? "" : ", targetID=" + targetID) + ") with a server.");
         String filter = "(&(" + Constants.OBJECTCLASS + "=" + LogStore.class.getName() + ")(name=" + name + "))";
         LogSyncTask service = new LogSyncTask(name, name, dataTransferMode, lowestIDsMode, targetID);
         newComponent = m_manager.createComponent()
-    		.setInterface(new String[] { Runnable.class.getName(), LogSync.class.getName() }, props)
+    		.setInterface(new String[] { Job.class.getName(), LogSync.class.getName() }, props)
     		.setImplementation(service)
     		.add(createServiceDependency().setService(ConnectionFactory.class).setRequired(true))
     		.add(createServiceDependency().setService(LogStore.class, filter).setRequired(true))
diff --git a/org.apache.ace.log/src/org/apache/ace/log/server/task/LogSyncTask.java b/org.apache.ace.log/src/org/apache/ace/log/server/task/LogSyncTask.java
index b9b9636..802ccba 100644
--- a/org.apache.ace.log/src/org/apache/ace/log/server/task/LogSyncTask.java
+++ b/org.apache.ace.log/src/org/apache/ace/log/server/task/LogSyncTask.java
@@ -37,6 +37,7 @@
 
 import javax.servlet.http.HttpServletResponse;
 
+import org.amdatu.scheduling.Job;
 import org.apache.ace.connectionfactory.ConnectionFactory;
 import org.apache.ace.discovery.Discovery;
 import org.apache.ace.feedback.Descriptor;
@@ -47,7 +48,7 @@
 import org.apache.ace.range.SortedRangeSet;
 import org.osgi.service.log.LogService;
 
-public class LogSyncTask implements Runnable, LogSync {
+public class LogSyncTask implements Job, LogSync {
 
     public static enum Mode {
         NONE, PUSH, PULL, PUSHPULL
@@ -115,8 +116,9 @@
     public boolean pushpullIDs() throws IOException {
     	return synchronizeLowestIDs(true, true);
     }
-
-    public void run() {
+    
+    @Override
+    public void execute() {
         try {
             switch (m_lowestIDMode) {
 	            case NONE:
diff --git a/org.apache.ace.log/src/org/apache/ace/log/target/LogSyncConfigurator.java b/org.apache.ace.log/src/org/apache/ace/log/target/LogSyncConfigurator.java
index 7fdd2e7..b79d351 100644
--- a/org.apache.ace.log/src/org/apache/ace/log/target/LogSyncConfigurator.java
+++ b/org.apache.ace.log/src/org/apache/ace/log/target/LogSyncConfigurator.java
@@ -18,17 +18,22 @@
  */
 package org.apache.ace.log.target;
 
+import static org.amdatu.scheduling.constants.Constants.DESCRIPTION;
+import static org.amdatu.scheduling.constants.Constants.REPEAT_FOREVER;
+import static org.amdatu.scheduling.constants.Constants.REPEAT_INTERVAL_PERIOD;
+import static org.amdatu.scheduling.constants.Constants.REPEAT_INTERVAL_VALUE;
+
 import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Map;
 
+import org.amdatu.scheduling.Job;
 import org.apache.ace.connectionfactory.ConnectionFactory;
 import org.apache.ace.discovery.Discovery;
 import org.apache.ace.identification.Identification;
 import org.apache.ace.log.target.store.LogStore;
 import org.apache.ace.log.target.task.LogSyncTask;
-import org.apache.ace.scheduler.constants.SchedulerConstants;
 import org.apache.felix.dm.Component;
 import org.apache.felix.dm.DependencyManager;
 import org.osgi.framework.Constants;
@@ -39,6 +44,7 @@
 public class LogSyncConfigurator implements ManagedServiceFactory {
     private static final String MA_NAME = "ma";
     private static final String LOG_NAME = "name";
+    private static final String LOG_SYNC_INTERVAL = "syncInterval";
 
     private DependencyManager m_manager;
     private final Map<String, Component> m_syncInstances = new HashMap<>();
@@ -61,6 +67,16 @@
         if ((name == null) || "".equals(name)) {
             throw new ConfigurationException(LOG_NAME, "Log name has to be specified.");
         }
+        
+        Long syncInterval = 2000L;
+        String interval = (String) dict.get(LOG_SYNC_INTERVAL);
+        if (interval != null) {
+            try {
+                syncInterval = Long.valueOf(interval);
+            } catch (NumberFormatException e) {
+                throw new ConfigurationException(LOG_SYNC_INTERVAL, "Log sync interval has to be a valid long value.");
+            }
+        }
 
         Component service = (Component) m_syncInstances.get(pid);
         if (service == null) {
@@ -89,12 +105,15 @@
                 description = "Task that synchronizes log store with id=" + name + " and ma=" + ma + " on the target and server";
                 properties.put(MA_NAME, ma);
             }
-
-            properties.put(SchedulerConstants.SCHEDULER_NAME_KEY, schedulerName);
-            properties.put(SchedulerConstants.SCHEDULER_DESCRIPTION_KEY, description);
-            properties.put(SchedulerConstants.SCHEDULER_RECIPE, "2000");
+            
+            properties.put("taskName", schedulerName);
+            properties.put(DESCRIPTION, description);
+            properties.put(REPEAT_FOREVER, true);
+            properties.put(REPEAT_INTERVAL_PERIOD, "millisecond");
+            properties.put(REPEAT_INTERVAL_VALUE, syncInterval);
+            
             Component sync = m_manager.createComponent()
-                .setInterface(Runnable.class.getName(), properties)
+                .setInterface(Job.class.getName(),  properties)
                 .setImplementation(new LogSyncTask(name))
                 .add(m_manager.createServiceDependency().setService(ConnectionFactory.class).setRequired(true))
                 .add(m_manager.createServiceDependency().setService(LogStore.class, filterString).setRequired(true))
diff --git a/org.apache.ace.log/src/org/apache/ace/log/target/task/LogSyncTask.java b/org.apache.ace.log/src/org/apache/ace/log/target/task/LogSyncTask.java
index 2ad18af..cce1166 100644
--- a/org.apache.ace.log/src/org/apache/ace/log/target/task/LogSyncTask.java
+++ b/org.apache.ace.log/src/org/apache/ace/log/target/task/LogSyncTask.java
@@ -30,6 +30,7 @@
 import java.net.URLConnection;
 import java.util.List;
 
+import org.amdatu.scheduling.Job;
 import org.apache.ace.connectionfactory.ConnectionFactory;
 import org.apache.ace.discovery.Discovery;
 import org.apache.ace.feedback.Descriptor;
@@ -42,7 +43,7 @@
 
 // TODO there are two versions of this class around, the other ohne being the server.LogSyncTask,
 // and both are fairly similar
-public class LogSyncTask implements Runnable {
+public class LogSyncTask implements Job {
 
     private static final String COMMAND_QUERY = "query";
     private static final String COMMAND_SEND = "send";
@@ -65,7 +66,8 @@
     /**
      * Synchronize the log events available remote with the events available locally.
      */
-    public void run() {
+    @Override
+    public void execute() {
         URL host = m_discovery.discover();
 
         if (host == null) {
diff --git a/org.apache.ace.repository/bnd.bnd b/org.apache.ace.repository/bnd.bnd
index 4504c91..8f61382 100644
--- a/org.apache.ace.repository/bnd.bnd
+++ b/org.apache.ace.repository/bnd.bnd
@@ -8,10 +8,10 @@
 	org.apache.felix.dependencymanager,\
 	org.apache.felix.http.api,\
 	org.apache.felix.http.servlet-api,\
+	org.amdatu.scheduling.api,\
 	org.apache.ace.range.api;version=latest,\
 	org.apache.ace.test;version=latest,\
 	org.apache.ace.authentication.api;version=latest,\
-	org.apache.ace.scheduler.api;version=latest,\
 	org.apache.ace.discovery.api;version=latest,\
 	org.apache.ace.connectionfactory;version=latest,\
 	org.apache.ace.http.api;version=latest
diff --git a/org.apache.ace.repository/src/org/apache/ace/repository/task/Activator.java b/org.apache.ace.repository/src/org/apache/ace/repository/task/Activator.java
index 84c11fa..e6cc4fa 100644
--- a/org.apache.ace.repository/src/org/apache/ace/repository/task/Activator.java
+++ b/org.apache.ace.repository/src/org/apache/ace/repository/task/Activator.java
@@ -20,25 +20,28 @@
 
 import java.util.Properties;
 
+import org.amdatu.scheduling.Job;
 import org.apache.ace.connectionfactory.ConnectionFactory;
 import org.apache.ace.discovery.Discovery;
 import org.apache.ace.repository.RepositoryReplication;
-import org.apache.ace.scheduler.constants.SchedulerConstants;
 import org.apache.felix.dm.DependencyActivatorBase;
 import org.apache.felix.dm.DependencyManager;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ManagedService;
 import org.osgi.service.log.LogService;
 
 public class Activator extends DependencyActivatorBase {
 
+    private static final String PID = "org.apache.ace.repository.replication";
+    
     @Override
     public void init(BundleContext context, DependencyManager manager) throws Exception {
         Properties props = new Properties();
-        props.put(SchedulerConstants.SCHEDULER_NAME_KEY, RepositoryReplicationTask.class.getName());
-        props.put(SchedulerConstants.SCHEDULER_DESCRIPTION_KEY, "Synchronizes repositories.");
+        props.put(Constants.SERVICE_PID, PID);
 
         manager.add(createComponent()
-            .setInterface(Runnable.class.getName(), props)
+            .setInterface(new String[] {ManagedService.class.getName(), Job.class.getName()}, props)
             .setImplementation(RepositoryReplicationTask.class)
             .add(createServiceDependency().setService(Discovery.class).setRequired(true))
             .add(createServiceDependency().setService(ConnectionFactory.class).setRequired(true))
@@ -47,7 +50,4 @@
             );
     }
 
-    @Override
-    public void destroy(BundleContext context, DependencyManager manager) throws Exception {
-    }
 }
\ No newline at end of file
diff --git a/org.apache.ace.repository/src/org/apache/ace/repository/task/RepositoryReplicationTask.java b/org.apache.ace.repository/src/org/apache/ace/repository/task/RepositoryReplicationTask.java
index 49e010e..4631a18 100644
--- a/org.apache.ace.repository/src/org/apache/ace/repository/task/RepositoryReplicationTask.java
+++ b/org.apache.ace.repository/src/org/apache/ace/repository/task/RepositoryReplicationTask.java
@@ -24,6 +24,7 @@
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -32,12 +33,17 @@
 
 import javax.servlet.http.HttpServletResponse;
 
+import org.amdatu.scheduling.Job;
+import org.amdatu.scheduling.constants.Constants;
 import org.apache.ace.connectionfactory.ConnectionFactory;
 import org.apache.ace.discovery.Discovery;
 import org.apache.ace.range.RangeIterator;
 import org.apache.ace.range.SortedRangeSet;
 import org.apache.ace.repository.RepositoryReplication;
+import org.apache.felix.dm.Component;
 import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
 import org.osgi.service.log.LogService;
 
 /**
@@ -45,9 +51,12 @@
  * repositories are configured and tries to synchronize them with remote copies. Only pulls stuff in, it does not push
  * stuff out.
  */
-public class RepositoryReplicationTask implements Runnable {
-    private final ConcurrentMap<ServiceReference<RepositoryReplication>, RepositoryReplication> m_replicators = new ConcurrentHashMap<>();
+public class RepositoryReplicationTask implements Job, ManagedService {
+    private static final String KEY_SYNC_INTERVAL = "syncInterval";
 
+    private final ConcurrentMap<ServiceReference<RepositoryReplication>, RepositoryReplication> m_replicators = new ConcurrentHashMap<>();
+    
+    private volatile Component m_component;
     private volatile Discovery m_discovery;
     private volatile ConnectionFactory m_connectionFactory;
     private volatile LogService m_log;
@@ -73,7 +82,8 @@
     /**
      * Replicates all current known repositories.
      */
-    public void run() {
+    @Override
+    public void execute() {
         // Take a snapshot of the current available replicators...
         Map<ServiceReference<RepositoryReplication>, RepositoryReplication> replicators = new HashMap<>(m_replicators);
 
@@ -185,4 +195,30 @@
             conn.disconnect();
         }
     }
+    
+    @Override
+    public void updated(Dictionary<String, ?> properties) throws ConfigurationException {
+        Long interval = null;
+        if (properties != null) {
+            Object value = properties.get(KEY_SYNC_INTERVAL);
+            if (value != null) {
+                try {
+                    interval = Long.valueOf(value.toString());
+                }catch (NumberFormatException e) {
+                    throw new ConfigurationException("interval", "Interval must be a valid Long value", e);
+                }
+            } else {
+                throw new ConfigurationException("interval", "Interval is required");
+            }
+            
+            Dictionary<Object,Object> serviceProps = m_component.getServiceProperties();
+            
+            serviceProps.put(Constants.REPEAT_FOREVER, true);
+            serviceProps.put(Constants.REPEAT_INTERVAL_PERIOD, "millisecond");
+            serviceProps.put(Constants.REPEAT_INTERVAL_VALUE, interval);
+            
+            m_component.setServiceProperties(serviceProps);
+        }
+
+    }
 }
diff --git a/org.apache.ace.scheduler/.classpath b/org.apache.ace.scheduler/.classpath
deleted file mode 100644
index 2458f8c..0000000
--- a/org.apache.ace.scheduler/.classpath
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="src" output="bin_test" path="test"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
-	<classpathentry kind="con" path="aQute.bnd.classpath.container"/>
-	<classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/org.apache.ace.scheduler/.gitignore b/org.apache.ace.scheduler/.gitignore
deleted file mode 100644
index 90dde36..0000000
--- a/org.apache.ace.scheduler/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-/bin/
-/bin_test/
-/generated/
diff --git a/org.apache.ace.scheduler/.project b/org.apache.ace.scheduler/.project
deleted file mode 100644
index 409c29a..0000000
--- a/org.apache.ace.scheduler/.project
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>org.apache.ace.scheduler</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>bndtools.core.bndbuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-		<nature>bndtools.core.bndnature</nature>
-	</natures>
-</projectDescription>
diff --git a/org.apache.ace.scheduler/.settings/org.eclipse.jdt.core.prefs b/org.apache.ace.scheduler/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644
index a698e59..0000000
--- a/org.apache.ace.scheduler/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,12 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
-org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.8
-org.eclipse.jdt.core.compiler.debug.lineNumber=generate
-org.eclipse.jdt.core.compiler.debug.localVariable=generate
-org.eclipse.jdt.core.compiler.debug.sourceFile=generate
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.source=1.8
diff --git a/org.apache.ace.scheduler/api.bnd b/org.apache.ace.scheduler/api.bnd
deleted file mode 100644
index a9e298f..0000000
--- a/org.apache.ace.scheduler/api.bnd
+++ /dev/null
@@ -1,6 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under the terms of ASLv2 (http://www.apache.org/licenses/LICENSE-2.0).
-
-Export-Package: org.apache.ace.scheduler.constants
-Bundle-Version: 1.0.2
-Bundle-Name: Apache ACE Scheduler API
-Bundle-Description: Provides the Apache ACE Scheduler API packages
\ No newline at end of file
diff --git a/org.apache.ace.scheduler/bnd.bnd b/org.apache.ace.scheduler/bnd.bnd
deleted file mode 100644
index b6ac3f8..0000000
--- a/org.apache.ace.scheduler/bnd.bnd
+++ /dev/null
@@ -1,10 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under the terms of ASLv2 (http://www.apache.org/licenses/LICENSE-2.0).
-
--buildpath: \
-	${^-buildpath},\
-	${testng},\
-	osgi.core;version=6.0.0,\
-	osgi.cmpn,\
-	org.apache.felix.dependencymanager,\
-	org.apache.ace.test;version=latest
--sub: *.bnd
diff --git a/org.apache.ace.scheduler/impl.bnd b/org.apache.ace.scheduler/impl.bnd
deleted file mode 100644
index 5888370..0000000
--- a/org.apache.ace.scheduler/impl.bnd
+++ /dev/null
@@ -1,7 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under the terms of ASLv2 (http://www.apache.org/licenses/LICENSE-2.0).
-
-Bundle-Activator: org.apache.ace.scheduler.Activator
-Private-Package: org.apache.ace.scheduler
-Bundle-Version: 1.0.2
-Bundle-Name: Apache ACE Scheduler Service
-Bundle-Description: Registers a component that schedules runnable services
\ No newline at end of file
diff --git a/org.apache.ace.scheduler/src/org/apache/ace/scheduler/Activator.java b/org.apache.ace.scheduler/src/org/apache/ace/scheduler/Activator.java
deleted file mode 100644
index df40b30..0000000
--- a/org.apache.ace.scheduler/src/org/apache/ace/scheduler/Activator.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.ace.scheduler;
-
-import java.util.Properties;
-
-import org.apache.ace.scheduler.constants.SchedulerConstants;
-import org.apache.felix.dm.DependencyActivatorBase;
-import org.apache.felix.dm.DependencyManager;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.cm.ConfigurationException;
-import org.osgi.service.cm.ManagedService;
-import org.osgi.service.log.LogService;
-
-/**
- * Activator for the scheduler service. This activator will monitor <code>Runnable</code>s coming available, and if they
- * are intended to be scheduled, gets the necessary information and passes that to the scheduler.
- */
-public class Activator extends DependencyActivatorBase {
-    private Scheduler m_scheduler;
-
-    public void init(BundleContext context, DependencyManager manager) throws Exception {
-        m_scheduler = new Scheduler();
-        Properties props = new Properties();
-        props.put(Constants.SERVICE_PID, SchedulerConstants.SCHEDULER_PID);
-        manager.add(createComponent()
-            .setInterface(ManagedService.class.getName(), props)
-            .setImplementation(m_scheduler)
-            .add(createServiceDependency()
-                .setService(LogService.class).setRequired(false))
-            .add(createServiceDependency()
-                .setService(Runnable.class).setRequired(false)
-                .setAutoConfig(false)
-                .setCallbacks(this, "addRunnable", "addRunnable", "removeRunnable")));
-    }
-
-    public void destroy(BundleContext context, DependencyManager manager) throws Exception {
-        // do nothing
-    }
-
-    /**
-     * Handler for both adding and updating runnable service registrations.
-     * 
-     * @throws ConfigurationException
-     *             Is thrown when the <code>SCHEDULER_RECIPE</code> contained in <code>ref</code>'s service dictionary
-     *             cannot be parsed by the scheduler.
-     */
-    public void addRunnable(ServiceReference<Runnable> ref, Runnable task) throws ConfigurationException {
-        String name = (String) ref.getProperty(SchedulerConstants.SCHEDULER_NAME_KEY);
-        if (name != null) {
-            String description = (String) ref.getProperty(SchedulerConstants.SCHEDULER_DESCRIPTION_KEY);
-            Object recipe = ref.getProperty(SchedulerConstants.SCHEDULER_RECIPE);
-            boolean recipeOverride = Boolean.valueOf((String) ref.getProperty(SchedulerConstants.SCHEDULER_RECIPE_OVERRIDE)).booleanValue();
-            m_scheduler.addRunnable(name, task, description, recipe, recipeOverride);
-        }
-    }
-
-    public synchronized void removeRunnable(ServiceReference<Runnable> ref, Runnable task) {
-        String name = (String) ref.getProperty(SchedulerConstants.SCHEDULER_NAME_KEY);
-        if (name != null) {
-            m_scheduler.removeRunnable(name);
-        }
-    }
-}
diff --git a/org.apache.ace.scheduler/src/org/apache/ace/scheduler/Executer.java b/org.apache.ace.scheduler/src/org/apache/ace/scheduler/Executer.java
deleted file mode 100644
index 9b69faa..0000000
--- a/org.apache.ace.scheduler/src/org/apache/ace/scheduler/Executer.java
+++ /dev/null
@@ -1,102 +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.ace.scheduler;
-
-import java.util.Timer;
-import java.util.TimerTask;
-
-/**
- * This class wraps a <code>Runnable</code> in a <code>TimerTask</code> that allows
- * it to be periodically run and to be stopped as soon as possible.
- */
-public class Executer extends TimerTask {
-    private final Timer m_timer = new Timer();
-    private final Runnable m_task;
-    private boolean m_stop = false;
-    private boolean m_stopped = true;
-
-    /**
-     * Creates a new instance of this class.
-     *
-     * @param task The task that should be periodically run.
-     */
-    public Executer(Runnable task) {
-        m_task = task;
-    }
-
-    /**
-     * Start executing the task repeatedly with an interval as specified.
-     *
-     * @param interval The interval between executions of the task, in milliseconds.
-     */
-    void start(long interval) {
-        if (interval > 0) {
-            m_timer.schedule(this, 0, interval);
-        }
-    }
-
-    /**
-     * Stop periodically executing this task. If the task is currently executing it
-     * will never be run again after the current execution, otherwise it will simply
-     * never run (again).
-     */
-    void stop() {
-        synchronized (m_timer) {
-            if (!m_stop) {
-                m_stop = true;
-                cancel();
-                m_timer.cancel();
-            }
-
-            boolean interrupted = false;
-            while (!m_stopped) {
-                try {
-                    m_timer.wait();
-                }
-                catch (InterruptedException e) {
-                    interrupted = true;
-                }
-            }
-            if (interrupted) {
-                Thread.currentThread().interrupt();
-            }
-        }
-    }
-
-    public void run() {
-        synchronized (m_timer) {
-            m_stopped = false;
-            if (m_stop) {
-                m_stopped = true;
-                m_timer.notifyAll();
-                return;
-            }
-        }
-        try {
-            m_task.run();
-        }
-        catch (Exception e) {
-            // TODO we should log this somehow
-        }
-        synchronized (m_timer) {
-            m_stopped = true;
-            m_timer.notifyAll();
-        }
-    }
-}
\ No newline at end of file
diff --git a/org.apache.ace.scheduler/src/org/apache/ace/scheduler/Scheduler.java b/org.apache.ace.scheduler/src/org/apache/ace/scheduler/Scheduler.java
deleted file mode 100644
index d67a365..0000000
--- a/org.apache.ace.scheduler/src/org/apache/ace/scheduler/Scheduler.java
+++ /dev/null
@@ -1,147 +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.ace.scheduler;
-
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.Iterator;
-import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import org.osgi.framework.Constants;
-import org.osgi.service.cm.ConfigurationException;
-import org.osgi.service.cm.ManagedService;
-import org.osgi.service.log.LogService;
-
-/**
- * The scheduler periodically runs tasks based on a scheduling recipe. Tasks can be added and removed using the
- * <code>addRunnable</code> and <code>removeRunnable</code> methods. Recipes are supplied using configuration properties
- * using the <code>updated</code> method, or are passed in the task's properties.<br>
- *
- * A task will be scheduled if both a <code>Runnable</code> and a <code>recipe</code> are available for it.
- */
-public class Scheduler implements ManagedService {
-    protected final ConcurrentMap<String, SchedulerTask> m_tasks = new ConcurrentHashMap<>();
-    private volatile LogService m_log;
-
-    /**
-     * Makes sure that all tasks are indeed stopped when the scheduler is stopped.
-     */
-    public void stop() {
-        for (SchedulerTask schedTask : m_tasks.values()) {
-            schedTask.stop();
-        }
-        m_tasks.clear();
-    }
-
-    /**
-     * Adds a new runnable to this scheduler. The runnable will be created if necessary, registered, and processed.
-     * 
-     * @param name
-     *            A name for this task.
-     * @param task
-     *            A runnable to run for this task.
-     * @param description
-     *            A description of the task.
-     * @param recipe
-     *            Optionally, a recipe for running this task.
-     * @param recipeOverride
-     *            Indicates whether or not the <code>recipe</code> passed in prevails over any recipe provided by the
-     *            <code>Scheduler</code>'s configuration.
-     * @throws ConfigurationException
-     *             When <code>recipe</code> is not <code>null</code>, and cannot be decoded into a recipe.
-     */
-    public void addRunnable(String name, Runnable task, String description, Object recipe, boolean recipeOverride) throws ConfigurationException {
-        SchedulerTask schedTask = m_tasks.get(name);
-        if (schedTask == null) {
-            schedTask = new SchedulerTask(name);
-            m_tasks.putIfAbsent(name, schedTask);
-        }
-        schedTask.updateTask(task, description, recipe, recipeOverride);
-        schedTask.process();
-    }
-
-    /**
-     * Removes a runnable from this scheduler.
-     * 
-     * @param name
-     *            The name of the runnable. If the name does not indicate a valid runnable, nothing is done.
-     */
-    public void removeRunnable(String name) {
-        SchedulerTask schedTask = m_tasks.get(name);
-        if (schedTask != null) {
-            try {
-                schedTask.updateTask(null, null, null, false);
-            }
-            catch (ConfigurationException e) {
-                // Will not occur; a null recipe will not cause an exception.
-            }
-            if (!schedTask.process()) {
-                m_tasks.remove(name);
-            }
-        }
-    }
-
-    /**
-     * Updates the configuration of the scheduler. The scheduler expects the configuration to contain recipes for
-     * scheduling. The key of a property should be the name identifying a task and the value should be a string
-     * describing the scheduling recipe for this task.
-     */
-    public void updated(Dictionary<String, ?> properties) throws ConfigurationException {
-        if (properties == null) {
-            return;
-        }
-
-        // first remove all the old schedules.
-        for (SchedulerTask schedTask : m_tasks.values()) {
-            schedTask.updateConfigurationRecipe(null);
-        }
-
-        // then apply the new ones
-        properties.remove(Constants.SERVICE_PID);
-
-        List<String> keys = Collections.list(properties.keys());
-        for (String name : keys) {
-            SchedulerTask schedTask = m_tasks.get(name);
-            if (schedTask == null) {
-                schedTask = new SchedulerTask(name);
-                m_tasks.putIfAbsent(name, schedTask);
-            }
-
-            try {
-                schedTask.updateConfigurationRecipe(properties.get(name));
-            }
-            catch (ConfigurationException ce) {
-                // This is most likely an illegal recipe, caused by an config property we don't understand.
-                // So, no problem.
-                m_log.log(LogService.LOG_INFO, name + "=" + properties.get(name) + " does not look like a valid schedule recipe.");
-            }
-        }
-
-        // and remove all tasks that now have no schedule or runnable
-        Iterator<String> i = m_tasks.keySet().iterator();
-        while (i.hasNext()) {
-            SchedulerTask schedTask = m_tasks.get(i.next());
-            if (!schedTask.process()) {
-                i.remove();
-            }
-        }
-    }
-}
diff --git a/org.apache.ace.scheduler/src/org/apache/ace/scheduler/SchedulerTask.java b/org.apache.ace.scheduler/src/org/apache/ace/scheduler/SchedulerTask.java
deleted file mode 100644
index 471f0bd..0000000
--- a/org.apache.ace.scheduler/src/org/apache/ace/scheduler/SchedulerTask.java
+++ /dev/null
@@ -1,186 +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.ace.scheduler;
-
-import org.osgi.service.cm.ConfigurationException;
-
-/**
- * Wrapper class for collecting a <code>Runnable</code> and its corresponding <code>recipe</code>(s).
- * Will schedule the task when both a schedule and a <code>Runnable</code> are available.<br>
- */
-public class SchedulerTask {
-    private final String m_name;
-    private Runnable m_task;
-    private String m_description;
-    private Object m_configurationRecipe;
-    private Object m_taskRecipe;
-    private boolean m_recipeOverride;
-    private Object m_currentRecipe;
-    private Executer m_executer;
-
-    /**
-     * Creates instance of this class.
-     * @param name The name of the runnable task.
-     */
-    SchedulerTask(String name) {
-        if (name == null) {
-            throw new IllegalArgumentException("A SchedulerTask's name cannot be null.");
-        }
-        m_name = name;
-    }
-
-    public String getName() {
-        return m_name;
-    }
-
-    public String getDescription() {
-        return m_description;
-    }
-
-    public Runnable getTask() {
-        return m_task;
-    }
-
-    /**
-     * Returns the currently most suited recipe, if any. This function not returning
-     * <code>null</code> does not mean that the task is scheduled (it may still be missing
-     * a <code>Runnable</code>).
-     */
-    public Object getCurrentRecipe() {
-        return m_currentRecipe;
-    }
-
-    /**
-     * Indicates whether this task is actually scheduled, and thus will run at some time
-     * in the future, unless the schedule is set to <code>null</code>, or the <code>Runnable</code>
-     * is removed.
-     */
-    public boolean isScheduled() {
-        return m_executer != null;
-    }
-
-    /**
-     * States a new set of properties for this task.
-     * @param task A runnable to run for this task.
-     * @param description A description of the task.
-     * @param taskRecipe Optionally, a recipe for running this task.
-     * @param recipeOverride Indicates whether or not the <code>recipe</code> passed in prevails over
-     * any recipe provided by the <code>Scheduler</code>'s configuration.
-     * @throws ConfigurationException When <code>recipe</code> is not <code>null</code>, and cannot
-     * be decoded into a recipe.
-     */
-    public void updateTask(Runnable task, String description, Object taskRecipe, boolean recipeOverride) throws ConfigurationException {
-        checkRecipe(taskRecipe);
-        m_task = task;
-        m_description = description;
-        m_taskRecipe = taskRecipe;
-        m_recipeOverride = recipeOverride;
-    }
-
-    /**
-     * States a new recipe as coming from a configuration.
-     * @param recipe Optionally, a recipe for running this task.
-     * @throws ConfigurationException When <code>recipe</code> is not <code>null</code>, and cannot
-     * be decoded into a recipe.
-     */
-    public void updateConfigurationRecipe(Object recipe) throws ConfigurationException {
-        checkRecipe(recipe);
-        m_configurationRecipe = recipe;
-    }
-
-    public void stop() {
-        if (m_executer != null) {
-            m_executer.stop();
-            m_executer = null;
-        }
-    }
-
-    public boolean process() {
-        Object recipe = findRecipe();
-        if ((recipe != null) && (m_task != null)) {
-            if (!recipe.equals(m_currentRecipe) && (m_executer != null)) {
-                m_executer.stop();
-                m_executer = null;
-            }
-            if (m_executer == null) {
-                m_executer = new Executer(m_task);
-                m_executer.start(parseScheduleRecipe(recipe));
-            }
-        }
-        else {
-            // there is nothing to do, since there is no recipe or task
-            stop();
-        }
-        m_currentRecipe = recipe;
-        return ((recipe != null) || (m_task != null));
-    }
-
-    /**
-     * Finds the most suitable recipe for the given task, using both the properties published
-     * with the task, and the scheduler service's properties.
-     * @return An <code>Object</code> representing the scheduler recipe, if any. If no suitable recipe can be found,
-     * <code>null</code> will be returned.
-     */
-    private Object findRecipe() {
-        if (m_recipeOverride) {
-            if (m_taskRecipe != null) {
-                return m_taskRecipe;
-            }
-            else if (m_configurationRecipe != null) {
-                return m_configurationRecipe;
-            }
-        }
-        else {
-            if (m_configurationRecipe != null) {
-                return m_configurationRecipe;
-            }
-            else if (m_taskRecipe != null) {
-                return m_taskRecipe;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Decodes an Object into a schedule.
-     * @param recipe An object representing a recipe.
-     * @return A decoded representation of the recipe.
-     */
-    private long parseScheduleRecipe(Object recipe) {
-        // For now assume just the number of milliseconds is in the string, we may want to do a
-        // more 'cron-like' scheduling in the future
-        return Long.valueOf(recipe.toString()).longValue();
-    }
-
-    /**
-     * Helper method that checks whether a recipe is valid.
-     * @throws ConfigurationException When <code>recipe</code> is not <code>null</code>, and cannot
-     * be decoded into a recipe.
-     */
-    private void checkRecipe(Object recipe) throws ConfigurationException {
-        if (recipe != null) {
-            try {
-                parseScheduleRecipe(recipe);
-            }
-            catch (NumberFormatException nfe) {
-                throw new ConfigurationException(m_name, "Could not parse scheduling recipe for task", nfe);
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/org.apache.ace.scheduler/src/org/apache/ace/scheduler/constants/SchedulerConstants.java b/org.apache.ace.scheduler/src/org/apache/ace/scheduler/constants/SchedulerConstants.java
deleted file mode 100644
index ec68020..0000000
--- a/org.apache.ace.scheduler/src/org/apache/ace/scheduler/constants/SchedulerConstants.java
+++ /dev/null
@@ -1,38 +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.ace.scheduler.constants;
-
-import aQute.bnd.annotation.ProviderType;
-
-/**
- * Compile time constants for the scheduler.
- */
-@ProviderType
-public interface SchedulerConstants {
-    /** Persistent ID for the scheduler, provided for configuration purposes. */
-    public static final String SCHEDULER_PID = "org.apache.ace.scheduler";
-    /** Name of the task that will be scheduled. */
-    public static final String SCHEDULER_NAME_KEY = "taskName";
-    /** Description of the task. */
-    public static final String SCHEDULER_DESCRIPTION_KEY = "description";
-    /** The recipe describing when the task should be scheduled. */
-    public static final String SCHEDULER_RECIPE = "recipe";
-    /** Determines if the recipe provided in the task overrides any recipe that is configured. */
-    public static final String SCHEDULER_RECIPE_OVERRIDE = "override";
-}
\ No newline at end of file
diff --git a/org.apache.ace.scheduler/src/org/apache/ace/scheduler/constants/packageinfo b/org.apache.ace.scheduler/src/org/apache/ace/scheduler/constants/packageinfo
deleted file mode 100644
index a4f1546..0000000
--- a/org.apache.ace.scheduler/src/org/apache/ace/scheduler/constants/packageinfo
+++ /dev/null
@@ -1 +0,0 @@
-version 1.0
\ No newline at end of file
diff --git a/org.apache.ace.scheduler/src/org/apache/ace/scheduler/packageinfo b/org.apache.ace.scheduler/src/org/apache/ace/scheduler/packageinfo
deleted file mode 100644
index a4f1546..0000000
--- a/org.apache.ace.scheduler/src/org/apache/ace/scheduler/packageinfo
+++ /dev/null
@@ -1 +0,0 @@
-version 1.0
\ No newline at end of file
diff --git a/org.apache.ace.scheduler/test/org/apache/ace/scheduler/ExecuterTest.java b/org.apache.ace.scheduler/test/org/apache/ace/scheduler/ExecuterTest.java
deleted file mode 100644
index 2afd4c2..0000000
--- a/org.apache.ace.scheduler/test/org/apache/ace/scheduler/ExecuterTest.java
+++ /dev/null
@@ -1,92 +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.ace.scheduler;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-public class ExecuterTest {
-
-    private Semaphore m_sem;
-
-    @BeforeMethod()
-    public void setup() {
-    }
-
-    /* start task, verify if it has run */
-    @Test()
-    public void testExecute() throws Exception {
-        m_sem = new Semaphore(1);
-        Executer executer = new Executer(new Runnable() {
-            public void run() {
-                m_sem.release();
-            }
-        });
-        executer.start(100);
-        m_sem.acquire();
-        assert m_sem.tryAcquire(2, TimeUnit.SECONDS);
-    }
-
-    /* start task, stop it, verify if it executed only once */
-    @Test()
-    public void testStop() throws Exception {
-        m_sem = new Semaphore(2);
-        Executer executer = new Executer(new Runnable() {
-            public void run() {
-                try {
-                    m_sem.tryAcquire(1, TimeUnit.SECONDS);
-                }
-                catch (InterruptedException e) {
-                    e.printStackTrace();
-                }
-            }
-        });
-        executer.start(10);
-        executer.stop();
-        Thread.sleep(100);
-        assert m_sem.tryAcquire(1, TimeUnit.SECONDS);
-    }
-
-    /*
-     * start task, which executes longer than the task interval specifies, causing multiple concurrent tasks to be
-     * started.
-     */
-    @Test()
-    public void testTooLongTask() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(5);
-
-        Executer executer = new Executer(new Runnable() {
-            public void run() {
-                try {
-                    Thread.sleep(20);
-                    latch.countDown();
-                }
-                catch (InterruptedException e) {
-                    e.printStackTrace();
-                }
-            }
-        });
-        executer.start(10);
-        assert latch.await(1, TimeUnit.SECONDS);
-    }
-}
diff --git a/org.apache.ace.scheduler/test/org/apache/ace/scheduler/SchedulerTest.java b/org.apache.ace.scheduler/test/org/apache/ace/scheduler/SchedulerTest.java
deleted file mode 100644
index 2cbde91..0000000
--- a/org.apache.ace.scheduler/test/org/apache/ace/scheduler/SchedulerTest.java
+++ /dev/null
@@ -1,155 +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.ace.scheduler;
-
-import java.util.Dictionary;
-import java.util.Hashtable;
-
-import org.apache.ace.test.utils.TestUtils;
-import org.osgi.service.log.LogService;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-public class SchedulerTest {
-
-    private Scheduler m_scheduler;
-
-    @BeforeMethod(alwaysRun = true)
-    protected void setUp() throws Exception {
-        m_scheduler = new Scheduler();
-        TestUtils.configureObject(m_scheduler, LogService.class);
-    }
-
-    @Test(expectedExceptions = IllegalArgumentException.class)
-    public synchronized void testIllegalCreation() {
-        new SchedulerTask(null);
-    }
-
-    @Test()
-    public synchronized void testUpdate() throws Exception {
-        Dictionary<String, Object> props = new Hashtable<>();
-
-        props.put("local.mock.task1", 1000l);
-        props.put("local.mock.task2", 2000l);
-        props.put("local.mock.task3", 3000l);
-        m_scheduler.updated(props);
-        assert m_scheduler.m_tasks.size() == props.size() : "Exactly three schedules should be known to the scheduler";
-        assert ((SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1")).getCurrentRecipe().equals(new Long(1000)) : "The schedule for mock task 1 should specify interval 1000, but it specifies "
-            + ((SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1")).getCurrentRecipe();
-
-        props.put("local.mock.task1", 4000l);
-        m_scheduler.updated(props);
-        assert ((SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1")).getCurrentRecipe().equals(new Long(4000)) : "The schedule for mock task 1 should specify interval 4000, but it specifies "
-            + ((SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1")).getCurrentRecipe();
-        assert !((SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1")).isScheduled() : "Since we have not provided a runnable for the scheduler, the tasks should not be scheduled.";
-    }
-
-    @Test()
-    public synchronized void testAdditionalProperties() throws Exception {
-        Dictionary<String, Object> props = new Hashtable<>();
-
-        props.put("local.mock.task1", "invalidValue");
-        m_scheduler.updated(props);
-        m_scheduler.addRunnable("local.mock.task1", new Runnable() {
-            public void run() {
-            }
-        }, "Dummy testing task", null, false);
-        // We should be able to get here, since unrecognized properties should be ignored.
-    }
-
-    @Test()
-    public synchronized void testAddTask() throws Exception {
-        assert m_scheduler.m_tasks.isEmpty();
-        m_scheduler.addRunnable("local.mock.task1", new Runnable() {
-            public void run() {
-            }
-        }, "Dummy testing task", null, false);
-        assert m_scheduler.m_tasks.size() == 1 : "Exactly one task should be known to the scheduler";
-        SchedulerTask task = (SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1");
-        assert "local.mock.task1".equals(task.getName()) : "Task that was just added has a different name than expected";
-    }
-
-    @Test()
-    public synchronized void testRemoveTask() throws Exception {
-        m_scheduler.addRunnable("local.mock.task1", new Runnable() {
-            public void run() {
-            }
-        }, "Dummy testing task", null, false);
-        m_scheduler.removeRunnable("nonExistent");
-        assert m_scheduler.m_tasks.size() == 1 : "Number of tasks known to the scheduler should still be one after removing a non-existing task";
-        m_scheduler.removeRunnable("local.mock.task1");
-        assert m_scheduler.m_tasks.isEmpty() : "Number of tasks known to the scheduler should be zero after removing the task we just added";
-    }
-
-    @Test()
-    public synchronized void testProcessTask() throws Exception {
-        Dictionary<String, Object> props = new Hashtable<>();
-
-        props.put("local.mock.task1", 1000);
-        m_scheduler.updated(props);
-
-        m_scheduler.addRunnable("local.mock.task1", new Runnable() {
-            public void run() {
-            }
-        }, "Dummy testing task", null, false);
-
-        assert ((SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1")).isScheduled() : "An executer should exist after adding a matching task and scheduling-recipe";
-    }
-
-    @Test()
-    public synchronized void testSchedulePrevailanceAndRemoval() throws Exception {
-        Dictionary<String, Object> props = new Hashtable<>();
-
-        props.put("local.mock.task1", 1000l);
-        m_scheduler.updated(props);
-
-        assert ((SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1")).getCurrentRecipe().equals(new Long(1000)) : "The schedule for mock task 1 should specify interval 1000, but it specifies "
-            + ((SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1")).getCurrentRecipe();
-        assert !((SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1")).isScheduled() : "Since we have not provided a runnable for the scheduler, the tasks should not be scheduled.";
-
-        m_scheduler.addRunnable("local.mock.task1", new Runnable() {
-            public void run() {
-            }
-        }, "Dummy testing task", 2000l, true);
-
-        assert ((SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1")).getCurrentRecipe().equals(new Long(2000)) : "The schedule for mock task 1 should specify interval 2000, but it specifies "
-            + ((SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1")).getCurrentRecipe();
-        assert ((SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1")).isScheduled() : "Since we have now provided a runnable for the scheduler, the tasks should be scheduled.";
-
-        m_scheduler.addRunnable("local.mock.task1", new Runnable() {
-            public void run() {
-            }
-        }, "Dummy testing task", 2000l, false);
-
-        assert ((SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1")).getCurrentRecipe().equals(new Long(1000)) : "The schedule for mock task 1 should specify interval 1000, but it specifies "
-            + ((SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1")).getCurrentRecipe();
-        assert ((SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1")).isScheduled() : "Since we have now provided a runnable for the scheduler, the tasks should be scheduled.";
-
-        props = new Hashtable<>();
-        m_scheduler.updated(props);
-
-        assert ((SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1")).getCurrentRecipe().equals(new Long(2000)) : "The schedule for mock task 1 should specify interval 2000, but it specifies "
-            + ((SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1")).getCurrentRecipe();
-        assert ((SchedulerTask) m_scheduler.m_tasks.get("local.mock.task1")).isScheduled() : "Since we have now provided a runnable for the scheduler, the tasks should be scheduled.";
-
-        m_scheduler.removeRunnable("local.mock.task1");
-
-        assert m_scheduler.m_tasks.size() == 0 : "We have now removed all information about mock task 1, so it should be gone now.";
-    }
-}
diff --git a/org.apache.ace.verifier/bnd.bnd b/org.apache.ace.verifier/bnd.bnd
index bbe0828..6f7bf33 100644
--- a/org.apache.ace.verifier/bnd.bnd
+++ b/org.apache.ace.verifier/bnd.bnd
@@ -14,7 +14,6 @@
 	org.apache.ace.connectionfactory;version=latest,\
 	org.apache.ace.repository.api;version=latest,\
 	org.apache.ace.repository.ext;version=latest,\
-	org.apache.ace.scheduler.api;version=latest,\
 	org.apache.ace.range.api;version=latest,\
 	org.apache.ace.client.repository.api;version=latest,\
 	org.apache.ace.test;version=latest,\
diff --git a/run-client/client.bndrun b/run-client/client.bndrun
index 3ac84a6..fdb8f74 100644
--- a/run-client/client.bndrun
+++ b/run-client/client.bndrun
@@ -16,6 +16,8 @@
 	org.apache.felix.http.servlet-api,\
 	org.apache.felix.http.jetty,\
 	org.apache.felix.dependencymanager.shell,\
+	org.amdatu.scheduling.api,\
+	org.amdatu.scheduling.quartz,\
 	com.vaadin,\
 	org.apache.ace.authentication.api;version=latest,\
 	org.apache.ace.authentication.impl;version=latest,\
@@ -41,7 +43,6 @@
 	org.apache.ace.range.api;version=latest,\
 	org.apache.ace.repository.api;version=latest,\
 	org.apache.ace.repository.impl;version=latest,\
-	org.apache.ace.scheduler.impl;version=latest,\
 	org.apache.ace.log.server.store.api;version=latest,\
 	org.apache.ace.log.server.store.file;version=latest,\
 	org.apache.ace.log.server.ui;version=latest,\
diff --git a/run-client/conf/org.apache.ace.log.server.task.factory/auditlog.cfg b/run-client/conf/org.apache.ace.log.server.task.factory/auditlog.cfg
index eda2448..ef60c52 100644
--- a/run-client/conf/org.apache.ace.log.server.task.factory/auditlog.cfg
+++ b/run-client/conf/org.apache.ace.log.server.task.factory/auditlog.cfg
@@ -2,3 +2,4 @@
 
 name=auditlog
 mode=pull
+syncInterval=2000
\ No newline at end of file
diff --git a/run-client/conf/org.apache.ace.scheduler.cfg b/run-client/conf/org.apache.ace.scheduler.cfg
deleted file mode 100644
index 7a8b073..0000000
--- a/run-client/conf/org.apache.ace.scheduler.cfg
+++ /dev/null
@@ -1,4 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under the terms of ASLv2 (http://www.apache.org/licenses/LICENSE-2.0).
-
-auditlog = 2000
-org.apache.ace.log.server.task.LogSyncTask = 2000
diff --git a/run-relay/conf/org.apache.ace.log.server.task.factory/auditlog.cfg b/run-relay/conf/org.apache.ace.log.server.task.factory/auditlog.cfg
index a7ace01..1d3eec4 100644
--- a/run-relay/conf/org.apache.ace.log.server.task.factory/auditlog.cfg
+++ b/run-relay/conf/org.apache.ace.log.server.task.factory/auditlog.cfg
@@ -4,3 +4,5 @@
 name = auditlog
 # how to synchronize, can be 'pull' (only receive updates), 'push' (only push updates) or 'pushpull' (2-way update)
 mode = pushpull
+# Synchronize the relay logs with the master every 5 seconds...
+syncInterval = 5000
diff --git a/run-relay/conf/org.apache.ace.repository.replication.cfg b/run-relay/conf/org.apache.ace.repository.replication.cfg
new file mode 100644
index 0000000..80a2af6
--- /dev/null
+++ b/run-relay/conf/org.apache.ace.repository.replication.cfg
@@ -0,0 +1,2 @@
+# Synchronize with the master repository every 5 seconds...  
+syncInterval = 5000
\ No newline at end of file
diff --git a/run-relay/conf/org.apache.ace.scheduler.cfg b/run-relay/conf/org.apache.ace.scheduler.cfg
deleted file mode 100644
index fb15c0e..0000000
--- a/run-relay/conf/org.apache.ace.scheduler.cfg
+++ /dev/null
@@ -1,7 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under the terms of ASLv2 (http://www.apache.org/licenses/LICENSE-2.0).
-
-auditlog = 2000
-# Synchronize the relay logs with the master every 5 seconds...
-org.apache.ace.log.server.task.LogSyncTask = 5000
-# Synchronize with the master repository every 5 seconds...  
-org.apache.ace.repository.task.RepositoryReplicationTask = 5000
diff --git a/run-relay/relay.bndrun b/run-relay/relay.bndrun
index b51a573..ec65a10 100644
--- a/run-relay/relay.bndrun
+++ b/run-relay/relay.bndrun
@@ -16,6 +16,8 @@
 	org.apache.felix.http.servlet-api,\
 	org.apache.felix.http.jetty,\
 	org.apache.felix.dependencymanager.shell,\
+	org.amdatu.scheduling.api,\
+	org.amdatu.scheduling.quartz,\
 	org.apache.ace.authentication.api;version=latest,\
 	org.apache.ace.authentication.impl;version=latest,\
 	org.apache.ace.authentication.processor.basicauth;version=latest,\
@@ -39,7 +41,6 @@
 	org.apache.ace.repository.impl;version=latest,\
 	org.apache.ace.repository.servlets;version=latest,\
 	org.apache.ace.repository.task;version=latest,\
-	org.apache.ace.scheduler.impl;version=latest,\
 	org.apache.ace.useradmin.repository;version=latest,\
 	org.apache.ace.http.context;version=latest
 
@@ -52,7 +53,7 @@
 	org.apache.felix.log.storeDebug=true,\
 	org.apache.felix.eventadmin.Timeout=0,\
 	org.apache.ace.server=localhost:8080,\
-	org.apache.ace.obr=localhost:8080,\
+	org.apache.ace.obr=localhost:8082,\
 	org.apache.ace.relay=localhost:8282,\
 	org.osgi.service.http.port=8282,\
 	org.apache.felix.log.maxSize=1000
diff --git a/run-server-allinone/conf/org.apache.ace.scheduler.cfg b/run-server-allinone/conf/org.apache.ace.scheduler.cfg
deleted file mode 100644
index 9a3cc1f..0000000
--- a/run-server-allinone/conf/org.apache.ace.scheduler.cfg
+++ /dev/null
@@ -1,3 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under the terms of ASLv2 (http://www.apache.org/licenses/LICENSE-2.0).
-
-auditlog = 2000
diff --git a/run-server/conf/org.apache.ace.scheduler.cfg b/run-server/conf/org.apache.ace.scheduler.cfg
deleted file mode 100644
index 9a3cc1f..0000000
--- a/run-server/conf/org.apache.ace.scheduler.cfg
+++ /dev/null
@@ -1,3 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under the terms of ASLv2 (http://www.apache.org/licenses/LICENSE-2.0).
-
-auditlog = 2000
diff --git a/run-server/server.bndrun b/run-server/server.bndrun
index dc6bc86..7e95d69 100644
--- a/run-server/server.bndrun
+++ b/run-server/server.bndrun
@@ -16,6 +16,8 @@
 	org.apache.felix.http.servlet-api,\
 	org.apache.felix.http.jetty,\
 	org.apache.felix.dependencymanager.shell,\
+	org.amdatu.scheduling.api,\
+	org.amdatu.scheduling.quartz,\
 	org.apache.ace.authentication.api;version=latest,\
 	org.apache.ace.authentication.impl;version=latest,\
 	org.apache.ace.authentication.processor.basicauth;version=latest,\
@@ -35,7 +37,6 @@
 	org.apache.ace.repository.api;version=latest,\
 	org.apache.ace.repository.impl;version=latest,\
 	org.apache.ace.repository.servlets;version=latest,\
-	org.apache.ace.scheduler.impl;version=latest,\
 	org.apache.ace.log.server.store.api;version=latest,\
 	org.apache.ace.log.server.store.file;version=latest,\
 	org.apache.ace.feedback.common;version=latest,\