Index: assemblies/features/standard/src/main/feature/feature.xml
===================================================================
--- assemblies/features/standard/src/main/feature/feature.xml	(revision 1499692)
+++ assemblies/features/standard/src/main/feature/feature.xml	(working copy)
@@ -157,6 +157,9 @@
 
     <feature name="scheduler" description="Provide a scheduler service in Karaf to fire events" version="${project.version}" resolver="(obr)">
         <bundle start-level="30">mvn:org.apache.karaf.scheduler/org.apache.karaf.scheduler.core/${project.version}</bundle>
+        <bundle start-level="30">mvn:org.apache.karaf.scheduler/org.apache.karaf.scheduler.command/${project.version}</bundle>
+        <bundle start-level="30">mvn:org.apache.sling/org.apache.sling.commons.threads/${sling.commons.threads.version}</bundle>
+        <bundle start-level="30">wrap:mvn:org.quartz-scheduler/quartz/${quartz.version}</bundle>
     </feature>
 
     <feature name="eventadmin" description="OSGi Event Admin service specification for event-based communication" version="${project.version}" resolver="(obr)">
Index: pom.xml
===================================================================
--- pom.xml	(revision 1499692)
+++ pom.xml	(working copy)
@@ -225,7 +225,9 @@
         <xbean.version>3.13</xbean.version>
         <xerces.version>2.11.0</xerces.version>
         <javax.mail.version>1.4.5</javax.mail.version>
-        <http.feature.version>[3,4)</http.feature.version>      
+        <http.feature.version>[3,4)</http.feature.version>
+        <sling.commons.threads.version>3.1.0</sling.commons.threads.version>
+        <quartz.version>2.2.0</quartz.version>
   
         <!-- Furter used maven plugin versions; e.g. in the docs -->
         <plugin.depends.version>1.2</plugin.depends.version>
@@ -669,6 +671,11 @@
                 <artifactId>org.apache.karaf.scheduler.core</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.apache.karaf.scheduler</groupId>
+                <artifactId>org.apache.karaf.scheduler.command</artifactId>
+                <version>${project.version}</version>
+            </dependency>
 
             <dependency>
                 <groupId>org.apache.karaf.scr</groupId>
@@ -1824,6 +1831,18 @@
                 <artifactId>commons-jexl</artifactId>
                 <version>${commons-jexl.version}</version>
             </dependency>
+
+            <dependency>
+                <groupId>org.apache.sling</groupId>
+                <artifactId>org.apache.sling.commons.threads</artifactId>
+                <version>${sling.commons.threads.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.quartz-scheduler</groupId>
+                <artifactId>quartz</artifactId>
+                <version>${quartz.version}</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 
Index: scheduler/command/NOTICE
===================================================================
--- scheduler/command/NOTICE	(revision 0)
+++ scheduler/command/NOTICE	(working copy)
@@ -0,0 +1,71 @@
+Apache Karaf
+Copyright 2010-2013 The Apache Software Foundation
+
+
+I. Included Software
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2010).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+OW2 (http://www.ow2.org/).
+Licensed under the BSD License.
+
+This product includes software developed at
+OPS4J (http://www.ops4j.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Eclipse Foundation (http://www.eclipse.org/).
+Licensed under the EPL.
+
+This product includes software written by
+Antony Lesuisse.
+Licensed under Public Domain.
+
+
+II. Used Software
+
+This product uses software developed at
+FUSE Source (http://www.fusesource.org/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+AOP Alliance (http://aopalliance.sourceforge.net/).
+Licensed under the Public Domain.
+
+This product uses software developed at
+Tanuki Software (http://www.tanukisoftware.com/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+Jasypt (http://jasypt.sourceforge.net/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+JLine (http://jline.sourceforge.net).
+Licensed under the BSD License.
+
+This product uses software developed at
+SLF4J (http://www.slf4j.org/).
+Licensed under the MIT License.
+
+This product uses software developed at
+SpringSource (http://www.springsource.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software from http://www.json.org.
+Copyright (c) 2002 JSON.org
+
+
+III. License Summary
+- Apache License 2.0
+- BSD License
+- EPL License
+- MIT License
Index: scheduler/command/pom.xml
===================================================================
--- scheduler/command/pom.xml	(revision 0)
+++ scheduler/command/pom.xml	(working copy)
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <!--
+
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements.  See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You under the Apache License, Version 2.0
+        (the "License"); you may not use this file except in compliance with
+        the License.  You may obtain a copy of the License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.karaf.scheduler</groupId>
+        <artifactId>scheduler</artifactId>
+        <version>3.0.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>org.apache.karaf.scheduler.command</artifactId>
+    <packaging>bundle</packaging>
+    <name>Apache Karaf :: Scheduler :: Commands</name>
+    <description>Provides shell commands to manipulate scheduler jobs.</description>
+
+    <properties>
+        <appendedResourcesDirectory>${basedir}/../../etc/appended-resources</appendedResourcesDirectory>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.table</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.console</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.scheduler</groupId>
+            <artifactId>org.apache.karaf.scheduler.core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.gogo.runtime</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-jdk14</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.quartz-scheduler</groupId>
+            <artifactId>quartz</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.utils</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>${project.basedir}/src/main/resources</directory>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>${project.basedir}/src/main/resources</directory>
+                <filtering>true</filtering>
+                <includes>
+                    <include>**/*.info</include>
+                </includes>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Export-Package>!*</Export-Package>
+                        <Private-Package>
+                            org.apache.karaf.scheduler.command*,
+                            org.apache.felix.utils.version;-split-package:=merge-first,
+                            org.apache.felix.utils.manifest;-split-package:=merge-first,
+                            !*
+                        </Private-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
Index: scheduler/command/src/main/java/org/apache/karaf/scheduler/command/ListJobs.java
===================================================================
--- scheduler/command/src/main/java/org/apache/karaf/scheduler/command/ListJobs.java	(revision 0)
+++ scheduler/command/src/main/java/org/apache/karaf/scheduler/command/ListJobs.java	(working copy)
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.scheduler.command;
+
+import java.util.List;
+
+import org.apache.karaf.scheduler.core.SchedulerService;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.apache.karaf.shell.table.ShellTable;
+import org.quartz.JobDetail;
+
+@Command(scope = "scheduler", name = "list", description = "Lists all registered jobs.")
+public class ListJobs extends OsgiCommandSupport {
+    
+    @Option(name= "--table", description = "Show registered jobs using a shell table")
+    boolean newLayout;
+
+    protected static final String HEADER_FORMAT = "%-75s %-25s";
+    protected static final String OUTPUT_FORMAT = "[%-73s] [%-23s]";
+    
+    protected static final String UNKNOWN = "---";
+    protected static final String SCHEDULER_KEY = "Key";
+    protected static final String SCHEDULER_DESC = "Description";
+    
+    private SchedulerService schedulerService;
+
+    public void setSchedulerService(SchedulerService schedulerService) {
+        this.schedulerService = schedulerService;
+    }
+
+    protected Object doExecute() throws Exception {
+    	List<JobDetail> jobs = schedulerService.getScheduledJobs();
+        if (jobs == null || jobs.size() == 0) {
+            System.out.println("There are no registered jobs.");
+            return null;
+        }
+
+        if (!newLayout) {
+	        // Print column headers.
+            System.out.println(String.format(HEADER_FORMAT, SCHEDULER_KEY, SCHEDULER_DESC));
+            
+        	for (JobDetail jobDetail : jobs) {
+        		System.out.println(String.format(OUTPUT_FORMAT, jobDetail.getKey(), jobDetail.getDescription() == null ? UNKNOWN : jobDetail.getDescription()));
+        	}
+        
+        } else {
+        
+	        ShellTable table = new ShellTable();
+	        table.column(SCHEDULER_KEY);
+	        table.column(SCHEDULER_DESC);
+
+			for (JobDetail jobDetail : jobs) {
+				table.addRow().addContent(jobDetail.getKey(), jobDetail.getDescription() == null ? UNKNOWN : jobDetail.getDescription());
+			}
+	        table.print(System.out);
+        }
+        return null;
+    }
+
+}
Index: scheduler/command/src/main/resources/OSGI-INF/blueprint/shell-scheduler.xml
===================================================================
--- scheduler/command/src/main/resources/OSGI-INF/blueprint/shell-scheduler.xml	(revision 0)
+++ scheduler/command/src/main/resources/OSGI-INF/blueprint/shell-scheduler.xml	(working copy)
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+    <reference id="schedulerService" interface="org.apache.karaf.scheduler.core.SchedulerService" />
+
+    <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+        <command>
+            <action class="org.apache.karaf.scheduler.command.ListJobs">
+                <property name="schedulerService" ref="schedulerService"/>
+            </action>
+        </command>
+    </command-bundle>
+    
+</blueprint>
Index: scheduler/command/src/main/resources/OSGI-INF/bundle.info
===================================================================
--- scheduler/command/src/main/resources/OSGI-INF/bundle.info	(revision 0)
+++ scheduler/command/src/main/resources/OSGI-INF/bundle.info	(working copy)
@@ -0,0 +1,19 @@
+h1. Synopsis
+
+${project.name}
+
+${project.description}
+
+Maven URL:
+   [mvn:${project.groupId}/${project.artifactId}/${project.version}]
+
+h1. Description
+
+This bundle provides shell commands to manipulate scheduler jobs.
+
+The following commands are available:
+* scheduler:list - List all registered jobs.
+
+h1. See also
+
+Commands and Using the console sections of the Karaf User Guide
Index: scheduler/core/pom.xml
===================================================================
--- scheduler/core/pom.xml	(revision 1499692)
+++ scheduler/core/pom.xml	(working copy)
@@ -10,7 +10,7 @@
         (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
+            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,
@@ -22,18 +22,48 @@
     <modelVersion>4.0.0</modelVersion>
 
     <parent>
+        <groupId>org.apache.karaf.scheduler</groupId>
         <artifactId>scheduler</artifactId>
-        <groupId>org.apache.karaf</groupId>
         <version>3.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 
-    <groupId>org.apache.karaf.scheduler</groupId>
     <artifactId>org.apache.karaf.scheduler.core</artifactId>
     <packaging>bundle</packaging>
     <name>Apache Karaf :: Scheduler :: Core</name>
-    <description>This bundle provides a Service Listener which listnes for Runnable Services and schedules their execution, based  on the service properties</description>
+    <description>Scheduler Services and API</description>
 
+    <properties>
+        <appendedResourcesDirectory>${basedir}/../../etc/appended-resources</appendedResourcesDirectory>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.utils</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.threads</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.quartz-scheduler</groupId>
+            <artifactId>quartz</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
     <build>
         <resources>
             <resource>
@@ -56,10 +86,27 @@
                 <artifactId>maven-bundle-plugin</artifactId>
                 <configuration>
                     <instructions>
-                        <Bundle-Activator>org.apache.karaf.scheduler.core.Activator</Bundle-Activator>
+                        <Export-Package>
+                            org.apache.karaf.scheduler.core,
+                            org.apache.sling.commons.scheduler
+                        </Export-Package>
                         <Import-Package>
+                            org.osgi.framework*,
+                            !javax.mail*,
+                            !javax.jms*,
+                            !javax.servlet*,
+                            !javax.transaction*,
+                            !javax.ejb*,
+                            !com.mchange.v2.c3p0*,
                             *
                         </Import-Package>
+                        <Private-Package>
+                            org.apache.karaf.bundle.core.internal,
+                            org.apache.sling.commons.scheduler.impl,
+                            org.apache.felix.utils.version,
+                            org.apache.felix.utils.manifest,
+                            org.apache.felix.utils.properties
+                        </Private-Package>
                     </instructions>
                 </configuration>
             </plugin>
@@ -66,17 +113,4 @@
         </plugins>
     </build>
 
-    <dependencies>
-        <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.core</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-            <scope>provided</scope>
-        </dependency>
-    </dependencies>
-
 </project>
\ No newline at end of file
Index: scheduler/core/src/main/java/org/apache/karaf/scheduler/core/Activator.java
===================================================================
--- scheduler/core/src/main/java/org/apache/karaf/scheduler/core/Activator.java	(revision 1499692)
+++ scheduler/core/src/main/java/org/apache/karaf/scheduler/core/Activator.java	(working copy)
@@ -1,49 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.karaf.scheduler.core;
-
-import org.osgi.framework.*;
-
-import java.util.Dictionary;
-import java.util.Properties;
-
-public class Activator implements BundleActivator {
-
-    private TaskScheduler scheduler = new TaskScheduler();
-    private RunnableServiceListener listener;
-
-    private static final String filter = "(&(objectclass=%s)(&(%s >= 0)(%s >= 0)))";
-
-    @Override
-    public void start(BundleContext context) throws Exception {
-        listener = new RunnableServiceListener(context, scheduler);
-
-        //register scheduler service
-        context.registerService(scheduler.getClass().getName(), scheduler, (Dictionary) new Properties());
-
-        //register service listener
-        context.addServiceListener(listener, String.format(filter, Runnable.class.getName(), KarafTimerTask.ID_PROPERTY, KarafTimerTask.PERIOD_PROPERTY));
-
-    }
-
-    @Override
-    public void stop(BundleContext context) throws Exception {
-
-    }
-
-}
Index: scheduler/core/src/main/java/org/apache/karaf/scheduler/core/Job.java
===================================================================
--- scheduler/core/src/main/java/org/apache/karaf/scheduler/core/Job.java	(revision 0)
+++ scheduler/core/src/main/java/org/apache/karaf/scheduler/core/Job.java	(working copy)
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.scheduler.core;
+
+import org.apache.sling.commons.scheduler.JobContext;
+
+/**
+ * A job is executed by the {@link SchedulerService} service.
+ * If the implementation of the job requires certain environment information
+ * it can implement this interface to get additional information
+ * through the provided {@link JobContext}.
+ * If no additional information is required, implementing {@link Runnable} is
+ * sufficient.
+ */
+public interface Job {
+
+    /**
+     * Execute this job.
+     * @param context The context of the job.
+     */
+    void execute(JobContext context);
+}
Index: scheduler/core/src/main/java/org/apache/karaf/scheduler/core/KarafTimerTask.java
===================================================================
--- scheduler/core/src/main/java/org/apache/karaf/scheduler/core/KarafTimerTask.java	(revision 1499692)
+++ scheduler/core/src/main/java/org/apache/karaf/scheduler/core/KarafTimerTask.java	(working copy)
@@ -1,82 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.karaf.scheduler.core;
-
-import java.util.TimerTask;
-
-public class KarafTimerTask<R extends Runnable> extends TimerTask {
-
-    public static final String ID_PROPERTY = "org.apache.karaf.scheduler.task.id";
-    public static final String PERIOD_PROPERTY = "org.apache.karaf.scheduler.task.period";
-
-    protected String id;
-    protected R task;
-    protected Long schedulePeriod = 0L;
-
-    /**
-     * Constructor
-     * @param id  the id of the task. Used for reference.
-     * @param task the task to be scheduled
-     */
-    public KarafTimerTask(String id, R task) {
-        this.id = id;
-        this.task = task;
-    }
-
-    /**
-     * Constructor
-     * @param id  the id of the task. Used for reference.
-     * @param task the task to be scheduled
-     * @param schedulePeriod the schedule period.
-     */
-    public KarafTimerTask(String id, R task, Long schedulePeriod) {
-        this.id = id;
-        this.task = task;
-        this.schedulePeriod = schedulePeriod;
-    }
-
-    @Override
-    public void run() {
-        task.run();
-    }
-
-    public String getId() {
-        return id;
-    }
-
-    public void setId(String id) {
-        this.id = id;
-    }
-
-    public R getTask() {
-        return task;
-    }
-
-    public void setTask(R task) {
-        this.task = task;
-    }
-
-    public Long getSchedulePeriod() {
-        return schedulePeriod;
-    }
-
-    public void setSchedulePeriod(Long schedulePeriod) {
-        this.schedulePeriod = schedulePeriod;
-    }
-
-}
Index: scheduler/core/src/main/java/org/apache/karaf/scheduler/core/RunnableServiceListener.java
===================================================================
--- scheduler/core/src/main/java/org/apache/karaf/scheduler/core/RunnableServiceListener.java	(revision 1499692)
+++ scheduler/core/src/main/java/org/apache/karaf/scheduler/core/RunnableServiceListener.java	(working copy)
@@ -1,85 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.karaf.scheduler.core;
-
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceEvent;
-import org.osgi.framework.ServiceListener;
-import org.osgi.framework.ServiceReference;
-
-public class RunnableServiceListener implements ServiceListener {
-
-    private final BundleContext bundleContext;
-    private final TaskScheduler scheduler;
-
-    /**
-     * Constructor
-     *
-     * @param bundleContext
-     * @param scheduler
-     */
-    public RunnableServiceListener(BundleContext bundleContext, TaskScheduler scheduler) {
-        this.bundleContext = bundleContext;
-        this.scheduler = scheduler;
-    }
-
-    @Override
-    public void serviceChanged(ServiceEvent event) {
-        switch (event.getType()) {
-            case ServiceEvent.REGISTERED:
-                scheduleRunnableService(event);
-                break;
-            case ServiceEvent.UNREGISTERING: {
-                unscheduleRunnableService(event);
-            }
-            break;
-            default:
-                break;
-        }
-    }
-
-    /**
-     * Schedules the execution of the Runnable Service of the {@link ServiceEvent}.
-     *
-     * @param event
-     */
-    protected void scheduleRunnableService(ServiceEvent event) {
-        ServiceReference reference = event.getServiceReference();
-        Runnable service = (Runnable) bundleContext.getService(reference);
-        String id = (String) reference.getProperty(KarafTimerTask.ID_PROPERTY);
-        String periodValue = (String) reference.getProperty(KarafTimerTask.PERIOD_PROPERTY);
-
-        if (periodValue != null) {
-            Long period = Long.parseLong(periodValue);
-            KarafTimerTask task = new KarafTimerTask(id, service, period);
-            scheduler.schedule(task);
-        }
-    }
-
-    /**
-     * Unschedules the execution of the Runnable Service of the {@link ServiceEvent}.
-     *
-     * @param event
-     */
-    protected void unscheduleRunnableService(ServiceEvent event) {
-        ServiceReference reference = event.getServiceReference();
-        String id = (String) reference.getProperty(KarafTimerTask.ID_PROPERTY);
-        scheduler.unschedule(id);
-    }
-
-}
Index: scheduler/core/src/main/java/org/apache/karaf/scheduler/core/SchedulerService.java
===================================================================
--- scheduler/core/src/main/java/org/apache/karaf/scheduler/core/SchedulerService.java	(revision 0)
+++ scheduler/core/src/main/java/org/apache/karaf/scheduler/core/SchedulerService.java	(working copy)
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.scheduler.core;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import org.quartz.JobDetail;
+
+/**
+ * A scheduler to schedule time/cron based jobs.
+ * A job is an object that is executed/fired by the scheduler. The object
+ * should either implement the {@link Job} interface or the {@link Runnable}
+ * interface.
+ */
+public interface SchedulerService {
+
+    /** Name of the configuration property to define the period for a job.
+     * The period is expressed in seconds.
+     * This property needs to be of type Long.
+     */
+    String PROPERTY_SCHEDULER_PERIOD = "scheduler.period";
+
+    /** Name of the configuration property to define if a periodically job
+     * should be scheduled immediate.
+     * Default is to not startup immediate, the job is started the first time
+     * after the period has expired.
+     * This property needs to be of type Boolean.
+     * @since 2.2.0 .*/
+    String PROPERTY_SCHEDULER_IMMEDIATE = "scheduler.immediate";
+
+    /** Name of the configuration property to define the cron expression for a job. */
+    String PROPERTY_SCHEDULER_EXPRESSION = "scheduler.expression";
+
+    /** Name of the configuration property to define if the job can be run concurrently. */
+    String PROPERTY_SCHEDULER_CONCURRENT = "scheduler.concurrent";
+
+    /** Name of the configuration property to define the job name. */
+    String PROPERTY_SCHEDULER_NAME = "scheduler.name";
+
+
+    /**
+     * Schedule a time based job.
+     * Note that if a job with the same name has already been added, the old job is cancelled and this new job replaces
+     * the old job.
+     *
+     * @param name The name of the job - or null. If no name is specified it can't be cancelled.
+     * @param job The job to execute (either {@link Job} or {@link Runnable}).
+     * @param config An optional configuration object - this configuration is only passed to the job the job implements {@link Job}.
+     * @param schedulingExpression The time specification using a scheduling expression.
+     * @param canRunConcurrently Whether this job can run even if previous scheduled runs are still running.
+     * @throws IllegalArgumentException If the scheduling expression can't be parsed or if the job has not the correct type.
+     * @throws Exception If the job can't be scheduled.
+     */
+    void addJob(String name, Object job, Map<String, Serializable> config, String schedulingExpression, boolean canRunConcurrently)
+    throws Exception;
+
+    /**
+     * Schedule a periodic job.
+     * The job is started the first time when the period has passed.
+     * Note that if a job with the same name has already been added, the old job is cancelled and this new job replaces
+     * the old job.
+     *
+     * @param name The name of the job - or null. If no name is specified it can't be cancelled.
+     * @param job The job to execute (either {@link Job} or {@link Runnable}).
+     * @param config An optional configuration object - this configuration is only passed to the job the job implements {@link Job}.
+     * @param period Every period seconds this job is started.
+     * @param canRunConcurrently Whether this job can run even if previous scheduled runs are still running.
+     * @throws IllegalArgumentException If the job has not the correct type.
+     * @throws Exception If the job can't be scheduled.
+     */
+    void addPeriodicJob(String name, Object job, Map<String, Serializable> config, long period, boolean canRunConcurrently)
+    throws Exception;
+
+    /**
+     * Schedule a periodic job.
+     * Note that if a job with the same name has already been added, the old job is cancelled and this new job replaces
+     * the old job.
+     *
+     * @param name The name of the job - or null. If no name is specified it can't be cancelled.
+     * @param job The job to execute (either {@link Job} or {@link Runnable}).
+     * @param config An optional configuration object - this configuration is only passed to the job the job implements {@link Job}.
+     * @param period Every period seconds this job is started.
+     * @param canRunConcurrently Whether this job can run even if previous scheduled runs are still running.
+     * @param startImmediate Whether to start the job immediately for the first time or wait for the period to expire.
+     * @throws IllegalArgumentException If the job has not the correct type.
+     * @throws Exception If the job can't be scheduled.
+     * @since 2.2
+     */
+    void addPeriodicJob(String name, Object job, Map<String, Serializable> config, long period, boolean canRunConcurrently,
+            boolean startImmediate)
+    throws Exception;
+
+    /**
+     * Fire a job immediately and only once.
+     *
+     * @param job The job to execute (either {@link Job} or {@link Runnable}).
+     * @param config An optional configuration object - this configuration is only passed to the job the job implements {@link Job}.
+     * @throws IllegalArgumentException If the job has not the correct type.
+     * @throws Exception If the job can't be scheduled.
+     */
+    void fireJob(Object job, Map<String, Serializable> config)
+    throws Exception;
+
+    /**
+     * Fire a job immediately more than once.
+     *
+     * @param job The job to execute (either {@link Job} or {@link Runnable}).
+     * @param config An optional configuration object - this configuration is only passed to the job the job implements {@link Job}.
+     * @param times The number of times this job should be started (must be higher than 1)
+     * @param period Every period seconds this job is started.
+     * @throws IllegalArgumentException If the job has not the correct type.
+     * @return true if the code could be added, false otherwise.
+     * @since 2.1
+     */
+    boolean fireJob(Object job, Map<String, Serializable> config, int times, long period);
+
+    /**
+     * Fire a job once at a specific date.
+     * Note that if a job with the same name has already been added, the old job is cancelled and this new job replaces
+     * the old job.
+     *
+     * @param name The name of the job - or null. If no name is specified it can't be cancelled.
+     * @param job The job to execute (either {@link Job} or {@link Runnable}).
+     * @param config An optional configuration object - this configuration is only passed to the job the job implements {@link Job}.
+     * @param date The date this job should be run.
+     * @throws IllegalArgumentException If the job has not the correct type.
+     * @throws Exception If the job can't be scheduled.
+     */
+    void fireJobAt(String name, Object job, Map<String, Serializable> config, Date date)
+    throws Exception;
+
+    /**
+     * Fire a job once at a specific date, several times with a given interval.
+     * Note that if a job with the same name has already been added, the old job is cancelled and this new job replaces
+     * the old job.
+     *
+     * @param name The name of the job - or null. If no name is specified it can't be cancelled.
+     * @param job The job to execute (either {@link Job} or {@link Runnable}).
+     * @param config An optional configuration object - this configuration is only passed to the job the job implements {@link Job}.
+     * @param date The date this job should be run.
+     * @param times The number of times this job should be started (must be higher than 1)
+     * @param period Every period seconds this job is started.
+     * @throws IllegalArgumentException If the job has not the correct type.
+     * @return true if the code could be added, false otherwise.
+     * @since 2.1
+     */
+    boolean fireJobAt(String name, Object job, Map<String, Serializable> config, Date date, int times, long period);
+
+    
+    /**
+     * Fire a job once at a specific date, several times with a given interval.
+     * Note that if a job with the same name has already been added, the old job is cancelled and this new job replaces
+     * the old job.
+     *
+     * @param name The name of the job - or null. If no name is specified it can't be cancelled.
+     * @param job The job to execute (either {@link Job} or {@link Runnable}).
+     * @param config An optional configuration object - this configuration is only passed to the job the job implements {@link Job}.
+     * @param date The date this job should be run.
+     * @param period Every period seconds this job is started.
+     * @throws IllegalArgumentException If the job has not the correct type.
+     * @return true if the code could be added, false otherwise.
+     * @since 2.1
+     */
+    boolean fireJobAt(String name, Object job, Map<String, Serializable> config, Date date, long period);
+    
+    /**
+     * Remove a scheduled job by name.
+     *
+     * @param name The name of the job.
+     * @throws NoSuchElementException If the job is not scheduled.
+     */
+    void removeJob(String name)
+    throws NoSuchElementException;
+
+    /**
+     * Get all currently scheduled jobs.
+     */
+    List<JobDetail> getScheduledJobs();
+}
Index: scheduler/core/src/main/java/org/apache/karaf/scheduler/core/TaskScheduler.java
===================================================================
--- scheduler/core/src/main/java/org/apache/karaf/scheduler/core/TaskScheduler.java	(revision 1499692)
+++ scheduler/core/src/main/java/org/apache/karaf/scheduler/core/TaskScheduler.java	(working copy)
@@ -1,54 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.karaf.scheduler.core;
-
-import java.util.Map;
-import java.util.Timer;
-import java.util.concurrent.ConcurrentHashMap;
-
-public class TaskScheduler {
-
-    private final Timer timer = new Timer();
-    private final Map<String,KarafTimerTask> tasks = new ConcurrentHashMap<String, KarafTimerTask>();
-
-    /**
-     * Schedule a {@link KarafTimerTask}
-     * @param task
-     */
-    public void schedule(KarafTimerTask task) {
-        if(task != null) {
-            tasks.put(task.getId(),task);
-            timer.schedule(task,0,task.getSchedulePeriod());
-        }
-
-    }
-
-    /**
-     * Unschedule a {@link KarafTimerTask}
-     * @param id The id of the task to unschedule.
-     */
-    public void unschedule(String id) {
-        if(id != null) {
-            KarafTimerTask task = tasks.remove(id);
-            if(task != null) {
-                task.cancel();
-            }
-        }
-    }
-
-}
Index: scheduler/core/src/main/java/org/apache/sling/commons/scheduler/JobContext.java
===================================================================
--- scheduler/core/src/main/java/org/apache/sling/commons/scheduler/JobContext.java	(revision 0)
+++ scheduler/core/src/main/java/org/apache/sling/commons/scheduler/JobContext.java	(working copy)
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.commons.scheduler;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * The context for a {@link Job}.
+ */
+public interface JobContext {
+
+    /**
+     * Get the name of the scheduled job.
+     * @return The name of the job.
+     */
+    String getName();
+
+    /**
+     * Get the configuration provided when the job was scheduled.
+     * @return A non-null map of values.
+     */
+    Map<String, Serializable> getConfiguration();
+}
Index: scheduler/core/src/main/java/org/apache/sling/commons/scheduler/impl/NonParallelQuartzJobExecutor.java
===================================================================
--- scheduler/core/src/main/java/org/apache/sling/commons/scheduler/impl/NonParallelQuartzJobExecutor.java	(revision 0)
+++ scheduler/core/src/main/java/org/apache/sling/commons/scheduler/impl/NonParallelQuartzJobExecutor.java	(working copy)
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.commons.scheduler.impl;
+
+import org.quartz.DisallowConcurrentExecution;
+
+
+/**
+ * This component is responsible to launch a {@link at.ac.ait.hbs.homer.core.scheduler.Job}
+ * or {@link Runnable} in a Quartz Scheduler but non concurrently.
+ *
+ */
+@DisallowConcurrentExecution
+public class NonParallelQuartzJobExecutor extends QuartzJobExecutor {
+
+    // nothing to code here
+}
Index: scheduler/core/src/main/java/org/apache/sling/commons/scheduler/impl/QuartzJobExecutor.java
===================================================================
--- scheduler/core/src/main/java/org/apache/sling/commons/scheduler/impl/QuartzJobExecutor.java	(revision 0)
+++ scheduler/core/src/main/java/org/apache/sling/commons/scheduler/impl/QuartzJobExecutor.java	(working copy)
@@ -0,0 +1,94 @@
+/*
+ * 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.sling.commons.scheduler.impl;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.apache.sling.commons.scheduler.JobContext;
+import org.quartz.Job;
+import org.quartz.JobDataMap;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+
+/**
+ * This component is resposible to launch a {@link at.ac.ait.hbs.homer.core.scheduler.Job}
+ * or {@link Runnable} in a Quartz Scheduler.
+ *
+ */
+public class QuartzJobExecutor implements Job {
+
+    /**
+     * @see org.quartz.Job#execute(org.quartz.JobExecutionContext)
+     */
+    public final void execute(final JobExecutionContext context) throws JobExecutionException {
+
+        final JobDataMap data = context.getJobDetail().getJobDataMap();
+
+        final Object job = data.get(QuartzScheduler.DATA_MAP_OBJECT);
+        final Logger logger = (Logger) data.get(QuartzScheduler.DATA_MAP_LOGGER);
+
+        try {
+            logger.debug("Executing job {} with name {}", job, data.get(QuartzScheduler.DATA_MAP_NAME));
+            if (job instanceof org.apache.karaf.scheduler.core.Job) {
+                @SuppressWarnings("unchecked")
+                final Map<String, Serializable> configuration = (Map<String, Serializable>) data.get(QuartzScheduler.DATA_MAP_CONFIGURATION);
+                final String name = (String) data.get(QuartzScheduler.DATA_MAP_NAME);
+
+                final JobContext jobCtx = new JobContextImpl(name, configuration);
+                ((org.apache.karaf.scheduler.core.Job) job).execute(jobCtx);
+            } else if (job instanceof Runnable) {
+                ((Runnable) job).run();
+            } else {
+                logger.error("Scheduled job {} is neither a job nor a runnable.", job);
+            }
+        } catch (final Throwable t) {
+            // if this is a quartz exception, rethrow it
+            if (t instanceof JobExecutionException) {
+                throw (JobExecutionException) t;
+            }
+            // there is nothing we can do here, so we just log
+            logger.error("Exception during job execution of " + job + " : " + t.getMessage(), t);
+        }
+    }
+
+    public static final class JobContextImpl implements JobContext {
+
+        private final Map<String, Serializable> configuration;
+        private final String name;
+
+        public JobContextImpl(final String name, final Map<String, Serializable> config) {
+            this.name = name;
+            this.configuration = config;
+        }
+
+        /**
+         * @see at.ac.ait.hbs.homer.core.scheduler.JobContext#getConfiguration()
+         */
+        public Map<String, Serializable> getConfiguration() {
+            return this.configuration;
+        }
+
+        /**
+         * @see at.ac.ait.hbs.homer.core.scheduler.JobContext#getName()
+         */
+        public String getName() {
+            return this.name;
+        }
+    }
+}
Index: scheduler/core/src/main/java/org/apache/sling/commons/scheduler/impl/QuartzScheduler.java
===================================================================
--- scheduler/core/src/main/java/org/apache/sling/commons/scheduler/impl/QuartzScheduler.java	(revision 0)
+++ scheduler/core/src/main/java/org/apache/sling/commons/scheduler/impl/QuartzScheduler.java	(working copy)
@@ -0,0 +1,818 @@
+/*
+ * 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.sling.commons.scheduler.impl;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.UUID;
+
+import org.apache.sling.commons.threads.ThreadPool;
+import org.apache.sling.commons.threads.ThreadPoolManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.quartz.CronScheduleBuilder;
+import org.quartz.JobBuilder;
+import org.quartz.JobDataMap;
+import org.quartz.JobDetail;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+
+import org.quartz.SimpleScheduleBuilder;
+import org.quartz.SimpleTrigger;
+import org.quartz.Trigger;
+import org.quartz.TriggerBuilder;
+import org.quartz.impl.DirectSchedulerFactory;
+import org.quartz.impl.matchers.GroupMatcher;
+import org.quartz.simpl.RAMJobStore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.karaf.scheduler.core.Job;
+import org.apache.karaf.scheduler.core.SchedulerService;
+
+/**
+ * The quartz based implementation of the scheduler.
+ *
+ */
+public class QuartzScheduler implements SchedulerService {
+
+	/** Default log. */
+    private final Logger logger = LoggerFactory.getLogger(QuartzScheduler.class);
+
+	protected static final String DEFAULT_QUARTZ_JOB_GROUP = "Sling";
+
+	protected static final String PREFIX = "Apache Sling Quartz Scheduler ";
+
+	protected static final String QUARTZ_SCHEDULER_NAME = "ApacheSling";
+
+	/** Map key for the job object */
+	static final String DATA_MAP_OBJECT = "QuartzJobScheduler.Object";
+
+	/** Map key for the job name */
+	static final String DATA_MAP_NAME = "QuartzJobScheduler.JobName";
+
+	/** Map key for the configuration. */
+	static final String DATA_MAP_CONFIGURATION = "QuartzJobScheduler.Configuration";
+
+	/** Map key for the logger. */
+	static final String DATA_MAP_LOGGER = "QuartzJobScheduler.Logger";
+
+	/** The quartz scheduler. */
+	private volatile Scheduler scheduler;
+
+	/** List of registrations while this service is not activated yet. */
+	private final List<Registration> registeredJobs = new ArrayList<Registration>();
+
+	/** The component context. */
+	private volatile BundleContext context;
+
+	private ThreadPoolManager threadPoolManager;
+
+	private ThreadPool threadPool;
+
+	private static final String PROPERTY_POOL_NAME = "Thread Pool Name";
+	
+	/**
+	 * Activate this component.
+	 * Start the scheduler.
+	 * @throws Exception
+	 */
+	public final void init() throws Exception {		
+		// start scheduler
+		this.scheduler = this.init(PROPERTY_POOL_NAME);
+
+		final Registration[] regs;
+		synchronized (this.registeredJobs) {
+			regs = this.registeredJobs.toArray(new Registration[this.registeredJobs.size()]);
+			this.registeredJobs.clear();
+		}
+		for (final Registration reg : regs) {
+			try {
+				this.register(reg.componentName, reg.reference);
+			} catch (Exception e) {
+				// we don't want that one malicious service brings down the scheduler, so we just log
+				// the exception and continue
+				logger.error("Exception during registering " + reg.componentName + " service " + reg.reference, e);
+			}
+		}
+	}
+
+	/**
+	 * Deactivate this component.
+	 * Stop the scheduler.
+	 */
+	public final void destroy() {
+		final Scheduler s = this.scheduler;
+		this.scheduler = null;
+		this.dispose(s);
+	}
+
+	/**
+	 * Initialize the quartz scheduler
+	 * @return Return the new scheduler instance.
+	 * @throws SchedulerException
+	 */
+	protected final Scheduler init(final String poolName) throws SchedulerException {
+
+		// SLING-2261 Prevent Quartz from checking for updates
+		System.setProperty("org.terracotta.quartz.skipUpdateCheck", Boolean.TRUE.toString());
+
+		final ThreadPoolManager tpm = this.threadPoolManager;
+		// sanity null check
+		if (tpm == null) {
+			throw new SchedulerException("Thread pool manager missing");
+		}
+
+		// create the pool
+		this.threadPool = tpm.get(poolName);
+		final QuartzThreadPool quartzPool = new QuartzThreadPool(this.threadPool);
+
+		final DirectSchedulerFactory factory = DirectSchedulerFactory.getInstance();
+		// unique run id
+		final String runID = new Date().toString().replace(' ', '_');
+		factory.createScheduler(QUARTZ_SCHEDULER_NAME, runID, quartzPool, new RAMJobStore());
+		// quartz does not provide a way to get the scheduler by name AND runID, so we have to iterate!
+		final Iterator<Scheduler> allSchedulersIter = factory.getAllSchedulers().iterator();
+		Scheduler s = null;
+		while (s == null && allSchedulersIter.hasNext()) {
+			final Scheduler current = allSchedulersIter.next();
+			if (QUARTZ_SCHEDULER_NAME.equals(current.getSchedulerName()) && runID.equals(current.getSchedulerInstanceId())) {
+				s = current;
+			}
+		}
+		if (s == null) {
+			throw new SchedulerException("Unable to find new scheduler with name " + QUARTZ_SCHEDULER_NAME + " and run ID " + runID);
+		}
+
+		s.start();
+		if (logger.isDebugEnabled()) {
+			logger.debug(PREFIX + "started.");
+		}
+		return s;
+	}
+
+	/**
+	 * Dispose the quartz scheduler
+	 * @param s The scheduler.
+	 */
+	protected final void dispose(final Scheduler s) {
+		if (s != null) {
+			try {
+				s.shutdown();
+			} catch (SchedulerException e) {
+				logger.debug("Exception during shutdown of scheduler.", e);
+			}
+			if (logger.isDebugEnabled()) {
+				logger.debug(PREFIX + "stopped.");
+			}
+		}
+		final ThreadPoolManager tpm = this.threadPoolManager;
+		if (tpm != null && this.threadPool != null) {
+			tpm.release(this.threadPool);
+		}
+		this.threadPool = null;
+	}
+
+	/**
+	 * Add a job to the scheduler
+	 *
+	 * @param name The name of the job to add (or null)
+	 * @param Tje jopb
+	 * @param trigger a Trigger
+	 * @param canRunConcurrently whether this job can be run concurrently
+	 *
+	 * @throws SchedulerException thrown in case of errors
+	 */
+	protected final void scheduleJob(final String name,
+							   final Object job,
+							   final Map<String, Serializable>    config,
+							   final Trigger trigger,
+							   final boolean canRunConcurrently)
+	throws SchedulerException {
+		// this method is also called from bind - as deactivate might have been
+		// called in the meantime, we just check
+		final Scheduler s = this.scheduler;
+		if (s == null) {
+			throw new IllegalStateException("Scheduler is not available anymore.");
+		}
+
+		// check if the supplied object is valid
+		this.checkJob(job);
+
+		// if there is already a job with the name, remove it first
+		if (name != null) {
+			try {
+				final JobDetail jobdetail = s.getJobDetail(JobKey.jobKey(name));
+				if (jobdetail != null) {
+					this.removeJob(name);
+				}
+			} catch (final SchedulerException ignored) {
+			}
+		}
+
+		final String jobName = this.getJobName(name);
+
+		// create the data map
+		final JobDataMap jobDataMap = this.initDataMap(jobName, job, config);
+
+		final JobDetail detail = this.createJobDetail(jobName, jobDataMap, canRunConcurrently);
+
+		logger.debug("Scheduling job {} with name {} and trigger {}", new Object[] {job, jobName, trigger});
+		s.scheduleJob(detail, trigger);
+	}
+
+	/**
+	 * Initialize the data map for the job executor.
+	 * @param jobName
+	 * @param job
+	 * @param config
+	 * @param concurent
+	 * @return
+	 */
+	protected final JobDataMap initDataMap(final String  jobName,
+									 final Object  job,
+									 final Map<String, Serializable> config) {
+		final JobDataMap jobDataMap = new JobDataMap();
+
+		jobDataMap.put(DATA_MAP_OBJECT, job);
+
+		jobDataMap.put(DATA_MAP_NAME, jobName);
+		jobDataMap.put(DATA_MAP_LOGGER, logger);
+		if (config != null) {
+			jobDataMap.put(DATA_MAP_CONFIGURATION, config);
+		}
+
+		return jobDataMap;
+	}
+
+	/**
+	 * Create the job detail.
+	 * @param name
+	 * @param jobDataMap
+	 * @return
+	 */
+	protected final JobDetail createJobDetail(final String name,
+										final JobDataMap jobDataMap,
+										final boolean concurrent) {
+		final JobDetail detail = JobBuilder.newJob((concurrent ? QuartzJobExecutor.class : NonParallelQuartzJobExecutor.class))
+				.withIdentity(name)
+				.usingJobData(jobDataMap)
+				.build();
+		return detail;
+	}
+
+	/**
+	 * Check the job object, either runnable or job is allowed
+	 */
+	protected final void checkJob(final Object job) throws IllegalArgumentException {
+		if (!(job instanceof Runnable) && !(job instanceof Job)) {
+			throw new IllegalArgumentException("Job object is neither an instance of " + Runnable.class.getName() + " nor " + Job.class.getName());
+		}
+	}
+
+	/**
+	 * @see SchedulerService.ac.ait.hbs.homer.core.scheduler.Scheduler#addJob(java.lang.String, java.lang.Object, java.util.Map, java.lang.String, boolean)
+	 */
+	public final void addJob(final String name,
+					   final Object job,
+					   final Map<String, Serializable>    config,
+					   final String schedulingExpression,
+					   final boolean canRunConcurrently)
+	throws SchedulerException {
+		final Trigger cronTrigger;
+		cronTrigger = TriggerBuilder.newTrigger()
+			.withIdentity(name)
+			.withSchedule(CronScheduleBuilder.cronSchedule(schedulingExpression))
+			.build();
+		this.scheduleJob(name, job, config, cronTrigger, canRunConcurrently);
+	}
+
+	/**
+	 * @see SchedulerService.ac.ait.hbs.homer.core.scheduler.Scheduler#addPeriodicJob(java.lang.String, java.lang.Object, java.util.Map, long, boolean)
+	 */
+	public final void addPeriodicJob(final String name,
+							   final Object job,
+							   final Map<String, Serializable> config,
+							   final long period,
+							   final boolean canRunConcurrently)
+	throws SchedulerException {
+		this.addPeriodicJob(name, job, config, period, canRunConcurrently, false);
+	}
+
+	/**
+	 * @see SchedulerService.ac.ait.hbs.homer.core.scheduler.Scheduler#addPeriodicJob(java.lang.String, java.lang.Object, java.util.Map, long, boolean, boolean)
+	 */
+	public final void addPeriodicJob(final String name,
+			final Object job,
+			final Map<String, Serializable> config,
+			final long period,
+			final boolean canRunConcurrently,
+			final boolean startImmediate)
+	throws SchedulerException {
+		final long ms = period * 1000;
+		final String jobName = this.getJobName(name);
+
+		final TriggerBuilder<SimpleTrigger> builder = TriggerBuilder.newTrigger()
+				.withIdentity(jobName)
+				.startAt(new Date(System.currentTimeMillis() + ms))
+				.withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInMilliseconds(ms));
+		final Trigger trigger;
+		if (startImmediate) {
+			trigger = builder.startNow().build();
+		} else {
+			trigger = builder.startAt(new Date(System.currentTimeMillis() + ms)).build();
+		}
+
+		this.scheduleJob(jobName, job, config, trigger, canRunConcurrently);
+	}
+
+	/**
+	 * @see SchedulerService.ac.ait.hbs.homer.core.scheduler.Scheduler#fireJob(java.lang.Object, java.util.Map)
+	 */
+	public final void fireJob(final Object job, final Map<String, Serializable> config)
+	throws SchedulerException {
+		this.checkJob(job);
+		final String jobName = job.getClass().getName();
+		final JobDataMap dataMap = this.initDataMap(jobName, job, config);
+
+		final JobDetail detail = this.createJobDetail(jobName, dataMap, true);
+
+		final Trigger trigger = TriggerBuilder.newTrigger()
+			.withIdentity(jobName)
+			.startNow()
+			.build();
+
+		this.scheduler.scheduleJob(detail, trigger);
+	}
+
+	/**
+	 * @see SchedulerService.ac.ait.hbs.homer.core.scheduler.Scheduler#fireJobAt(java.lang.String, java.lang.Object, java.util.Map, java.util.Date)
+	 */
+	public final void fireJobAt(final String name, final Object job, final Map<String, Serializable> config, final Date date)
+	throws SchedulerException {
+		final String jobName = this.getJobName(name);
+		final Trigger trigger = TriggerBuilder.newTrigger()
+		.withIdentity(jobName)
+		.startAt(date)
+		.build();
+		this.scheduleJob(jobName, job, config, trigger, true);
+	}
+
+	/**
+	 * @see SchedulerService.ac.ait.hbs.homer.core.scheduler.Scheduler#fireJob(java.lang.Object, java.util.Map, int, long)
+	 */
+	public final boolean fireJob(final Object job,
+						   final Map<String, Serializable> config,
+						   final int times,
+						   final long period) {
+		this.checkJob(job);
+		if (times < 2) {
+			throw new IllegalArgumentException("Times argument must be higher than 1");
+		}
+		final long ms = period * 1000;
+		final String jobName = job.getClass().getName();
+		final JobDataMap dataMap = this.initDataMap(jobName, job, config);
+
+		final JobDetail detail = this.createJobDetail(jobName, dataMap, true);
+
+		final Trigger trigger = TriggerBuilder.newTrigger()
+			.withIdentity(jobName)
+			.startNow()
+			.withSchedule(SimpleScheduleBuilder.simpleSchedule().withRepeatCount(times).withIntervalInMilliseconds(ms))
+			.build();
+
+		try {
+			this.scheduler.scheduleJob(detail, trigger);
+		} catch (final SchedulerException se) {
+			// ignore this and return false
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * @see SchedulerService.ac.ait.hbs.homer.core.scheduler.Scheduler#fireJobAt(java.lang.String, java.lang.Object, java.util.Map, java.util.Date, int, long)
+	 */
+	public final boolean fireJobAt(final String name,
+							 final Object job,
+							 final Map<String, Serializable> config,
+							 final Date date,
+							 final int times,
+							 final long period) {
+		if (times < 2) {
+			throw new IllegalArgumentException("Times argument must be higher than 1");
+		}
+		final String jobName = job.getClass().getName();
+		final long ms = period * 1000;
+
+		final Trigger trigger = TriggerBuilder.newTrigger()
+			.withIdentity(jobName)
+			.startAt(date)
+			.withSchedule(SimpleScheduleBuilder.simpleSchedule().withRepeatCount(times).withIntervalInMilliseconds(ms))
+			.build();
+
+		try {
+			this.scheduleJob(jobName, job, config, trigger, true);
+		} catch (final SchedulerException se) {
+			// ignore this and return false
+			return false;
+		}
+		return true;
+	}
+
+	
+	/**
+	 * @see SchedulerService.ac.ait.hbs.homer.core.scheduler.Scheduler#fireJobAt(java.lang.String, java.lang.Object, java.util.Map, java.util.Date, long)
+	 */
+	public final boolean fireJobAt(final String name,
+							 final Object job,
+							 final Map<String, Serializable> config,
+							 final Date date,
+							 final long period) {
+		
+		final String jobName = job.getClass().getName();
+		final long ms = period * 1000;
+
+		final Trigger trigger = TriggerBuilder.newTrigger()
+			.withIdentity(jobName)
+			.startAt(date)
+			.withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInMilliseconds(ms))
+			.build();
+
+		try {
+			this.scheduleJob(jobName, job, config, trigger, true);
+		} catch (final SchedulerException se) {
+			// ignore this and return false
+			return false;
+		}
+		return true;
+	}
+	
+	
+	/**
+	 * Helper method which gets always a job name.
+	 * @param name The job name or null
+	 * @return The input job name or a unique job name.
+	 */
+	private String getJobName(final String name) {
+		return name != null ? name : PREFIX + UUID.randomUUID();
+	}
+
+	/**
+	 * @see SchedulerService.ac.ait.hbs.homer.core.scheduler.Scheduler#removeJob(java.lang.String)
+	 */
+	public final void removeJob(final String name) throws NoSuchElementException {
+		// as this method might be called from unbind and during
+		// unbind a deactivate could happen, we check the scheduler first
+		final Scheduler s = this.scheduler;
+		if (s != null) {
+			try {
+				s.deleteJob(JobKey.jobKey(name));
+				logger.debug("Unscheduling job with name {}", name);
+			} catch (final SchedulerException se) {
+				throw new NoSuchElementException(se.getMessage());
+			}
+		}
+	}
+
+	/**
+	 * Create unique identifier
+	 * @param type
+	 * @param ref
+	 * @throws Exception
+	 */
+	private String getServiceIdentifier(final ServiceReference ref) {
+		String name = (String) ref.getProperty(SchedulerService.PROPERTY_SCHEDULER_NAME);
+		if (name == null) {
+			name = (String) ref.getProperty(Constants.SERVICE_PID);
+			if (name == null) {
+				name = "Registered Service";
+			}
+		}
+		// now append service id to create a unique identifer
+		name = name + "." + ref.getProperty(Constants.SERVICE_ID);
+		return name;
+	}
+
+	/**
+	 * Register a job or task
+	 * @param type The type (job or task)
+	 * @param ref The service reference
+	 */
+	private void register(final String type, final ServiceReference ref) {
+		// we called from bind, it might be that deactivate has been
+		// called in the meantime
+		if (context != null) {
+			final Object job = context.getService(ref);
+			if (job != null) {
+				this.checkJob(job);
+				try {
+					final String name = getServiceIdentifier(ref);
+					final Boolean concurrent = getBooleanProperty(SchedulerService.PROPERTY_SCHEDULER_CONCURRENT, ref);
+					final Boolean immediate = getBooleanProperty(SchedulerService.PROPERTY_SCHEDULER_IMMEDIATE, ref);
+					final String expression = getStringProperty(SchedulerService.PROPERTY_SCHEDULER_EXPRESSION, ref);
+					if (expression != null) {
+						this.addJob(name, job, null, expression, (concurrent != null ? concurrent : true));
+					} else {
+						final Long period = getLongProperty(SchedulerService.PROPERTY_SCHEDULER_PERIOD, ref);
+						if (period != null) {
+							if (period < 1) {
+								logger.debug("Ignoring service {} : scheduler period is less than 1.", ref);
+							} else {
+								this.addPeriodicJob(name, job, null, period, (concurrent != null ? concurrent : true), (immediate != null ? immediate : false));
+							}
+						} else {
+							logger.debug("Ignoring servce {} : no scheduling property found.", ref);
+						}
+					}
+				} catch (final IllegalStateException e) {
+					// this can happen if deactivate has been called or the scheduling expression is invalid
+					logger.warn("Ignoring servce " + ref + " : exception occurred during registering.", e);
+				} catch (final SchedulerException e) {
+					// this can happen if deactivate has been called or the scheduling expression is invalid
+					logger.warn("Ignoring servce " + ref + " : exception occurred during registering.", e);
+				}
+			}
+		}
+	}
+	
+	private Boolean getBooleanProperty(final String key, final ServiceReference ref) {
+		Object value = ref.getProperty(key);
+		if (value instanceof Boolean) {
+			return (Boolean) value;
+		} else if (value instanceof String) {
+			return Boolean.parseBoolean((String) value);
+		}
+		return null;
+	}
+	
+	private Long getLongProperty(final String key, final ServiceReference ref) {
+		Object value = ref.getProperty(key);
+		if (value instanceof Long) {
+			return (Long) value;
+		} else if (value instanceof String) {
+			return Long.parseLong((String) value);
+		}
+		return null;
+	}
+	
+	private String getStringProperty(final String key, final ServiceReference ref) {
+		Object value = ref.getProperty(key);
+		if (value instanceof String) {
+			return (String) value;
+		}
+		return null;
+	}
+
+	/**
+	 * Unregister a service.
+	 * @param ref The service reference.
+	 */
+	private void unregister(final ServiceReference ref) {
+		try {
+			final String name = getServiceIdentifier(ref);
+			this.removeJob(name);
+		} catch (NoSuchElementException nsee) {
+			// we ignore this
+		}
+	}
+
+	/**
+	 * Bind a new job.
+	 * @param ref
+	 * @throws Exception
+	 */
+	public final void bindJob(final ServiceReference ref) {
+		if (ref == null) {
+			return;
+		}
+		
+		try {
+			if (this.scheduler != null) {
+				this.register(Registration.JOB, ref);
+			} else {
+				synchronized (this.registeredJobs) {
+					this.registeredJobs.add(new Registration(ref, Registration.JOB));
+				}
+			}
+		} catch (Throwable ex) {
+			logger.error("Error while binding job", ex);
+		}
+	}
+
+	/**
+	 * Unbind a job.
+	 * @param ref
+	 */
+	public final void unbindJob(final ServiceReference ref) {
+		if (ref == null) {
+			return;
+		}
+		
+		try {
+			if (this.scheduler != null) {
+				this.unregister(ref);
+			} else {
+				synchronized (this.registeredJobs) {
+					this.registeredJobs.remove(new Registration(ref, Registration.JOB));
+				}
+			}
+		} catch (Throwable ex) {
+			logger.error("Error while unbinding job", ex);
+		}
+	}
+
+	/**
+	 * Bind a new task.
+	 * @param ref
+	 * @throws Exception
+	 */
+	public final void bindTask(final ServiceReference ref) {
+		if (ref == null) {
+			return;
+		}
+		
+		try {
+			if (this.scheduler != null) {
+				this.register(Registration.TASK, ref);
+			} else {
+				synchronized (this.registeredJobs) {
+					this.registeredJobs.add(new Registration(ref, Registration.TASK));
+				}
+			}
+		} catch (Throwable ex) {
+			logger.error("Error while binding task", ex);
+		}
+	}
+
+	/**
+	 * Unbind a task.
+	 * @param ref
+	 */
+	public final void unbindTask(final ServiceReference ref) {
+		if (ref == null) {
+			return;
+		}
+		
+		try {
+			if (this.scheduler != null) {
+				this.unregister(ref);
+			} else {
+				synchronized (this.registeredJobs) {
+					this.registeredJobs.remove(new Registration(ref, Registration.TASK));
+				}
+			}
+		} catch (Throwable ex) {
+			logger.error("Error while unbinding task", ex);
+		}
+	}
+	
+	public final List<JobDetail> getScheduledJobs() {
+		List<JobDetail> result = new ArrayList<JobDetail>();
+
+		try {
+	        List<String> jobGroupNames = scheduler.getJobGroupNames();
+	        for (String jobGroupName : jobGroupNames) {
+				try {
+					Set<JobKey> jobKeys = scheduler.getJobKeys(GroupMatcher.jobGroupEquals(jobGroupName));
+	
+					for (JobKey jobKey : jobKeys) {
+						result.add(scheduler.getJobDetail(jobKey));
+					}
+				} catch (SchedulerException ex) {
+					//
+				}
+	        }
+		} catch (SchedulerException ex) {
+			//
+		}
+        
+        return result;
+	}
+
+	protected final Scheduler getScheduler() {
+		return this.scheduler;
+	}
+	
+	public final void setThreadPoolManager(final ThreadPoolManager threadPoolManager) {
+		this.threadPoolManager = threadPoolManager;
+	}
+	
+	public final void setBundleContext(final BundleContext context) {
+		this.context = context;
+	}
+
+	/**
+	 * Helper class holding a registration if this service is not active yet.
+	 */
+	private static final class Registration {
+		public static final String JOB = "job";
+		public static final String TASK = "task";
+
+		private final ServiceReference reference;
+		private final String componentName;
+
+		public Registration(final ServiceReference r, final String name) {
+			this.reference = r;
+			this.componentName = name;
+		}
+
+		@Override
+		public boolean equals(final Object obj) {
+			if (!(obj instanceof Registration)) {
+				return false;
+			}
+			if (obj == this) {
+				return true;
+			}
+			return this.reference.equals(((Registration) obj).reference);
+		}
+
+		@Override
+		public int hashCode() {
+			return this.reference.hashCode();
+		}
+	}
+
+	public static final class QuartzThreadPool implements org.quartz.spi.ThreadPool {
+
+		/** Our executor thread pool */
+		private ThreadPool executor;
+
+		/**
+		 * Create a new wrapper implementation for Quartz.
+		 */
+		public QuartzThreadPool(final ThreadPool executor) {
+			this.executor = executor;
+		}
+
+		/* (non-Javadoc)
+		 * @see org.quartz.spi.QuartzThreadPool#getPoolSize()
+		 */
+		public int getPoolSize() {
+			return this.executor.getConfiguration().getMaxPoolSize();
+		}
+
+		/* (non-Javadoc)
+		 * @see org.quartz.spi.QuartzThreadPool#initialize()
+		 */
+		public void initialize() {
+			// nothing to do
+		}
+
+		public void setInstanceId(final String id) {
+			// we ignore this
+		}
+
+		public void setInstanceName(final String name) {
+			// we ignore this
+		}
+
+		/* (non-Javadoc)
+		 * @see org.quartz.spi.QuartzThreadPool#runInThread(java.lang.Runnable)
+		 */
+		public boolean runInThread(final Runnable job) {
+			this.executor.execute(job);
+
+			return true;
+		}
+
+		/**
+		 * @see org.quartz.spi.ThreadPool#blockForAvailableThreads()
+		 */
+		public int blockForAvailableThreads() {
+			return this.executor.getConfiguration().getMaxPoolSize() - this.executor.getConfiguration().getQueueSize();
+		}
+
+		/* (non-Javadoc)
+		 * @see org.quartz.spi.QuartzThreadPool#shutdown(boolean)
+		 */
+		public void shutdown(final boolean waitForJobsToComplete) {
+			// the pool is managed by the thread pool manager,
+			// so we can just return
+			this.executor = null;
+		}
+	}
+}
Index: scheduler/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml
===================================================================
--- scheduler/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml	(revision 0)
+++ scheduler/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml	(working copy)
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+           xmlns:bp="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+           xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
+           xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0"
+           default-activation="eager">
+	
+	<reference id="threadPoolManager" interface="org.apache.sling.commons.threads.ThreadPoolManager" availability="mandatory" />
+	
+	<bean id="quartzScheduler" class="org.apache.sling.commons.scheduler.impl.QuartzScheduler" init-method="init" destroy-method="destroy">
+		<property name="bundleContext" ref="blueprintBundleContext"/>
+		<property name="threadPoolManager" ref="threadPoolManager" />
+	</bean>
+    <reference-list interface="org.apache.karaf.scheduler.core.Job" availability="optional">
+        <reference-listener ref="quartzScheduler" bind-method="bindJob" unbind-method="unbindJob" />
+    </reference-list>
+    <reference-list interface="java.lang.Runnable" availability="optional">
+        <reference-listener ref="quartzScheduler" bind-method="bindTask" unbind-method="unbindTask" />
+    </reference-list>
+	<service ref="quartzScheduler">
+		<interfaces>
+			<value>org.apache.karaf.scheduler.core.SchedulerService</value>
+		</interfaces>
+		<service-properties>
+		</service-properties>
+	</service>
+
+</blueprint>
Index: scheduler/core/src/main/resources/OSGI-INF/bundle.info
===================================================================
--- scheduler/core/src/main/resources/OSGI-INF/bundle.info	(revision 1499692)
+++ scheduler/core/src/main/resources/OSGI-INF/bundle.info	(working copy)
@@ -12,9 +12,11 @@
 This bundle registers a service listener, which listens from service events related to java.lang.Runnable interface.
 Each service with such interface may be added the following properties:
 
-org.apache.karaf.scheduler.task.id
-org.apache.karaf.scheduler.task.period
+scheduler.expression
+scheduler.period
+scheduler.name (optional)
+scheduler.concurrent (default: true)
+scheduler.immediate (default: false)
 
-If both properties are found, then the Runnable is decorated by a TimerTask and scheduled for repeated execution using the period property.
+If the 'scheduler.expression' or 'scheduler.period' property are found, then the Runnable is scheduled for repeated execution.
 
-
Index: scheduler/pom.xml
===================================================================
--- scheduler/pom.xml	(revision 1499692)
+++ scheduler/pom.xml	(working copy)
@@ -10,7 +10,7 @@
         (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
+            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,
@@ -22,13 +22,13 @@
     <modelVersion>4.0.0</modelVersion>
 
     <parent>
+        <groupId>org.apache.karaf</groupId>
         <artifactId>karaf</artifactId>
-        <groupId>org.apache.karaf</groupId>
         <version>3.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 
-    <groupId>org.apache.karaf</groupId>
+    <groupId>org.apache.karaf.scheduler</groupId>
     <artifactId>scheduler</artifactId>
     <packaging>pom</packaging>
     <name>Apache Karaf :: Scheduler</name>
@@ -35,6 +35,7 @@
 
     <modules>
         <module>core</module>
+        <module>command</module>
     </modules>
 
 </project>
\ No newline at end of file
