ZEST-128 - Chasing race condition problems.

Project: http://git-wip-us.apache.org/repos/asf/zest-java/repo
Commit: http://git-wip-us.apache.org/repos/asf/zest-java/commit/73b7f196
Tree: http://git-wip-us.apache.org/repos/asf/zest-java/tree/73b7f196
Diff: http://git-wip-us.apache.org/repos/asf/zest-java/diff/73b7f196

Branch: refs/heads/develop
Commit: 73b7f196946f93be2805d4b5529924a5240baae9
Parents: 551f684
Author: Niclas Hedhman <[email protected]>
Authored: Sun Nov 15 19:05:36 2015 +0800
Committer: Niclas Hedhman <[email protected]>
Committed: Sun Nov 15 19:05:36 2015 +0800

----------------------------------------------------------------------
 .../association/AssociationAssignmentTest.java  |   6 +-
 .../zest/library/scheduler/CronSchedule.java    | 117 ++++++++
 .../zest/library/scheduler/Execution.java       | 231 ---------------
 .../zest/library/scheduler/OnceSchedule.java    |  68 +++++
 .../apache/zest/library/scheduler/Schedule.java | 131 +++++++++
 .../zest/library/scheduler/ScheduleFactory.java |  42 +++
 .../zest/library/scheduler/Scheduler.java       |  11 +-
 .../zest/library/scheduler/SchedulerMixin.java  | 187 -------------
 .../library/scheduler/SchedulerService.java     |   1 +
 .../library/scheduler/SchedulesHandler.java     |   2 +-
 .../org/apache/zest/library/scheduler/Task.java |   2 +-
 .../zest/library/scheduler/TaskRunner.java      | 115 --------
 .../scheduler/bootstrap/SchedulerAssembler.java |  10 +-
 .../defaults/DefaultScheduleFactoryMixin.java   |   8 +-
 .../defaults/DefaultThreadFactory.java          |   3 +-
 .../library/scheduler/internal/Execution.java   | 278 +++++++++++++++++++
 .../scheduler/internal/ScheduleTime.java        |  61 ++++
 .../scheduler/internal/SchedulerMixin.java      | 199 +++++++++++++
 .../library/scheduler/internal/Schedules.java   |  28 ++
 .../library/scheduler/internal/TaskRunner.java  | 115 ++++++++
 .../library/scheduler/schedule/Schedule.java    | 130 ---------
 .../scheduler/schedule/ScheduleFactory.java     |  37 ---
 .../scheduler/schedule/ScheduleTime.java        |  90 ------
 .../library/scheduler/schedule/Schedules.java   |  24 --
 .../scheduler/schedule/cron/CronExpression.java |  36 ---
 .../schedule/cron/CronExpressionConstraint.java |  34 ---
 .../scheduler/schedule/cron/CronSchedule.java   |  87 ------
 .../scheduler/schedule/cron/package.html        |  21 --
 .../scheduler/schedule/once/OnceSchedule.java   |  66 -----
 .../scheduler/schedule/once/package.html        |  21 --
 .../timeline/TimelineForScheduleConcern.java    |   2 +-
 .../timeline/TimelineScheduleMixin.java         |   3 +-
 .../timeline/TimelineSchedulerServiceMixin.java |   4 +-
 .../library/scheduler/CronScheduleTest.java     |   7 +-
 .../zest/library/scheduler/SchedulerTest.java   |   1 -
 .../scheduler/docsupport/SchedulerDocs.java     |   2 +-
 36 files changed, 1071 insertions(+), 1109 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/core/runtime/src/test/java/org/apache/zest/runtime/association/AssociationAssignmentTest.java
----------------------------------------------------------------------
diff --git 
a/core/runtime/src/test/java/org/apache/zest/runtime/association/AssociationAssignmentTest.java
 
b/core/runtime/src/test/java/org/apache/zest/runtime/association/AssociationAssignmentTest.java
index e224008..d0c7147 100644
--- 
a/core/runtime/src/test/java/org/apache/zest/runtime/association/AssociationAssignmentTest.java
+++ 
b/core/runtime/src/test/java/org/apache/zest/runtime/association/AssociationAssignmentTest.java
@@ -24,12 +24,13 @@ import org.apache.zest.api.association.Association;
 import org.apache.zest.api.entity.EntityBuilder;
 import org.apache.zest.api.entity.EntityComposite;
 import org.apache.zest.api.unitofwork.UnitOfWork;
+import org.apache.zest.api.value.ValueSerialization;
 import org.apache.zest.bootstrap.AssemblyException;
 import org.apache.zest.bootstrap.ModuleAssembly;
 import org.apache.zest.entitystore.memory.MemoryEntityStoreService;
 import org.apache.zest.spi.uuid.UuidIdentityGeneratorService;
 import org.apache.zest.test.AbstractZestTest;
-import 
org.apache.zest.valueserialization.orgjson.OrgJsonValueSerializationAssembler;
+import 
org.apache.zest.valueserialization.orgjson.OrgJsonValueSerializationService;
 import org.junit.Test;
 
 import static org.hamcrest.core.IsEqual.equalTo;
@@ -45,7 +46,8 @@ public class AssociationAssignmentTest extends 
AbstractZestTest
     {
         module.services( MemoryEntityStoreService.class );
         module.services( UuidIdentityGeneratorService.class );
-        new OrgJsonValueSerializationAssembler().assemble( module );
+        module.services( OrgJsonValueSerializationService.class )
+            .taggedWith( ValueSerialization.Formats.JSON );
         module.entities( TheAssociatedType.class );
         module.entities( TheMainType.class );
     }

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/CronSchedule.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/CronSchedule.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/CronSchedule.java
new file mode 100644
index 0000000..9ee212d
--- /dev/null
+++ 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/CronSchedule.java
@@ -0,0 +1,117 @@
+/*
+ * 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.zest.library.scheduler;
+
+import java.lang.annotation.Retention;
+import org.apache.zest.api.constraint.Constraint;
+import org.apache.zest.api.constraint.ConstraintDeclaration;
+import org.apache.zest.api.constraint.Constraints;
+import org.apache.zest.api.mixin.Mixins;
+import org.apache.zest.api.property.Immutable;
+import org.apache.zest.api.property.Property;
+import org.apache.zest.library.constraints.annotation.InstanceOf;
+import org.apache.zest.library.constraints.annotation.NotEmpty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Mixins( CronSchedule.CronScheduleMixin.class )
+public interface CronSchedule
+    extends Schedule
+{
+    /**
+     * The Cron expression indicating when the Schedule is to be run.
+     * The Schedule can NOT be changed once it is set. If this is needed, 
delete this Schedule and attach the Task
+     * to a new Schedule.
+     *
+     * @return The cron expression that will be used on {@link 
org.apache.zest.api.unitofwork.UnitOfWork} completion to compute next run
+     */
+    @CronExpression
+    @Immutable
+    Property<String> cronExpression();
+
+    abstract class CronScheduleMixin
+        implements CronSchedule
+    {
+        private static final Logger LOGGER = LoggerFactory.getLogger( 
Schedule.class );
+
+        @Override
+        public void taskStarting()
+        {
+        }
+
+        @Override
+        public void taskCompletedSuccessfully()
+        {
+        }
+
+        @Override
+        public void taskCompletedWithException( Throwable ex )
+        {
+        }
+
+        @Override
+        public String presentationString()
+        {
+            return cronExpression().get();
+        }
+
+        @Override
+        public long nextRun( long from )
+        {
+            long actualFrom = from;
+            long firstRun = start().get().getMillis();
+            if( firstRun > from )
+            {
+                actualFrom = firstRun;
+            }
+            Long nextRun = createCron().firstRunAfter( actualFrom );
+            LOGGER.info( "CronSchedule::nextRun({}) is {}", from, firstRun );
+            return nextRun;
+        }
+
+        private org.codeartisans.sked.cron.CronSchedule createCron()
+        {
+            return new org.codeartisans.sked.cron.CronSchedule( 
cronExpression().get() );
+        }
+    }
+
+    @ConstraintDeclaration
+    @Retention( RUNTIME )
+    @NotEmpty
+    @InstanceOf( String.class )
+    @Constraints( CronExpressionConstraint.class )
+    @interface CronExpression
+    {
+    }
+
+    class CronExpressionConstraint
+        implements Constraint<CronExpression, String>
+    {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        public boolean isValid( CronExpression annotation, String 
cronExpression )
+        {
+            return org.codeartisans.sked.cron.CronSchedule.isExpressionValid( 
cronExpression );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/Execution.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/Execution.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/Execution.java
deleted file mode 100644
index d3a732b..0000000
--- 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/Execution.java
+++ /dev/null
@@ -1,231 +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.zest.library.scheduler;
-
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.RejectedExecutionHandler;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import org.apache.zest.api.concern.Concerns;
-import org.apache.zest.api.configuration.Configuration;
-import org.apache.zest.api.injection.scope.Structure;
-import org.apache.zest.api.injection.scope.This;
-import org.apache.zest.api.mixin.Mixins;
-import org.apache.zest.api.structure.Module;
-import org.apache.zest.api.unitofwork.NoSuchEntityException;
-import org.apache.zest.api.unitofwork.UnitOfWork;
-import org.apache.zest.api.unitofwork.concern.UnitOfWorkConcern;
-import org.apache.zest.api.unitofwork.concern.UnitOfWorkPropagation;
-import org.apache.zest.api.unitofwork.concern.UnitOfWorkRetry;
-import org.apache.zest.library.scheduler.schedule.Schedule;
-import org.apache.zest.library.scheduler.schedule.ScheduleTime;
-
-@Mixins( Execution.ExecutionMixin.class )
-@Concerns( UnitOfWorkConcern.class )
-public interface Execution
-{
-
-    void dispatchForExecution( Schedule schedule );
-
-    void start()
-        throws Exception;
-
-    void stop()
-        throws Exception;
-
-    @UnitOfWorkPropagation
-    @UnitOfWorkRetry( retries = 3 )
-    void updateNextTime( ScheduleTime schedule );
-
-    class ExecutionMixin
-        implements Execution, Runnable
-    {
-        private static final ThreadGroup TG = new ThreadGroup( "Zest 
Scheduling" );
-
-        @Structure
-        private Module module;
-
-        @This
-        private Scheduler scheduler;
-
-        @This
-        private Configuration<SchedulerConfiguration> config;
-
-        @This
-        private ThreadFactory threadFactory;
-
-        @This
-        private RejectedExecutionHandler rejectionHandler;
-
-        private final SortedSet<ScheduleTime> timingQueue = new TreeSet<>();
-        private volatile boolean running;
-        private ThreadPoolExecutor taskExecutor;
-        private volatile Thread scheduleThread;
-
-        @Override
-        @UnitOfWorkPropagation
-        public void run()
-        {
-            synchronized( this )
-            {
-                running = true;
-                while( running )
-                {
-                    try
-                    {
-                        if( timingQueue.size() > 0 )
-                        {
-                            ScheduleTime scheduleTime = timingQueue.first();
-                            waitFor( scheduleTime );
-                            timingQueue.remove( scheduleTime );
-                            updateNextTime( scheduleTime );
-                            submitTaskForExecution( scheduleTime );
-                        }
-                        else
-                        {
-                            this.wait( 100 );
-                        }
-                    }
-                    catch( InterruptedException e )
-                    {
-                        // Ignore. Used to signal "Hey, wake up. Time to 
work..."
-                    }
-                    catch( Throwable e )
-                    {
-                        e.printStackTrace();
-                    }
-                }
-            }
-        }
-
-        private void waitFor( ScheduleTime scheduleTime )
-            throws InterruptedException
-        {
-            long now = System.currentTimeMillis();
-            long waitingTime = scheduleTime.nextTime() - now;
-            if( waitingTime > 0 )
-            {
-                this.wait( waitingTime );
-            }
-        }
-
-        @Override
-        public void updateNextTime( ScheduleTime scheduleTime )
-        {
-            long now = System.currentTimeMillis();
-            UnitOfWork uow = module.currentUnitOfWork();
-            try
-            {
-                Schedule schedule = uow.get( Schedule.class, 
scheduleTime.scheduleIdentity() );
-                long nextTime = schedule.nextRun( now + 1000 );
-                if( nextTime != Long.MIN_VALUE )
-                {
-                    scheduleTime = new ScheduleTime( 
schedule.identity().get(), nextTime );
-                    timingQueue.add( scheduleTime );
-                }
-            }
-            catch( NoSuchEntityException e )
-            {
-                // Schedule has been removed.
-                scheduler.cancelSchedule( scheduleTime.scheduleIdentity() );
-            }
-        }
-
-        private void submitTaskForExecution( ScheduleTime scheduleTime )
-        {
-            Runnable taskRunner = module.newTransient( Runnable.class, 
scheduleTime );
-            this.taskExecutor.submit( taskRunner );
-        }
-
-        public void dispatchForExecution( Schedule schedule )
-        {
-            long now = System.currentTimeMillis();
-            synchronized( this )
-            {
-                long nextRun = schedule.nextRun( now );
-                if( nextRun > 0 )
-                {
-                    timingQueue.add( new ScheduleTime( 
schedule.identity().get(), nextRun ) );
-                    scheduleThread.interrupt();
-                }
-            }
-        }
-
-        @Override
-        public void start()
-            throws Exception
-        {
-            SchedulerConfiguration configuration = config.get();
-            Integer workersCount = configuration.workersCount().get();
-            Integer workQueueSize = configuration.workQueueSize().get();
-            createThreadPoolExecutor( workersCount, workQueueSize );
-            taskExecutor.prestartAllCoreThreads();
-
-            scheduleThread = new Thread( TG, this, "Scheduler" );
-            scheduleThread.start();
-        }
-
-        private void createThreadPoolExecutor( Integer workersCount, Integer 
workQueueSize )
-        {
-            int corePoolSize = 2;
-            if( workersCount > 4 )
-            {
-                corePoolSize = workersCount / 4 + 1;
-            }
-            if( corePoolSize > 50 )
-            {
-                corePoolSize = 20;
-            }
-            if( workersCount > 200 )
-            {
-                workersCount = 200;
-            }
-            taskExecutor = new ThreadPoolExecutor( corePoolSize, workersCount,
-                                                   0, TimeUnit.MILLISECONDS,
-                                                   new LinkedBlockingQueue<>( 
workQueueSize ),
-                                                   threadFactory, 
rejectionHandler );
-        }
-
-        @Override
-        public void stop()
-            throws Exception
-        {
-            running = false;
-            synchronized( this )
-            {
-                scheduleThread.interrupt();
-            }
-            taskExecutor.shutdown();
-            try
-            {
-                taskExecutor.awaitTermination( 5, TimeUnit.SECONDS );
-            }
-            catch( InterruptedException e )
-            {
-                e.printStackTrace();
-            }
-            taskExecutor.shutdownNow();
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/OnceSchedule.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/OnceSchedule.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/OnceSchedule.java
new file mode 100644
index 0000000..299c2be
--- /dev/null
+++ 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/OnceSchedule.java
@@ -0,0 +1,68 @@
+/*
+ * 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.zest.library.scheduler;
+
+import org.apache.zest.api.mixin.Mixins;
+
+@Mixins( OnceSchedule.OnceScheduleMixin.class )
+public interface OnceSchedule
+    extends Schedule
+{
+    abstract class OnceScheduleMixin
+        implements OnceSchedule
+    {
+        @Override
+        public void taskStarting()
+        {
+        }
+
+        @Override
+        public void taskCompletedSuccessfully()
+        {
+        }
+
+        @Override
+        public void taskCompletedWithException( Throwable ex )
+        {
+        }
+
+        @Override
+        public long nextRun( long from )
+        {
+            if( done().get() )
+            {
+                return Long.MIN_VALUE;
+            }
+            done().set( true );
+            long runAt = start().get().getMillis();
+            if( runAt >= from )
+            {
+                return runAt;
+            }
+            return from;
+        }
+
+        @Override
+        public String presentationString()
+        {
+            return start().get().toString();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/Schedule.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/Schedule.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/Schedule.java
new file mode 100644
index 0000000..a0f8a6e
--- /dev/null
+++ 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/Schedule.java
@@ -0,0 +1,131 @@
+/*
+ * 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.zest.library.scheduler;
+
+import org.apache.zest.api.association.Association;
+import org.apache.zest.api.common.UseDefaults;
+import org.apache.zest.api.entity.EntityComposite;
+import org.apache.zest.api.property.Immutable;
+import org.apache.zest.api.property.Property;
+import org.apache.zest.library.scheduler.Task;
+import org.joda.time.DateTime;
+
+/**
+ * Represent the scheduling of a {@link Task}.
+ */
+public interface Schedule extends EntityComposite
+{
+    /**
+     * @return The Association to the Task to be executed when it is time.
+     */
+    Association<Task> task();
+
+    /** The first run of this Schedule.
+     *
+     * @return The property containing the first time this Schedule will be 
run.
+     */
+    @Immutable
+    Property<DateTime> start();
+
+    /** Returns true if the Schedule has been cancelled.
+     *
+     * @return true if the Schedule has been cancelled.
+     */
+    @UseDefaults
+    Property<Boolean> cancelled();
+
+    /** Returns true if the Schedule is currently running.
+     *
+     * @return true if the Schedule is currently running.
+     */
+    @UseDefaults
+    Property<Boolean> running();
+
+    /** Returns the number of times the {@link Task} has been executed.
+     * <p>
+     * Each time the {@link Task#run} method completes, with or without an 
{@link Exception}, this
+     * counter is incremented by 1.
+     * </p>
+     *
+     * @return true the number of Exception that has occurred when running the 
{@link Task}.
+     */
+    @UseDefaults
+    Property<Long> executionCounter();
+
+    /** Returns the number of Exception that has occurred when running the 
{@link Task}.
+     * <p>
+     * Each time the {@link Task#run} method throws a {@link 
RuntimeException}, this property
+     * is incremenented by 1,
+     * </p>
+     *
+     * @return true the number of Exception that has occurred when running the 
{@link Task}.
+     */
+    @UseDefaults
+    Property<Long> exceptionCounter();
+
+    /** Returns true if the Schedule is done and will not be executed any more 
times.
+     *
+     * @return true if the Schedule is done and will not be executed any more 
times.
+     */
+    @UseDefaults
+    Property<Boolean> done();
+
+    /** Returns the number of times the Schedule has been skipped, due to the 
Task was still running.
+     *
+      * @return the number of times the Schedule has been skipped, due to the 
Task was still running.
+     */
+    @UseDefaults
+    Property<Long> overrun();
+
+    /**
+     * Called just before the {@link 
org.apache.zest.library.scheduler.Task#run()} method is called.
+     */
+    void taskStarting();
+
+    /**
+     * Called directly after the {@link 
org.apache.zest.library.scheduler.Task#run()} method has been completed and
+     * returned from the method normally.
+     */
+    void taskCompletedSuccessfully();
+
+    /**
+     * Called directly after the {@link 
org.apache.zest.library.scheduler.Task#run()} method has been completed but
+     * threw a RuntimeException.
+     * @param ex The execption that was thrown in the Task. If the thrown 
Exception was an
+     *           {@link java.lang.reflect.UndeclaredThrowableException} then 
the underlying exception is passed here.
+     */
+    void taskCompletedWithException( Throwable ex );
+
+    /**
+     * Compute the next time this schedule is to be run.
+     *
+     * @param from The starting time when to look for the next time it will 
run.
+     *
+     * @return The exact absolute time when this Schedule is to be run next 
time, or -1 if never
+     */
+    long nextRun( long from );
+
+    /**
+     * Return a representation of the Schedule in a human understandable 
format.
+     *
+     * @return A String representing this schedule.
+     */
+    String presentationString();
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/ScheduleFactory.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/ScheduleFactory.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/ScheduleFactory.java
new file mode 100644
index 0000000..e891814
--- /dev/null
+++ 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/ScheduleFactory.java
@@ -0,0 +1,42 @@
+/*
+ * 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.zest.library.scheduler;
+
+import org.apache.zest.api.concern.Concerns;
+import org.apache.zest.api.unitofwork.concern.UnitOfWorkConcern;
+import org.apache.zest.api.unitofwork.concern.UnitOfWorkPropagation;
+import org.apache.zest.library.scheduler.Schedule;
+import org.apache.zest.library.scheduler.defaults.DefaultScheduleFactoryMixin;
+import org.joda.time.DateTime;
+import org.apache.zest.api.mixin.Mixins;
+import org.apache.zest.library.scheduler.Task;
+
+import static 
org.apache.zest.api.unitofwork.concern.UnitOfWorkPropagation.Propagation.MANDATORY;
+
+@Mixins( DefaultScheduleFactoryMixin.class )
+@Concerns( UnitOfWorkConcern.class )
+public interface ScheduleFactory
+{
+    @UnitOfWorkPropagation( MANDATORY)
+    Schedule newCronSchedule( Task task, String cronExpression, DateTime start 
);
+
+    @UnitOfWorkPropagation( MANDATORY)
+    Schedule newOnceSchedule( Task task, DateTime runAt );
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/Scheduler.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/Scheduler.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/Scheduler.java
index f8aae19..8510645 100644
--- 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/Scheduler.java
+++ 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/Scheduler.java
@@ -18,14 +18,13 @@
  */
 package org.apache.zest.library.scheduler;
 
+import org.apache.zest.library.scheduler.internal.Schedules;
 import org.joda.time.DateTime;
 import org.apache.zest.api.concern.Concerns;
 import org.apache.zest.api.structure.Application;
 import org.apache.zest.api.unitofwork.concern.UnitOfWorkConcern;
 import org.apache.zest.api.unitofwork.concern.UnitOfWorkPropagation;
 import org.apache.zest.library.scheduler.bootstrap.SchedulerAssembler;
-import org.apache.zest.library.scheduler.schedule.Schedule;
-import org.apache.zest.library.scheduler.schedule.cron.CronExpression;
 import org.apache.zest.library.scheduler.timeline.Timeline;
 
 import static 
org.apache.zest.api.unitofwork.concern.UnitOfWorkPropagation.Propagation.MANDATORY;
@@ -46,7 +45,7 @@ import static 
org.apache.zest.api.unitofwork.concern.UnitOfWorkPropagation.Propa
  * </p>
  * <p>
  * All {@link Schedule}s are durable and stored in the visible {@link 
org.apache.zest.spi.entitystore.EntityStore} like
- * any ordinary {@link org.apache.zest.api.entity.EntityComposite}. There is 
also a {@link org.apache.zest.library.scheduler.schedule.Schedules}
+ * any ordinary {@link org.apache.zest.api.entity.EntityComposite}. There is 
also a {@link Schedules}
  * entity composite that has Associations to all active, completed and 
cancelled schedules.
  * </p>
  * <p>
@@ -87,7 +86,7 @@ public interface Scheduler
      * @return The newly created Schedule
      */
     @UnitOfWorkPropagation( MANDATORY )
-    Schedule scheduleCron( Task task, @CronExpression String cronExpression );
+    Schedule scheduleCron( Task task, @CronSchedule.CronExpression String 
cronExpression );
 
     /**
      * Schedule a Task using a CronExpression with a given initial delay in 
milliseconds.
@@ -99,7 +98,7 @@ public interface Scheduler
      * @return The newly created Schedule
      */
     @UnitOfWorkPropagation( MANDATORY )
-    Schedule scheduleCron( Task task, @CronExpression String cronExpression, 
long initialDelay );
+    Schedule scheduleCron( Task task, @CronSchedule.CronExpression String 
cronExpression, long initialDelay );
 
     /**
      * Schedule a Task using a CronExpression starting at a given date.
@@ -111,7 +110,7 @@ public interface Scheduler
      * @return The newly created Schedule
      */
     @UnitOfWorkPropagation( MANDATORY )
-    Schedule scheduleCron( Task task, @CronExpression String cronExpression, 
DateTime start );
+    Schedule scheduleCron( Task task, @CronSchedule.CronExpression String 
cronExpression, DateTime start );
 
     /** Schedules a custom Schedule.
      *

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/SchedulerMixin.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/SchedulerMixin.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/SchedulerMixin.java
deleted file mode 100644
index 9f7423c..0000000
--- 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/SchedulerMixin.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (c) 2010-2012, Paul Merlin.
- * Copyright (c) 2012, Niclas Hedhman.
- *
- * Licensed  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.zest.library.scheduler;
-
-import org.apache.zest.api.configuration.Configuration;
-import org.apache.zest.api.injection.scope.Service;
-import org.apache.zest.api.injection.scope.Structure;
-import org.apache.zest.api.injection.scope.This;
-import org.apache.zest.api.service.ServiceActivation;
-import org.apache.zest.api.structure.Module;
-import org.apache.zest.api.unitofwork.NoSuchEntityException;
-import org.apache.zest.api.unitofwork.UnitOfWork;
-import org.apache.zest.api.unitofwork.UnitOfWorkCompletionException;
-import org.apache.zest.api.usecase.UsecaseBuilder;
-import org.apache.zest.library.scheduler.schedule.Schedule;
-import org.apache.zest.library.scheduler.schedule.ScheduleFactory;
-import org.apache.zest.library.scheduler.schedule.Schedules;
-import org.apache.zest.library.scheduler.schedule.cron.CronExpression;
-import org.joda.time.DateTime;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class SchedulerMixin
-    implements Scheduler, ServiceActivation
-{
-    private static final Logger LOGGER = LoggerFactory.getLogger( 
Scheduler.class );
-
-    @Service
-    private ScheduleFactory scheduleFactory;
-
-    @Structure
-    private Module module;
-
-    @This
-    private SchedulerService me;
-
-    @This
-    private SchedulesHandler schedulesHandler;
-
-    @This
-    private Execution execution;
-
-    @This
-    private Configuration<SchedulerConfiguration> config;
-
-    public SchedulerMixin()
-    {
-    }
-
-    @Override
-    public Schedule scheduleOnce( Task task, int initialSecondsDelay )
-    {
-        long now = System.currentTimeMillis();
-        Schedule schedule = scheduleFactory.newOnceSchedule( task, new 
DateTime( now + initialSecondsDelay * 1000 ) );
-        saveAndDispatch( schedule );
-        return schedule;
-    }
-
-    @Override
-    public Schedule scheduleOnce( Task task, DateTime runAt )
-    {
-        Schedule schedule = scheduleFactory.newOnceSchedule( task, runAt );
-        saveAndDispatch( schedule );
-        return schedule;
-    }
-
-    @Override
-    public Schedule scheduleCron( Task task, String cronExpression )
-    {
-        DateTime now = new DateTime();
-        Schedule schedule = scheduleFactory.newCronSchedule( task, 
cronExpression, now );
-        saveAndDispatch( schedule );
-        return schedule;
-    }
-
-    @Override
-    public Schedule scheduleCron( Task task, @CronExpression String 
cronExpression, DateTime start )
-    {
-        Schedule schedule = scheduleFactory.newCronSchedule( task, 
cronExpression, start );
-        saveAndDispatch( schedule );
-        return schedule;
-    }
-
-    @Override
-    public void scheduleCron( Schedule schedule )
-    {
-        saveAndDispatch( schedule );
-    }
-
-    @Override
-    public Schedule scheduleCron( Task task, String cronExpression, long 
initialDelay )
-    {
-        DateTime start = new DateTime( System.currentTimeMillis() + 
initialDelay );
-        Schedule schedule = scheduleFactory.newCronSchedule( task, 
cronExpression, start );
-        saveAndDispatch( schedule );
-        return schedule;
-    }
-
-    @Override
-    public void cancelSchedule( String scheduleId )
-    {
-        UnitOfWork uow = module.currentUnitOfWork();
-        Schedule schedule = null;
-        try
-        {
-            schedule = uow.get( Schedule.class, scheduleId );
-        }
-        catch( NoSuchEntityException e )
-        {
-            return;
-        }
-        cancelSchedule( schedule );
-    }
-
-    @Override
-    public void cancelSchedule( Schedule schedule )
-    {
-        Schedules active = schedulesHandler.getActiveSchedules();
-        if( active.schedules().remove( schedule ) )
-        {
-            schedule.cancelled().set( true );
-        }
-    }
-
-    private void saveAndDispatch( Schedule schedule )
-    {
-        Schedules schedules = schedulesHandler.getActiveSchedules();
-        schedules.schedules().add( schedule );
-        execution.dispatchForExecution( schedule );
-    }
-
-    private void loadSchedules()
-        throws UnitOfWorkCompletionException
-    {
-        try (UnitOfWork ignored = module.newUnitOfWork( 
UsecaseBuilder.newUsecase( "Initialize Schedules" ) ))
-        {
-            Schedules schedules = schedulesHandler.getActiveSchedules();
-            for( Schedule schedule : schedules.schedules() )
-            {
-                if( schedule.cancelled().get() || schedule.done().get() )
-                {
-                    schedules.schedules().remove( schedule );
-                }
-                else
-                {
-                    execution.dispatchForExecution( schedule );
-                }
-            }
-        }
-    }
-
-    @Override
-    public void activateService()
-        throws Exception
-    {
-        // Throws IllegalArgument if corePoolSize or keepAliveTime less than 
zero,
-        // or if workersCount less than or equal to zero,
-        // or if corePoolSize greater than workersCount.
-        execution.start();
-        loadSchedules();
-        LOGGER.debug( "Activated" );
-    }
-
-    @Override
-    public void passivateService()
-        throws Exception
-    {
-        execution.stop();
-        LOGGER.debug( "Passivated" );
-    }
-}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/SchedulerService.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/SchedulerService.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/SchedulerService.java
index 0d1ef4c..ade3d1e 100644
--- 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/SchedulerService.java
+++ 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/SchedulerService.java
@@ -23,6 +23,7 @@ import org.apache.zest.api.mixin.Mixins;
 import org.apache.zest.api.service.ServiceActivation;
 import org.apache.zest.library.scheduler.defaults.DefaultRejectionHandler;
 import org.apache.zest.library.scheduler.defaults.DefaultThreadFactory;
+import org.apache.zest.library.scheduler.internal.SchedulerMixin;
 
 @Mixins( { SchedulerMixin.class, DefaultThreadFactory.class, 
DefaultRejectionHandler.class } )
 public interface SchedulerService

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/SchedulesHandler.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/SchedulesHandler.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/SchedulesHandler.java
index b76ef96..b4a2b4d 100644
--- 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/SchedulesHandler.java
+++ 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/SchedulesHandler.java
@@ -28,7 +28,7 @@ import org.apache.zest.api.structure.Module;
 import org.apache.zest.api.unitofwork.NoSuchEntityException;
 import org.apache.zest.api.unitofwork.UnitOfWork;
 import org.apache.zest.api.unitofwork.concern.UnitOfWorkPropagation;
-import org.apache.zest.library.scheduler.schedule.Schedules;
+import org.apache.zest.library.scheduler.internal.Schedules;
 
 @Mixins(SchedulesHandler.SchedulesHandlerMixin.class)
 public interface SchedulesHandler

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/Task.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/Task.java 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/Task.java
index 6cc284c..9cce9ce 100644
--- 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/Task.java
+++ 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/Task.java
@@ -29,7 +29,7 @@ import 
org.apache.zest.api.unitofwork.concern.UnitOfWorkPropagation;
 /**
  * Compose an Entity using this type to be able to Schedule it.
  *<p>
- * A Task is associated from a {@link 
org.apache.zest.library.scheduler.schedule.Schedule}, and upon time to execute
+ * A Task is associated from a {@link Schedule}, and upon time to execute
  * the SchedulerService will dispatch a TaskRunner in a new thread, and 
establish a UnitOfWork (Usecase name of "Task Runner").
  *</p>
  *<p>

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/TaskRunner.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/TaskRunner.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/TaskRunner.java
deleted file mode 100644
index 3e0e975..0000000
--- 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/TaskRunner.java
+++ /dev/null
@@ -1,115 +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.zest.library.scheduler;
-
-import java.lang.reflect.UndeclaredThrowableException;
-import java.util.concurrent.locks.ReentrantLock;
-import org.apache.zest.api.injection.scope.Structure;
-import org.apache.zest.api.injection.scope.Uses;
-import org.apache.zest.api.structure.Module;
-import org.apache.zest.api.unitofwork.UnitOfWork;
-import org.apache.zest.api.usecase.UsecaseBuilder;
-import org.apache.zest.library.scheduler.schedule.Schedule;
-import org.apache.zest.library.scheduler.schedule.ScheduleTime;
-
-public class TaskRunner
-    implements Runnable
-{
-    private static ReentrantLock lock = new ReentrantLock();
-
-    @Structure
-    private Module module;
-
-    @Uses
-    private ScheduleTime schedule;
-
-    @Override
-    public void run()
-    {
-        // TODO: (niclas) I am NOT happy with this implementation, requiring 3 
UnitOfWorks to be created. 15-20 milliseconds on my MacBook. If there is a 
better way to detect overrun, two of those might not be needed.
-        UnitOfWork uow = module.newUnitOfWork( UsecaseBuilder.newUsecase( 
"Task Runner initialize" ) );
-        try
-        {
-            lock.lock();
-            Schedule schedule = uow.get( Schedule.class, 
this.schedule.scheduleIdentity() );
-            if( !schedule.running().get() )  // check for overrun.
-            {
-                try
-                {
-                    schedule.taskStarting();
-                    schedule.running().set( true );
-                    uow.complete();                     // This completion is 
needed to detect overrun
-                    lock.unlock();
-
-                    uow = module.newUnitOfWork( UsecaseBuilder.newUsecase( 
"Task Runner" ) );
-                    schedule = uow.get( schedule );     // re-attach the 
entity to the new UnitOfWork
-                    Task task = schedule.task().get();
-                    task.run();
-                    uow.complete();
-                    lock.lock();
-                    uow = module.newUnitOfWork( UsecaseBuilder.newUsecase( 
"Task Runner conclude" ) );
-                    schedule = uow.get( schedule );     // re-attach the 
entity to the new UnitOfWork
-                    schedule.running().set( false );
-                    schedule.taskCompletedSuccessfully();
-                    schedule.executionCounter().set( 
schedule.executionCounter().get() + 1 );
-                }
-                catch( RuntimeException ex )
-                {
-                    schedule.running().set( false );
-                    processException( schedule, ex );
-                }
-            }
-            else
-            {
-                schedule.overrun().set( schedule.overrun().get() + 1 );
-            }
-            uow.complete();
-        }
-        catch( Exception e )
-        {
-            e.printStackTrace();
-            throw new UndeclaredThrowableException( e );
-        }
-        finally
-        {
-            uow.discard();
-            try
-            {
-                lock.unlock();
-            }
-            catch( IllegalMonitorStateException e )
-            {
-                // ignore, as it may happen on certain exceptions.
-            }
-        }
-    }
-
-    private void processException( Schedule schedule, RuntimeException ex )
-    {
-        Throwable exception = ex;
-        while( exception instanceof UndeclaredThrowableException )
-        {
-            exception = ( (UndeclaredThrowableException) ex 
).getUndeclaredThrowable();
-        }
-        schedule.taskCompletedWithException( exception );
-        schedule.exceptionCounter().set( schedule.exceptionCounter().get() + 1 
);
-    }
-}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/bootstrap/SchedulerAssembler.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/bootstrap/SchedulerAssembler.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/bootstrap/SchedulerAssembler.java
index af5f475..317abd1 100644
--- 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/bootstrap/SchedulerAssembler.java
+++ 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/bootstrap/SchedulerAssembler.java
@@ -27,11 +27,11 @@ import org.apache.zest.bootstrap.ServiceDeclaration;
 import org.apache.zest.bootstrap.ValueDeclaration;
 import org.apache.zest.library.scheduler.SchedulerConfiguration;
 import org.apache.zest.library.scheduler.SchedulerService;
-import org.apache.zest.library.scheduler.TaskRunner;
-import org.apache.zest.library.scheduler.schedule.ScheduleFactory;
-import org.apache.zest.library.scheduler.schedule.Schedules;
-import org.apache.zest.library.scheduler.schedule.cron.CronSchedule;
-import org.apache.zest.library.scheduler.schedule.once.OnceSchedule;
+import org.apache.zest.library.scheduler.internal.TaskRunner;
+import org.apache.zest.library.scheduler.ScheduleFactory;
+import org.apache.zest.library.scheduler.internal.Schedules;
+import org.apache.zest.library.scheduler.CronSchedule;
+import org.apache.zest.library.scheduler.OnceSchedule;
 import org.apache.zest.library.scheduler.timeline.Timeline;
 import org.apache.zest.library.scheduler.timeline.TimelineForScheduleConcern;
 import org.apache.zest.library.scheduler.timeline.TimelineRecord;

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/defaults/DefaultScheduleFactoryMixin.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/defaults/DefaultScheduleFactoryMixin.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/defaults/DefaultScheduleFactoryMixin.java
index f05c041..27c7125 100644
--- 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/defaults/DefaultScheduleFactoryMixin.java
+++ 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/defaults/DefaultScheduleFactoryMixin.java
@@ -27,10 +27,10 @@ import org.apache.zest.api.structure.Module;
 import org.apache.zest.api.unitofwork.UnitOfWork;
 import org.apache.zest.library.scheduler.SchedulerService;
 import org.apache.zest.library.scheduler.Task;
-import org.apache.zest.library.scheduler.schedule.Schedule;
-import org.apache.zest.library.scheduler.schedule.ScheduleFactory;
-import org.apache.zest.library.scheduler.schedule.cron.CronSchedule;
-import org.apache.zest.library.scheduler.schedule.once.OnceSchedule;
+import org.apache.zest.library.scheduler.Schedule;
+import org.apache.zest.library.scheduler.ScheduleFactory;
+import org.apache.zest.library.scheduler.CronSchedule;
+import org.apache.zest.library.scheduler.OnceSchedule;
 import org.apache.zest.spi.uuid.UuidIdentityGeneratorService;
 import org.joda.time.DateTime;
 import org.slf4j.Logger;

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/defaults/DefaultThreadFactory.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/defaults/DefaultThreadFactory.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/defaults/DefaultThreadFactory.java
index c834f50..43520bd 100644
--- 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/defaults/DefaultThreadFactory.java
+++ 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/defaults/DefaultThreadFactory.java
@@ -22,6 +22,7 @@ package org.apache.zest.library.scheduler.defaults;
 
 import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.zest.api.injection.scope.This;
+import org.apache.zest.library.scheduler.internal.Execution;
 import org.apache.zest.library.scheduler.SchedulerService;
 
 public class DefaultThreadFactory
@@ -35,7 +36,7 @@ public class DefaultThreadFactory
     protected DefaultThreadFactory( @This SchedulerService me )
     {
         SecurityManager sm = System.getSecurityManager();
-        group = ( sm != null ) ? sm.getThreadGroup() : 
Thread.currentThread().getThreadGroup();
+        group = ( sm != null ) ? sm.getThreadGroup() : 
Execution.ExecutionMixin.TG;
         namePrefix = me.identity().get() + "-P" + 
POOL_NUMBER.getAndIncrement() + "W";
     }
 

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/Execution.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/Execution.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/Execution.java
new file mode 100644
index 0000000..a92ee2b
--- /dev/null
+++ 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/Execution.java
@@ -0,0 +1,278 @@
+/*
+ * 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.zest.library.scheduler.internal;
+
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import org.apache.zest.api.concern.Concerns;
+import org.apache.zest.api.configuration.Configuration;
+import org.apache.zest.api.injection.scope.Structure;
+import org.apache.zest.api.injection.scope.This;
+import org.apache.zest.api.mixin.Mixins;
+import org.apache.zest.api.structure.Module;
+import org.apache.zest.api.unitofwork.NoSuchEntityException;
+import org.apache.zest.api.unitofwork.UnitOfWork;
+import org.apache.zest.api.unitofwork.concern.UnitOfWorkConcern;
+import org.apache.zest.api.unitofwork.concern.UnitOfWorkPropagation;
+import org.apache.zest.library.scheduler.Schedule;
+import org.apache.zest.library.scheduler.Scheduler;
+import org.apache.zest.library.scheduler.SchedulerConfiguration;
+
+/**
+ * This composite handles the Execution of Schedules.
+ *
+ * The composite is internal and should never be used by clients.
+ */
+@Mixins( Execution.ExecutionMixin.class )
+@Concerns( UnitOfWorkConcern.class )
+public interface Execution
+{
+    void dispatchForExecution( Schedule schedule );
+
+    void start()
+        throws Exception;
+
+    void stop()
+        throws Exception;
+
+    @UnitOfWorkPropagation( usecase = "Schedule Next Time Update" )
+    void updateNextTime( ScheduleTime schedule );   // This method is public, 
only because the UnitOfWorkConcern is wanted.
+
+    class ExecutionMixin
+        implements Execution, Runnable
+    {
+        public static final ThreadGroup TG = new ThreadGroup( "Zest 
Scheduling" );
+
+        private final Object lock = new Object();
+
+        @Structure
+        private Module module;
+
+        @This
+        private Scheduler scheduler;
+
+        @This
+        private Configuration<SchedulerConfiguration> config;
+
+        @This
+        private ThreadFactory threadFactory;
+
+        @This
+        private RejectedExecutionHandler rejectionHandler;
+
+        private final SortedSet<ScheduleTime> timingQueue = new TreeSet<>();
+        private volatile boolean running;
+        private ThreadPoolExecutor taskExecutor;
+        private volatile Thread scheduleThread;
+
+        @Override
+        public void run()
+        {
+            running = true;
+            while( running )
+            {
+                try
+                {
+                    ScheduleTime scheduleTime = timing();
+                    if( scheduleTime != null )
+                    {
+                        waitFor( scheduleTime );
+
+                        if( isTime( scheduleTime ) ) // We might have been 
awakened to reschedule
+                        {
+                            updateNextTime( scheduleTime );
+                        }
+                    }
+                    else
+                    {
+                        waitFor( 100 );
+                    }
+                }
+                catch( Throwable e )
+                {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        private ScheduleTime timing()
+        {
+            synchronized( lock )
+            {
+                if( timingQueue.size() == 0 )
+                {
+                    return null;
+                }
+                return timingQueue.first();
+            }
+        }
+
+        private boolean isTime( ScheduleTime scheduleTime )
+        {
+            long now = System.currentTimeMillis();
+            return scheduleTime.nextTime() <= now;
+        }
+
+        private void waitFor( ScheduleTime scheduleTime )
+            throws InterruptedException
+        {
+            long now = System.currentTimeMillis();
+            long waitingTime = scheduleTime.nextTime() - now;
+            waitFor( waitingTime );
+        }
+
+        private void waitFor( long waitingTime )
+        {
+            if( waitingTime > 0 )
+            {
+                synchronized( lock )
+                {
+                    try
+                    {
+                        lock.wait( waitingTime );
+                    }
+                    catch( InterruptedException e )
+                    {
+                        // should be ignored.
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void updateNextTime( ScheduleTime oldScheduleTime )
+        {
+            long now = System.currentTimeMillis();
+            UnitOfWork uow = module.currentUnitOfWork();
+            try
+            {
+                submitTaskForExecution( oldScheduleTime );
+                Schedule schedule = uow.get( Schedule.class, 
oldScheduleTime.scheduleIdentity() );
+                long nextTime = schedule.nextRun( now + 1000 );
+                if( nextTime != Long.MIN_VALUE )
+                {
+                    ScheduleTime newScheduleTime = new ScheduleTime( 
schedule.identity().get(), nextTime );
+                    synchronized( lock )
+                    {
+                        // Re-add to the Timing Queue, to re-position the 
sorting.
+                        timingQueue.remove( oldScheduleTime );
+                        timingQueue.add( newScheduleTime );
+                    }
+                }
+                else
+                {
+                    synchronized( lock )
+                    {
+                        timingQueue.remove( oldScheduleTime );
+                    }
+                }
+            }
+            catch( NoSuchEntityException e )
+            {
+                e.printStackTrace();
+//                scheduler.cancelSchedule( oldScheduleTime.scheduleIdentity() 
);
+            }
+        }
+
+        private void submitTaskForExecution( ScheduleTime scheduleTime )
+        {
+            Runnable taskRunner = module.newTransient( Runnable.class, 
scheduleTime );
+            this.taskExecutor.submit( taskRunner );
+        }
+
+        public void dispatchForExecution( Schedule schedule )
+        {
+            long now = System.currentTimeMillis();
+            long nextRun = schedule.nextRun( now + 1000 );
+            if( nextRun > 0 )
+            {
+                synchronized( lock )
+                {
+                    timingQueue.add( new ScheduleTime( 
schedule.identity().get(), nextRun ) );
+                    lock.notifyAll();
+                }
+            }
+        }
+
+        @Override
+        public void start()
+            throws Exception
+        {
+            SchedulerConfiguration configuration = config.get();
+            Integer workersCount = configuration.workersCount().get();
+            Integer workQueueSize = configuration.workQueueSize().get();
+            createThreadPoolExecutor( workersCount, workQueueSize );
+            taskExecutor.prestartAllCoreThreads();
+
+            SecurityManager sm = System.getSecurityManager();
+            ThreadGroup threadGroup = sm != null ? sm.getThreadGroup() : TG;
+            scheduleThread = new Thread( threadGroup, this, "Scheduler" );
+            scheduleThread.start();
+        }
+
+        private void createThreadPoolExecutor( Integer workersCount, Integer 
workQueueSize )
+        {
+            int corePoolSize = 2;
+            if( workersCount > 4 )
+            {
+                corePoolSize = workersCount / 4 + 1;
+            }
+            if( corePoolSize > 50 )
+            {
+                corePoolSize = 20;
+            }
+            if( workersCount > 200 )
+            {
+                workersCount = 200;
+            }
+            taskExecutor = new ThreadPoolExecutor( corePoolSize, workersCount,
+                                                   0, TimeUnit.MILLISECONDS,
+                                                   new LinkedBlockingQueue<>( 
workQueueSize ),
+                                                   threadFactory, 
rejectionHandler );
+        }
+
+        @Override
+        public void stop()
+            throws Exception
+        {
+            running = false;
+            synchronized( this )
+            {
+                scheduleThread.interrupt();
+            }
+            taskExecutor.shutdown();
+            try
+            {
+                taskExecutor.awaitTermination( 5, TimeUnit.SECONDS );
+            }
+            catch( InterruptedException e )
+            {
+                e.printStackTrace();
+            }
+            taskExecutor.shutdownNow();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/ScheduleTime.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/ScheduleTime.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/ScheduleTime.java
new file mode 100644
index 0000000..b008de7
--- /dev/null
+++ 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/ScheduleTime.java
@@ -0,0 +1,61 @@
+/*
+ * 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.zest.library.scheduler.internal;
+
+import org.apache.zest.api.util.NullArgumentException;
+
+public final class ScheduleTime
+    implements Comparable<ScheduleTime>
+{
+    private String scheduleIdentity;
+    private long nextTime;
+
+    public ScheduleTime( String scheduleIdentity, long nextTime )
+    {
+        NullArgumentException.validateNotEmpty( "scheduleIdentity", 
scheduleIdentity );
+        this.scheduleIdentity = scheduleIdentity;
+        this.nextTime = nextTime;
+    }
+
+    public long nextTime()
+    {
+        return nextTime;
+    }
+
+    public String scheduleIdentity()
+    {
+        return scheduleIdentity;
+    }
+
+    @Override
+    public int compareTo( ScheduleTime another )
+    {
+        if( this.scheduleIdentity.equals( another.scheduleIdentity ) )
+        {
+            return 0;
+        }
+
+        if( this.nextTime < another.nextTime )
+        {
+            return -1;
+        }
+        return 1;
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/SchedulerMixin.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/SchedulerMixin.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/SchedulerMixin.java
new file mode 100644
index 0000000..3afafa5
--- /dev/null
+++ 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/SchedulerMixin.java
@@ -0,0 +1,199 @@
+/*
+ * 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.zest.library.scheduler.internal;
+
+import org.apache.zest.api.configuration.Configuration;
+import org.apache.zest.api.injection.scope.Service;
+import org.apache.zest.api.injection.scope.Structure;
+import org.apache.zest.api.injection.scope.This;
+import org.apache.zest.api.service.ServiceActivation;
+import org.apache.zest.api.structure.Module;
+import org.apache.zest.api.unitofwork.NoSuchEntityException;
+import org.apache.zest.api.unitofwork.UnitOfWork;
+import org.apache.zest.api.unitofwork.UnitOfWorkCompletionException;
+import org.apache.zest.api.usecase.UsecaseBuilder;
+import org.apache.zest.library.scheduler.Scheduler;
+import org.apache.zest.library.scheduler.SchedulerConfiguration;
+import org.apache.zest.library.scheduler.SchedulerService;
+import org.apache.zest.library.scheduler.SchedulesHandler;
+import org.apache.zest.library.scheduler.Task;
+import org.apache.zest.library.scheduler.CronSchedule;
+import org.apache.zest.library.scheduler.Schedule;
+import org.apache.zest.library.scheduler.ScheduleFactory;
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SchedulerMixin
+    implements Scheduler, ServiceActivation
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger( 
Scheduler.class );
+
+    @Service
+    private ScheduleFactory scheduleFactory;
+
+    @Structure
+    private Module module;
+
+    @This
+    private SchedulerService me;
+
+    @This
+    private SchedulesHandler schedulesHandler;
+
+    @This
+    private Execution execution;
+
+    @This
+    private Configuration<SchedulerConfiguration> config;
+
+    public SchedulerMixin()
+    {
+    }
+
+    @Override
+    public Schedule scheduleOnce( Task task, int initialSecondsDelay )
+    {
+        long now = System.currentTimeMillis();
+        Schedule schedule = scheduleFactory.newOnceSchedule( task, new 
DateTime( now + initialSecondsDelay * 1000 ) );
+        saveAndDispatch( schedule );
+        return schedule;
+    }
+
+    @Override
+    public Schedule scheduleOnce( Task task, DateTime runAt )
+    {
+        Schedule schedule = scheduleFactory.newOnceSchedule( task, runAt );
+        saveAndDispatch( schedule );
+        return schedule;
+    }
+
+    @Override
+    public Schedule scheduleCron( Task task, String cronExpression )
+    {
+        DateTime now = new DateTime();
+        Schedule schedule = scheduleFactory.newCronSchedule( task, 
cronExpression, now );
+        saveAndDispatch( schedule );
+        return schedule;
+    }
+
+    @Override
+    public Schedule scheduleCron( Task task, @CronSchedule.CronExpression 
String cronExpression, DateTime start )
+    {
+        Schedule schedule = scheduleFactory.newCronSchedule( task, 
cronExpression, start );
+        saveAndDispatch( schedule );
+        return schedule;
+    }
+
+    @Override
+    public void scheduleCron( Schedule schedule )
+    {
+        saveAndDispatch( schedule );
+    }
+
+    @Override
+    public Schedule scheduleCron( Task task, String cronExpression, long 
initialDelay )
+    {
+        DateTime start = new DateTime( System.currentTimeMillis() + 
initialDelay );
+        Schedule schedule = scheduleFactory.newCronSchedule( task, 
cronExpression, start );
+        saveAndDispatch( schedule );
+        return schedule;
+    }
+
+    @Override
+    public void cancelSchedule( String scheduleId )
+    {
+        UnitOfWork uow = module.currentUnitOfWork();
+        Schedule schedule = null;
+        try
+        {
+            schedule = uow.get( Schedule.class, scheduleId );
+        }
+        catch( NoSuchEntityException e )
+        {
+            return;
+        }
+        cancelSchedule( schedule );
+    }
+
+    @Override
+    public void cancelSchedule( Schedule schedule )
+    {
+        Schedules active = schedulesHandler.getActiveSchedules();
+        if( active.schedules().remove( schedule ) )
+        {
+            schedule.cancelled().set( true );
+        }
+    }
+
+    private void saveAndDispatch( Schedule schedule )
+    {
+        Schedules schedules = schedulesHandler.getActiveSchedules();
+        schedules.schedules().add( schedule );
+        execution.dispatchForExecution( schedule );
+    }
+
+    private void loadSchedules()
+        throws UnitOfWorkCompletionException
+    {
+        try (UnitOfWork ignored = module.newUnitOfWork( 
UsecaseBuilder.newUsecase( "Initialize Schedules" ) ))
+        {
+            Schedules schedules = schedulesHandler.getActiveSchedules();
+            for( Schedule schedule : schedules.schedules() )
+            {
+                dispatch( schedule );
+            }
+        }
+    }
+
+    private void dispatch( Schedule schedule )
+    {
+        try
+        {
+            if( !schedule.cancelled().get() && !schedule.done().get() )
+            {
+                execution.dispatchForExecution( schedule );
+            }
+        } catch( Exception e )
+        {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void activateService()
+        throws Exception
+    {
+        // Throws IllegalArgument if corePoolSize or keepAliveTime less than 
zero,
+        // or if workersCount less than or equal to zero,
+        // or if corePoolSize greater than workersCount.
+        execution.start();
+        loadSchedules();
+        LOGGER.debug( "Activated" );
+    }
+
+    @Override
+    public void passivateService()
+        throws Exception
+    {
+        execution.stop();
+        LOGGER.debug( "Passivated" );
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/Schedules.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/Schedules.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/Schedules.java
new file mode 100644
index 0000000..95563c1
--- /dev/null
+++ 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/Schedules.java
@@ -0,0 +1,28 @@
+/*
+ * 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.zest.library.scheduler.internal;
+
+import org.apache.zest.api.association.ManyAssociation;
+import org.apache.zest.library.scheduler.Schedule;
+
+public interface Schedules
+{
+    ManyAssociation<Schedule> schedules();
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/TaskRunner.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/TaskRunner.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/TaskRunner.java
new file mode 100644
index 0000000..fa52d73
--- /dev/null
+++ 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/internal/TaskRunner.java
@@ -0,0 +1,115 @@
+/*
+ * 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.zest.library.scheduler.internal;
+
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.concurrent.locks.ReentrantLock;
+import org.apache.zest.api.injection.scope.Structure;
+import org.apache.zest.api.injection.scope.Uses;
+import org.apache.zest.api.structure.Module;
+import org.apache.zest.api.unitofwork.UnitOfWork;
+import org.apache.zest.api.usecase.UsecaseBuilder;
+import org.apache.zest.library.scheduler.Task;
+import org.apache.zest.library.scheduler.Schedule;
+
+public class TaskRunner
+    implements Runnable
+{
+    private static ReentrantLock lock = new ReentrantLock();
+
+    @Structure
+    private Module module;
+
+    @Uses
+    private ScheduleTime schedule;
+
+    @Override
+    public void run()
+    {
+        // TODO: (niclas) I am NOT happy with this implementation, requiring 3 
UnitOfWorks to be created. 15-20 milliseconds on my MacBook. If there is a 
better way to detect overrun, two of those might not be needed.
+        UnitOfWork uow = module.newUnitOfWork( UsecaseBuilder.newUsecase( 
"Task Runner initialize" ) );
+        try
+        {
+            lock.lock();
+            Schedule schedule = uow.get( Schedule.class, 
this.schedule.scheduleIdentity() );
+            if( !schedule.running().get() )  // check for overrun.
+            {
+                try
+                {
+                    schedule.taskStarting();
+                    schedule.running().set( true );
+                    uow.complete();                     // This completion is 
needed to detect overrun
+
+                    uow = module.newUnitOfWork( UsecaseBuilder.newUsecase( 
"Task Runner" ) );
+                    schedule = uow.get( schedule );     // re-attach the 
entity to the new UnitOfWork
+                    Task task = schedule.task().get();
+                    lock.unlock();
+                    task.run();
+                    lock.lock();
+                    uow.complete();
+                    uow = module.newUnitOfWork( UsecaseBuilder.newUsecase( 
"Task Runner conclude" ) );
+                    schedule = uow.get( schedule );     // re-attach the 
entity to the new UnitOfWork
+                    schedule.running().set( false );
+                    schedule.taskCompletedSuccessfully();
+                    schedule.executionCounter().set( 
schedule.executionCounter().get() + 1 );
+                }
+                catch( RuntimeException ex )
+                {
+                    schedule.running().set( false );
+                    processException( schedule, ex );
+                }
+            }
+            else
+            {
+                schedule.overrun().set( schedule.overrun().get() + 1 );
+            }
+            uow.complete();
+        }
+        catch( Exception e )
+        {
+            e.printStackTrace();
+            throw new UndeclaredThrowableException( e );
+        }
+        finally
+        {
+            uow.discard();
+            try
+            {
+                lock.unlock();
+            }
+            catch( IllegalMonitorStateException e )
+            {
+                // ignore, as it may happen on certain exceptions.
+            }
+        }
+    }
+
+    private void processException( Schedule schedule, RuntimeException ex )
+    {
+        Throwable exception = ex;
+        while( exception instanceof UndeclaredThrowableException )
+        {
+            exception = ( (UndeclaredThrowableException) ex 
).getUndeclaredThrowable();
+        }
+        schedule.taskCompletedWithException( exception );
+        schedule.exceptionCounter().set( schedule.exceptionCounter().get() + 1 
);
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/schedule/Schedule.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/schedule/Schedule.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/schedule/Schedule.java
deleted file mode 100644
index 48f2e6f..0000000
--- 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/schedule/Schedule.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (c) 2010-2012, Paul Merlin.
- * Copyright (c) 2012, Niclas Hedhman.
- *
- * Licensed  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.zest.library.scheduler.schedule;
-
-import org.apache.zest.api.association.Association;
-import org.apache.zest.api.common.UseDefaults;
-import org.apache.zest.api.entity.EntityComposite;
-import org.apache.zest.api.property.Immutable;
-import org.apache.zest.api.property.Property;
-import org.apache.zest.library.scheduler.Task;
-import org.joda.time.DateTime;
-
-/**
- * Represent the scheduling of a {@link Task}.
- */
-public interface Schedule extends EntityComposite
-{
-    /**
-     * @return The Association to the Task to be executed when it is time.
-     */
-    Association<Task> task();
-
-    /** The first run of this Schedule.
-     *
-     * @return The property containing the first time this Schedule will be 
run.
-     */
-    @Immutable
-    Property<DateTime> start();
-
-    /** Returns true if the Schedule has been cancelled.
-     *
-     * @return true if the Schedule has been cancelled.
-     */
-    @UseDefaults
-    Property<Boolean> cancelled();
-
-    /** Returns true if the Schedule is currently running.
-     *
-     * @return true if the Schedule is currently running.
-     */
-    @UseDefaults
-    Property<Boolean> running();
-
-    /** Returns the number of times the {@link Task} has been executed.
-     * <p>
-     * Each time the {@link Task#run} method completes, with or without an 
{@link Exception}, this
-     * counter is incremented by 1.
-     * </p>
-     *
-     * @return true the number of Exception that has occurred when running the 
{@link Task}.
-     */
-    @UseDefaults
-    Property<Long> executionCounter();
-
-    /** Returns the number of Exception that has occurred when running the 
{@link Task}.
-     * <p>
-     * Each time the {@link Task#run} method throws a {@link 
RuntimeException}, this property
-     * is incremenented by 1,
-     * </p>
-     *
-     * @return true the number of Exception that has occurred when running the 
{@link Task}.
-     */
-    @UseDefaults
-    Property<Long> exceptionCounter();
-
-    /** Returns true if the Schedule is done and will not be executed any more 
times.
-     *
-     * @return true if the Schedule is done and will not be executed any more 
times.
-     */
-    @UseDefaults
-    Property<Boolean> done();
-
-    /** Returns the number of times the Schedule has been skipped, due to the 
Task was still running.
-     *
-      * @return the number of times the Schedule has been skipped, due to the 
Task was still running.
-     */
-    @UseDefaults
-    Property<Long> overrun();
-
-    /**
-     * Called just before the {@link 
org.apache.zest.library.scheduler.Task#run()} method is called.
-     */
-    void taskStarting();
-
-    /**
-     * Called directly after the {@link 
org.apache.zest.library.scheduler.Task#run()} method has been completed and
-     * returned from the method normally.
-     */
-    void taskCompletedSuccessfully();
-
-    /**
-     * Called directly after the {@link 
org.apache.zest.library.scheduler.Task#run()} method has been completed but
-     * threw a RuntimeException.
-     * @param ex The execption that was thrown in the Task. If the thrown 
Exception was an
-     *           {@link java.lang.reflect.UndeclaredThrowableException} then 
the underlying exception is passed here.
-     */
-    void taskCompletedWithException( Throwable ex );
-
-    /**
-     * Compute the next time this schedule is to be run.
-     *
-     * @param from The starting time when to look for the next time it will 
run.
-     *
-     * @return The exact absolute time when this Schedule is to be run next 
time, or -1 if never
-     */
-    long nextRun( long from );
-
-    /**
-     * Return a representation of the Schedule in a human understandable 
format.
-     *
-     * @return A String representing this schedule.
-     */
-    String presentationString();
-}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/schedule/ScheduleFactory.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/schedule/ScheduleFactory.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/schedule/ScheduleFactory.java
deleted file mode 100644
index 133ec1c..0000000
--- 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/schedule/ScheduleFactory.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2012, Niclas Hedhman. All Rights Reserved.
- *
- * Licensed 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.zest.library.scheduler.schedule;
-
-import org.apache.zest.api.concern.Concerns;
-import org.apache.zest.api.unitofwork.concern.UnitOfWorkConcern;
-import org.apache.zest.api.unitofwork.concern.UnitOfWorkPropagation;
-import org.apache.zest.library.scheduler.defaults.DefaultScheduleFactoryMixin;
-import org.joda.time.DateTime;
-import org.apache.zest.api.mixin.Mixins;
-import org.apache.zest.library.scheduler.Task;
-
-import static 
org.apache.zest.api.unitofwork.concern.UnitOfWorkPropagation.Propagation.MANDATORY;
-
-@Mixins( DefaultScheduleFactoryMixin.class )
-@Concerns( UnitOfWorkConcern.class )
-public interface ScheduleFactory
-{
-    @UnitOfWorkPropagation( MANDATORY)
-    Schedule newCronSchedule( Task task, String cronExpression, DateTime start 
);
-
-    @UnitOfWorkPropagation( MANDATORY)
-    Schedule newOnceSchedule( Task task, DateTime runAt );
-}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/schedule/ScheduleTime.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/schedule/ScheduleTime.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/schedule/ScheduleTime.java
deleted file mode 100644
index ed38cd6..0000000
--- 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/schedule/ScheduleTime.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (c) 2010-2012, Paul Merlin. All Rights Reserved.
- * Copyright (c) 2012, Niclas Hedhman. All Rights Reserved.
- *
- * Licensed 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.zest.library.scheduler.schedule;
-
-import org.apache.zest.api.util.NullArgumentException;
-
-public final class ScheduleTime
-    implements Comparable<ScheduleTime>
-{
-    private String scheduleIdentity;
-    private long nextTime;
-
-    public ScheduleTime( String scheduleIdentity, long nextTime )
-    {
-        NullArgumentException.validateNotEmpty( "scheduleIdentity", 
scheduleIdentity );
-        this.scheduleIdentity = scheduleIdentity;
-        this.nextTime = nextTime;
-    }
-
-    @Override
-    public boolean equals( Object o )
-    {
-        if( this == o )
-        {
-            return true;
-        }
-        if( o == null || getClass() != o.getClass() )
-        {
-            return false;
-        }
-        ScheduleTime that = (ScheduleTime) o;
-        if( nextTime != that.nextTime )
-        {
-            return false;
-        }
-        return scheduleIdentity.equals( that.scheduleIdentity );
-    }
-
-    @Override
-    public int hashCode()
-    {
-        int result = scheduleIdentity.hashCode();
-        result = 31 * result + (int) ( nextTime ^ ( nextTime >>> 32 ) );
-        return result;
-    }
-
-    public long nextTime()
-    {
-        return nextTime;
-    }
-
-    public String scheduleIdentity()
-    {
-        return scheduleIdentity;
-    }
-
-    @Override
-    public int compareTo( ScheduleTime another )
-    {
-        if( this.nextTime < another.nextTime )
-        {
-            return -1;
-        }
-        else
-        {
-            if( this.nextTime == another.nextTime )
-            {
-                return 0;
-            }
-            else
-            {
-                return 1;
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/73b7f196/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/schedule/Schedules.java
----------------------------------------------------------------------
diff --git 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/schedule/Schedules.java
 
b/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/schedule/Schedules.java
deleted file mode 100644
index ac65da3..0000000
--- 
a/libraries/scheduler/src/main/java/org/apache/zest/library/scheduler/schedule/Schedules.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2010-2012, Paul Merlin. All Rights Reserved.
- * Copyright (c) 2012, Niclas Hedhman. All Rights Reserved.
- *
- * Licensed 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.zest.library.scheduler.schedule;
-
-import org.apache.zest.api.association.ManyAssociation;
-
-public interface Schedules
-{
-    ManyAssociation<Schedule> schedules();
-}

Reply via email to