You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2013/10/11 14:38:55 UTC

svn commit: r1531274 - in /sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event: impl/jobs/ impl/jobs/config/ impl/jobs/console/ impl/support/ jobs/

Author: cziegeler
Date: Fri Oct 11 12:38:55 2013
New Revision: 1531274

URL: http://svn.apache.org/r1531274
Log:
SLING-3139 : Provide a way to schedule jobs

Added:
    sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/config/ScheduledJobConfiguration.java   (with props)
    sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/config/ScheduledJobConfigurationManager.java   (with props)
Modified:
    sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/JobBuilderImpl.java
    sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/JobSchedulerImpl.java
    sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/console/InventoryPlugin.java
    sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/console/WebConsolePlugin.java
    sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/support/ResourceHelper.java
    sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/support/ScheduleInfoImpl.java
    sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/jobs/JobBuilder.java
    sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/jobs/ScheduleInfo.java

Modified: sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/JobBuilderImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/JobBuilderImpl.java?rev=1531274&r1=1531273&r2=1531274&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/JobBuilderImpl.java (original)
+++ sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/JobBuilderImpl.java Fri Oct 11 12:38:55 2013
@@ -94,8 +94,8 @@ public class JobBuilderImpl implements J
         }
 
         @Override
-        public DayBuilder dayly(final int hour, final int minute) {
-            schedules.add(ScheduleInfoImpl.DAYLY(hour, minute));
+        public DayBuilder daily(final int hour, final int minute) {
+            schedules.add(ScheduleInfoImpl.DAILY(hour, minute));
             return this;
         }
 
@@ -119,7 +119,7 @@ public class JobBuilderImpl implements J
 
         @Override
         public DayBuilder at(int hour, int minute) {
-            schedules.add(ScheduleInfoImpl.DAYLY(hour, minute));
+            schedules.add(ScheduleInfoImpl.DAILY(hour, minute));
             return this;
         }
 

Modified: sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/JobSchedulerImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/JobSchedulerImpl.java?rev=1531274&r1=1531273&r2=1531274&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/JobSchedulerImpl.java (original)
+++ sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/JobSchedulerImpl.java Fri Oct 11 12:38:55 2013
@@ -56,7 +56,6 @@ import org.apache.sling.event.impl.suppo
 import org.apache.sling.event.impl.support.ScheduleInfoImpl;
 import org.apache.sling.event.jobs.Job;
 import org.apache.sling.event.jobs.JobBuilder;
-import org.apache.sling.event.jobs.JobUtil;
 import org.apache.sling.event.jobs.ScheduleInfo;
 import org.apache.sling.event.jobs.ScheduledJobInfo;
 import org.osgi.service.event.Event;
@@ -272,7 +271,7 @@ public class JobSchedulerImpl
                 final String name = info.getSchedulerJobId() + "-" + String.valueOf(index);
                 ScheduleOptions options = null;
                 switch ( si.getType() ) {
-                    case DAYLY:
+                    case DAILY:
                     case WEEKLY:
                     case HOURLY:
                         options = this.scheduler.EXPR(((ScheduleInfoImpl)si).getCronExpression());

Added: sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/config/ScheduledJobConfiguration.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/config/ScheduledJobConfiguration.java?rev=1531274&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/config/ScheduledJobConfiguration.java (added)
+++ sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/config/ScheduledJobConfiguration.java Fri Oct 11 12:38:55 2013
@@ -0,0 +1,78 @@
+/*
+ * 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.event.impl.jobs.config;
+
+import java.util.Map;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.ConfigurationPolicy;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.PropertyOption;
+import org.apache.felix.scr.annotations.PropertyUnbounded;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.event.impl.support.ResourceHelper;
+import org.osgi.framework.Constants;
+
+@Component(name="org.apache.sling.event.jobs.ScheduledJobConfiguration",
+        configurationFactory=true,policy=ConfigurationPolicy.REQUIRE,
+        metatype=true, label="Apache Sling Scheduled Job",
+        description="Configuration for a scheduled job.")
+@Service(value={ScheduledJobConfiguration.class})
+@Properties({
+    @Property(name=ResourceHelper.PROPERTY_SCHEDULE_NAME, label="Schedule Name", description="Unique schedule name", value=""),
+    @Property(name=ResourceHelper.PROPERTY_JOB_TOPIC, label="Job Topic", description="The topic of the scheduled job.", value=""),
+    @Property(name=ResourceHelper.PROPERTY_SCHEDULE_INFO_TYPE, label="Schedule Type", description="Define the schedule frequency.",
+        value="WEEKLY",
+        options={@PropertyOption(name="WEEKLY",value="Weekly"),
+                 @PropertyOption(name="DAILY",value="Daily"),
+                 @PropertyOption(name="HOURLY",value="Hourly")}),
+    @Property(name=ResourceHelper.PROPERTY_SCHEDULE_INFO, unbounded=PropertyUnbounded.ARRAY, value="",
+            label="Schedules", description="This value depends on the type. For a weekly schedule three numbers"
+                    + " separated by a colon must be used for each schedule, the first number specifying the day (1-7)"
+                    + " the second the hour (0-23) and the third the minute (0-59). For a daily schedule two numbers"
+                    + " separated by a colon for hour and minute must be specified and for hourly a single number"
+                    + " (0-59) for the minute is required."),
+    @Property(name=ResourceHelper.PROPERTY_SCHEDULE_SUSPENDED, boolValue=false,
+              label="Suspended", description="If this flag is set, the schedule is currently suspended (inactive)"),
+    @Property(name=Constants.SERVICE_RANKING, intValue=0, propertyPrivate=false,
+              label="Ranking", description="Configuration ranking - if there is more than one configuration"
+                    +" with the same name, the one with the highest ranking is used.")
+})
+public class ScheduledJobConfiguration {
+
+
+    private Map<String, Object> configuration;
+
+    @Activate
+    protected void activate(final Map<String, Object> configuration) {
+        this.configuration = configuration;
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        this.configuration = null;
+    }
+
+    public Map<String, Object> getConfiguration() {
+        return this.configuration;
+    }
+}

Propchange: sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/config/ScheduledJobConfiguration.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/config/ScheduledJobConfiguration.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/config/ScheduledJobConfiguration.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/config/ScheduledJobConfigurationManager.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/config/ScheduledJobConfigurationManager.java?rev=1531274&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/config/ScheduledJobConfigurationManager.java (added)
+++ sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/config/ScheduledJobConfigurationManager.java Fri Oct 11 12:38:55 2013
@@ -0,0 +1,286 @@
+/*
+ * 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.event.impl.jobs.config;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.ReferencePolicy;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.apache.sling.discovery.TopologyEvent;
+import org.apache.sling.discovery.TopologyEvent.Type;
+import org.apache.sling.discovery.TopologyEventListener;
+import org.apache.sling.event.impl.jobs.JobManagerImpl;
+import org.apache.sling.event.impl.jobs.Utility;
+import org.apache.sling.event.impl.support.ResourceHelper;
+import org.apache.sling.event.impl.support.ScheduleInfoImpl;
+import org.apache.sling.event.jobs.JobManager;
+import org.apache.sling.event.jobs.ScheduleInfo;
+import org.apache.sling.event.jobs.ScheduledJobInfo;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Component handling scheduled job OSGi configurations
+ */
+@Component
+@Service(value=TopologyEventListener.class)
+@Reference(name="config", cardinality=ReferenceCardinality.OPTIONAL_MULTIPLE,
+           policy=ReferencePolicy.DYNAMIC, referenceInterface=ScheduledJobConfiguration.class)
+public class ScheduledJobConfigurationManager implements TopologyEventListener {
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    private final AtomicBoolean isLeader = new AtomicBoolean(false);
+
+    private final Map<String, List<Schedule>> configs = new HashMap<String, List<Schedule>>();
+
+    @Reference
+    private JobManager jobManager;
+
+    @Override
+    public void handleTopologyEvent(final TopologyEvent event) {
+        if ( event.getType() == Type.TOPOLOGY_CHANGED || event.getType() == Type.TOPOLOGY_INIT ) {
+            synchronized ( this.configs ) {
+                final boolean wasLeader = this.isLeader.get();
+                this.isLeader.set(event.getNewView().getLocalInstance().isLeader());
+                if ( !wasLeader && this.isLeader.get() ) {
+                    this.updateSchedules();
+                }
+            }
+        }
+    }
+
+    /**
+     * Bind a new configuration
+     */
+    protected void bindConfig(final ScheduledJobConfiguration config, final Map<String, Object> properties) {
+        // create schedule
+        final Schedule schedule = new Schedule(config, properties);
+
+        if ( schedule.errors.size() > 0 ) {
+            logger.warn("Ignoring job schedule configuration: {}. Reason(s): {}", schedule,
+                    schedule.errors);
+            return;
+        }
+
+        synchronized ( configs ) {
+            List<Schedule> schedules = this.configs.get(schedule.scheduleName);
+            if ( schedules == null ) {
+                schedules = new ArrayList<ScheduledJobConfigurationManager.Schedule>();
+                this.configs.put(schedule.scheduleName, schedules);
+            }
+            schedules.add(schedule);
+            Collections.sort(schedules);
+            if ( this.isLeader.get() && schedules.get(0) == schedule ) {
+                if ( schedules.size() > 1 ) {
+                    this.stop(schedule.scheduleName);
+                }
+            }
+        }
+    }
+
+    /**
+     * Unbind a configuration
+     */
+    protected void unbindConfig(final ServiceReference ref) {
+        final String scheduleName = PropertiesUtil.toString(ref.getProperty(ResourceHelper.PROPERTY_SCHEDULE_NAME), null);
+        if ( scheduleName != null && scheduleName.length() > 0 ) {
+            final long serviceId = (Long)ref.getProperty(Constants.SERVICE_ID);
+            synchronized ( configs ) {
+                List<Schedule> schedules = this.configs.get(scheduleName);
+                if ( schedules != null ) {
+                    boolean isFirst = true;
+                    boolean update = false;
+                    final Iterator<Schedule> i = schedules.iterator();
+                    while ( i.hasNext() ) {
+                        final Schedule current = i.next();
+                        if ( current.serviceId == serviceId ) {
+                            if ( isFirst ) {
+                                update = true;
+                            }
+                            i.remove();
+                            break;
+                        }
+                        isFirst = false;
+                    }
+                    if ( schedules.size() == 0 ) {
+                        this.configs.remove(scheduleName);
+                    }
+                    if ( update && this.isLeader.get() ) {
+                        this.stop(scheduleName);
+                        if ( schedules.size() > 0 ) {
+                            this.start(schedules.get(0));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private void start(final Schedule s) {
+        final List<String> errors = new ArrayList<String>();
+        ((JobManagerImpl)jobManager).addScheduledJob(s.jobTopic, null, s.jobProperties, s.scheduleName, s.suspended, s.scheduleInfos, errors);
+        if ( errors.size() > 0 ) {
+            logger.error("Unable to schedule job from configuration: {} : {}", errors, s);
+        }
+    }
+
+    private void stop(final String scheduleName) {
+        final ScheduledJobInfo info = this.jobManager.getScheduledJob(scheduleName);
+        if ( info != null ) {
+            info.unschedule();
+        }
+    }
+
+    private void updateSchedules() {
+        for(final List<Schedule> schedules : this.configs.values() ) {
+            final Schedule s = schedules.get(0);
+            this.start(s);
+        }
+    }
+
+    private static final class Schedule implements Comparable<Schedule> {
+
+        public final int ranking;
+        public final long serviceId;
+        public final ScheduleInfo.ScheduleType scheduleType;
+        public final String jobTopic;
+        public final String scheduleName;
+        public final boolean suspended;
+        public final Map<String, Object> jobProperties;
+        public final List<ScheduleInfoImpl> scheduleInfos = new ArrayList<ScheduleInfoImpl>();
+
+        public final List<String> errors = new ArrayList<String>();
+
+        public Schedule(final ScheduledJobConfiguration config, final Map<String, Object> properties) {
+            final Object sr = properties.get(Constants.SERVICE_RANKING);
+            if ( sr == null || !(sr instanceof Integer)) {
+                this.ranking = 0;
+            } else {
+                this.ranking = (Integer)sr;
+            }
+            this.serviceId = (Long)properties.get(Constants.SERVICE_ID);
+            final Map<String, Object> configProperties = config.getConfiguration();
+            // type
+            final String scheduleTypeString = PropertiesUtil.toString(configProperties.get(ResourceHelper.PROPERTY_SCHEDULE_INFO_TYPE), null);
+            ScheduleInfo.ScheduleType sType = null;
+            if ( scheduleTypeString != null ) {
+                try {
+                    sType = ScheduleInfo.ScheduleType.valueOf(scheduleTypeString);
+                } catch ( final IllegalArgumentException iae) {
+                    // ignore
+                }
+            }
+            this.scheduleType = sType;
+            if ( this.scheduleType == null ) {
+                this.errors.add("No valid schedule type set: " + scheduleTypeString);
+            } else {
+                // Schedule info
+                final String[] scheduleConfigs = PropertiesUtil.toStringArray(configProperties.get(ResourceHelper.PROPERTY_SCHEDULE_INFO), null);
+                if ( scheduleConfigs != null ) {
+                    for(final String s : scheduleConfigs) {
+                        final ScheduleInfoImpl info = ScheduleInfoImpl.deserialize(this.scheduleType, s.trim());
+                        if ( info != null ) {
+                            this.scheduleInfos.add(info);
+                        }
+                    }
+                }
+                if ( this.scheduleInfos.size() == 0 || this.scheduleInfos.size() < scheduleConfigs.length ) {
+                    this.errors.add("Either no schedules or invalid schedules found: " + Arrays.toString(scheduleConfigs));
+                }
+            }
+            this.jobTopic = PropertiesUtil.toString(configProperties.get(ResourceHelper.PROPERTY_JOB_TOPIC), null);
+            this.scheduleName = PropertiesUtil.toString(configProperties.get(ResourceHelper.PROPERTY_SCHEDULE_NAME), null);
+            this.suspended = PropertiesUtil.toBoolean(configProperties.get(ResourceHelper.PROPERTY_SCHEDULE_SUSPENDED), false);
+            this.jobProperties = new HashMap<String, Object>(configProperties);
+            final Iterator<String> nameIter = this.jobProperties.keySet().iterator();
+            while ( nameIter.hasNext() ) {
+                final String name = nameIter.next();
+                if ( name.startsWith("service.")
+                     || name.startsWith("component.")
+                     || name.equals(ResourceHelper.PROPERTY_SCHEDULE_NAME)
+                     || name.equals(ResourceHelper.PROPERTY_SCHEDULE_INFO)
+                     || name.equals(ResourceHelper.PROPERTY_SCHEDULE_INFO_TYPE)
+                     || name.equals(ResourceHelper.PROPERTY_SCHEDULE_SUSPENDED)
+                     || name.equals(ResourceHelper.PROPERTY_JOB_TOPIC) ) {
+                    nameIter.remove();
+                }
+            }
+
+            if ( this.scheduleName == null || this.scheduleName.trim().length() == 0 ) {
+                this.errors.add("Schedule name missign.");
+                return;
+            }
+            final String errorMessage = Utility.checkJob(this.jobTopic, this.jobProperties);
+            if ( errorMessage != null ) {
+                this.errors.add(errorMessage);
+            }
+            for(final ScheduleInfoImpl info : this.scheduleInfos) {
+                info.check(this.errors);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "Schedule [ranking=" + ranking + ", serviceId=" + serviceId
+                    + ", scheduleType=" + scheduleType + ", jobTopic="
+                    + jobTopic + ", scheduleName=" + scheduleName
+                    + ", suspended=" + suspended + ", jobProperties="
+                    + jobProperties + ", scheduleInfos=" + scheduleInfos + ", errors=" + errors
+                    + "]";
+        }
+
+        @Override
+        public int compareTo(final Schedule o) {
+            if ( this.ranking < o.ranking ) {
+                return 1;
+            } else if (this.ranking > o.ranking ) {
+                return -1;
+            }
+            // If ranks are equal, then sort by service id in descending order.
+            return (this.serviceId < o.serviceId) ? -1 : 1;
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            if ( obj instanceof Schedule ) {
+                return ((Schedule)obj).serviceId == this.serviceId;
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return this.scheduleName.hashCode();
+        }
+    }
+}

Propchange: sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/config/ScheduledJobConfigurationManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/config/ScheduledJobConfigurationManager.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/config/ScheduledJobConfigurationManager.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/console/InventoryPlugin.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/console/InventoryPlugin.java?rev=1531274&r1=1531273&r2=1531274&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/console/InventoryPlugin.java (original)
+++ sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/console/InventoryPlugin.java Fri Oct 11 12:38:55 2013
@@ -23,6 +23,7 @@ import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
@@ -44,6 +45,8 @@ import org.apache.sling.event.impl.jobs.
 import org.apache.sling.event.jobs.JobManager;
 import org.apache.sling.event.jobs.Queue;
 import org.apache.sling.event.jobs.QueueConfiguration;
+import org.apache.sling.event.jobs.ScheduleInfo;
+import org.apache.sling.event.jobs.ScheduledJobInfo;
 import org.apache.sling.event.jobs.Statistics;
 import org.apache.sling.event.jobs.TopicStatistics;
 
@@ -184,6 +187,43 @@ public class InventoryPlugin implements 
         }
         pw.println();
 
+        pw.println("Scheduled Jobs");
+        pw.println("<table class='nicetable'><tbody>");
+        final Collection<ScheduledJobInfo> infos = this.jobManager.getScheduledJobs();
+        if ( infos.size() == 0 ) {
+            pw.print("No jobs currently scheduled");
+        } else {
+            pw.println("<tr><th>Schedule</th><th>Job Topic</th><th>Job Name</th><th>Schedule Type</th><th>Schedules</th></tr>");
+            for(final ScheduledJobInfo info : infos) {
+                pw.printf("Schedule : %s%n", info.getName());
+                pw.printf("Job Topic< : %s%n", info.getJobTopic());
+                if ( info.getJobName() != null ) {
+                    pw.printf("Schedule : %s%n", info.getJobName());
+                }
+                pw.printf("ScheduleType : %s%n", info.getSchedules().iterator().next().getType().name());
+                pw.print("Schedules : ");
+                boolean first = true;
+                for(final ScheduleInfo si : info.getSchedules() ) {
+                    if ( !first ) {
+                        pw.print(", ");
+                    }
+                    first = false;
+                    switch ( si.getType() ) {
+                    case WEEKLY : pw.printf("%s : %s:%s", si.getDayOfWeek(), si.getHourOfDay(), si.getMinuteOfHour());
+                                  break;
+                    case DAILY : pw.printf("%s:%s", si.getHourOfDay(), si.getMinuteOfHour());
+                                 break;
+                    case HOURLY : pw.printf("%s", si.getMinuteOfHour());
+                                 break;
+                    default : pw.printf("%s", si.getAt());
+                    }
+                }
+                pw.println();
+                pw.println();
+            }
+        }
+        pw.println();
+
         boolean isEmpty = true;
         for(final Queue q : this.jobManager.getQueues()) {
             isEmpty = false;

Modified: sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/console/WebConsolePlugin.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/console/WebConsolePlugin.java?rev=1531274&r1=1531273&r2=1531274&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/console/WebConsolePlugin.java (original)
+++ sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/jobs/console/WebConsolePlugin.java Fri Oct 11 12:38:55 2013
@@ -22,6 +22,7 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
+import java.util.Collection;
 import java.util.Date;
 import java.util.Dictionary;
 import java.util.Hashtable;
@@ -49,6 +50,8 @@ import org.apache.sling.event.jobs.JobMa
 import org.apache.sling.event.jobs.JobUtil;
 import org.apache.sling.event.jobs.Queue;
 import org.apache.sling.event.jobs.QueueConfiguration;
+import org.apache.sling.event.jobs.ScheduleInfo;
+import org.apache.sling.event.jobs.ScheduledJobInfo;
 import org.apache.sling.event.jobs.Statistics;
 import org.apache.sling.event.jobs.TopicStatistics;
 import org.osgi.service.event.Event;
@@ -267,6 +270,38 @@ public class WebConsolePlugin extends Ht
         pw.println("</tbody></table>");
         pw.println("<br/>");
 
+        pw.println("<p class='statline'>Scheduled Jobs</p>");
+        pw.println("<table class='nicetable'><tbody>");
+        final Collection<ScheduledJobInfo> infos = this.jobManager.getScheduledJobs();
+        if ( infos.size() == 0 ) {
+            pw.print("<tr><td colspan='5'>No jobs currently scheduled.</td></tr>");
+        } else {
+            pw.println("<tr><th>Schedule</th><th>Job Topic</th><th>Job Name</th><th>Schedule Type</th><th>Schedules</th></tr>");
+            for(final ScheduledJobInfo info : infos) {
+                pw.printf("<tr><td><b>%s</b></td><td>%s</td><td>%s</td><td>%s</td><td>",
+                        info.getName(), info.getJobTopic(), info.getJobName(), info.getSchedules().iterator().next().getType().name());
+                boolean first = true;
+                for(final ScheduleInfo si : info.getSchedules() ) {
+                    if ( !first ) {
+                        pw.print("<br/>");
+                    }
+                    first = false;
+                    switch ( si.getType() ) {
+                    case WEEKLY : pw.printf("%s : %s:%s", si.getDayOfWeek(), si.getHourOfDay(), si.getMinuteOfHour());
+                                  break;
+                    case DAILY : pw.printf("%s:%s", si.getHourOfDay(), si.getMinuteOfHour());
+                                 break;
+                    case HOURLY : pw.printf("%s", si.getMinuteOfHour());
+                                 break;
+                    default : pw.printf("%s", si.getAt());
+                    }
+                }
+                pw.print("</td></tr>");
+            }
+        }
+        pw.println("</tbody></table>");
+        pw.println("<br/>");
+
         boolean isEmpty = true;
         for(final Queue q : this.jobManager.getQueues()) {
             isEmpty = false;

Modified: sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/support/ResourceHelper.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/support/ResourceHelper.java?rev=1531274&r1=1531273&r2=1531274&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/support/ResourceHelper.java (original)
+++ sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/support/ResourceHelper.java Fri Oct 11 12:38:55 2013
@@ -59,6 +59,7 @@ public abstract class ResourceHelper {
 
     public static final String PROPERTY_SCHEDULE_NAME = "slingevent:scheduleName";
     public static final String PROPERTY_SCHEDULE_INFO = "slingevent:scheduleInfo";
+    public static final String PROPERTY_SCHEDULE_INFO_TYPE = "slingevent:scheduleInfoType";
     public static final String PROPERTY_SCHEDULE_SUSPENDED = "slingevent:scheduleSuspended";
 
     public static final String PROPERTY_JOB_ID = "slingevent:eventId";
@@ -88,6 +89,7 @@ public abstract class ResourceHelper {
         Job.PROPERTY_RESULT_MESSAGE,
         PROPERTY_SCHEDULE_INFO,
         PROPERTY_SCHEDULE_NAME,
+        PROPERTY_SCHEDULE_INFO_TYPE,
         PROPERTY_SCHEDULE_SUSPENDED
     };
 

Modified: sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/support/ScheduleInfoImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/support/ScheduleInfoImpl.java?rev=1531274&r1=1531273&r2=1531274&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/support/ScheduleInfoImpl.java (original)
+++ sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/impl/support/ScheduleInfoImpl.java Fri Oct 11 12:38:55 2013
@@ -44,8 +44,8 @@ public class ScheduleInfoImpl implements
         return new ScheduleInfoImpl(ScheduleType.WEEKLY, day, hour, minute, null);
     }
 
-    public static ScheduleInfoImpl DAYLY(final int hour, final int minute) {
-        return new ScheduleInfoImpl(ScheduleType.DAYLY, -1, hour, minute, null);
+    public static ScheduleInfoImpl DAILY(final int hour, final int minute) {
+        return new ScheduleInfoImpl(ScheduleType.DAILY, -1, hour, minute, null);
     }
 
     private final ScheduleType scheduleType;
@@ -70,6 +70,43 @@ public class ScheduleInfoImpl implements
         this.at = at;
     }
 
+    public static ScheduleInfoImpl deserialize(final ScheduleType scheduleType, final String s) {
+        final String[] parts = s.split(":");
+        if ( scheduleType == ScheduleType.WEEKLY && parts.length == 3 ) {
+            try {
+                return new ScheduleInfoImpl(scheduleType,
+                        Integer.parseInt(parts[0]),
+                        Integer.parseInt(parts[1]),
+                        Integer.parseInt(parts[2]),
+                        null);
+            } catch ( final IllegalArgumentException iae) {
+                // ignore and return null
+            }
+        } else if ( scheduleType == ScheduleType.DAILY && parts.length == 2 ) {
+            try {
+                return new ScheduleInfoImpl(scheduleType,
+                        -1,
+                        Integer.parseInt(parts[0]),
+                        Integer.parseInt(parts[1]),
+                        null);
+            } catch ( final IllegalArgumentException iae) {
+                // ignore and return null
+            }
+        } else if ( scheduleType == ScheduleType.HOURLY && parts.length == 1 ) {
+            try {
+                return new ScheduleInfoImpl(scheduleType,
+                        -1,
+                        -1,
+                        Integer.parseInt(parts[0]),
+                        null);
+            } catch ( final IllegalArgumentException iae) {
+                // ignore and return null
+            }
+        }
+
+        return null;
+    }
+
     public static ScheduleInfoImpl deserialize(final String s) {
         final String[] parts = s.split(":");
         if ( parts.length == 6 && parts[0].equals(VERSION) ) {
@@ -85,6 +122,7 @@ public class ScheduleInfoImpl implements
         }
         return null;
     }
+
     public String getSerializedString() {
         final StringBuilder sb = new StringBuilder();
         sb.append(VERSION);
@@ -132,7 +170,7 @@ public class ScheduleInfoImpl implements
 
     public void check(final List<String> errors) {
         switch ( this.scheduleType ) {
-        case DAYLY : if ( hourOfDay < 0 || hourOfDay > 23 || minuteOfHour < 0 || minuteOfHour > 59 ) {
+        case DAILY : if ( hourOfDay < 0 || hourOfDay > 23 || minuteOfHour < 0 || minuteOfHour > 59 ) {
                          errors.add("Wrong time information : " + minuteOfHour + ":" + minuteOfHour);
                      }
                      break;
@@ -158,7 +196,7 @@ public class ScheduleInfoImpl implements
         final Calendar now = Calendar.getInstance();
         switch ( this.scheduleType ) {
             case DATE : return this.at;
-            case DAYLY : final Calendar next = Calendar.getInstance();
+            case DAILY : final Calendar next = Calendar.getInstance();
                          next.set(Calendar.HOUR_OF_DAY, this.hourOfDay);
                          next.set(Calendar.MINUTE, this.minuteOfHour);
                          if ( next.before(now) ) {
@@ -187,7 +225,7 @@ public class ScheduleInfoImpl implements
      * If the job is scheduled daily or weekly, return the cron expression
      */
     public String getCronExpression() {
-        if ( this.scheduleType == ScheduleType.DAYLY ) {
+        if ( this.scheduleType == ScheduleType.DAILY ) {
             final StringBuilder sb = new StringBuilder("0 ");
             sb.append(String.valueOf(this.minuteOfHour));
             sb.append(' ');

Modified: sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/jobs/JobBuilder.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/jobs/JobBuilder.java?rev=1531274&r1=1531273&r2=1531274&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/jobs/JobBuilder.java (original)
+++ sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/jobs/JobBuilder.java Fri Oct 11 12:38:55 2013
@@ -109,7 +109,7 @@ public interface JobBuilder {
          * @param hour  Hour of the day ranging from 0 to 23.
          * @param minute Minute of the hour ranging from 0 to 59.
          */
-        DayBuilder dayly(final int hour, final int minute);
+        DayBuilder daily(final int hour, final int minute);
 
         /**
          * Schedule the job weekly, the time needs to be specified in addition.

Modified: sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/jobs/ScheduleInfo.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/jobs/ScheduleInfo.java?rev=1531274&r1=1531273&r2=1531274&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/jobs/ScheduleInfo.java (original)
+++ sling/trunk/bundles/extensions/event/src/main/java/org/apache/sling/event/jobs/ScheduleInfo.java Fri Oct 11 12:38:55 2013
@@ -32,7 +32,7 @@ public interface ScheduleInfo {
     enum ScheduleType {
         DATE,         // scheduled for a date
         HOURLY,       // scheduled hourly
-        DAYLY,        // scheduled once a day
+        DAILY,        // scheduled once a day
         WEEKLY        // scheduled once a week
     }