blob: d67a365aa77447f9c704cfc74ee815509a305503 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.ace.scheduler;
import java.util.Collections;
import java.util.Dictionary;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.osgi.framework.Constants;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.osgi.service.log.LogService;
/**
* The scheduler periodically runs tasks based on a scheduling recipe. Tasks can be added and removed using the
* <code>addRunnable</code> and <code>removeRunnable</code> methods. Recipes are supplied using configuration properties
* using the <code>updated</code> method, or are passed in the task's properties.<br>
*
* A task will be scheduled if both a <code>Runnable</code> and a <code>recipe</code> are available for it.
*/
public class Scheduler implements ManagedService {
protected final ConcurrentMap<String, SchedulerTask> m_tasks = new ConcurrentHashMap<>();
private volatile LogService m_log;
/**
* Makes sure that all tasks are indeed stopped when the scheduler is stopped.
*/
public void stop() {
for (SchedulerTask schedTask : m_tasks.values()) {
schedTask.stop();
}
m_tasks.clear();
}
/**
* Adds a new runnable to this scheduler. The runnable will be created if necessary, registered, and processed.
*
* @param name
* A name for this task.
* @param task
* A runnable to run for this task.
* @param description
* A description of the task.
* @param recipe
* Optionally, a recipe for running this task.
* @param recipeOverride
* Indicates whether or not the <code>recipe</code> passed in prevails over any recipe provided by the
* <code>Scheduler</code>'s configuration.
* @throws ConfigurationException
* When <code>recipe</code> is not <code>null</code>, and cannot be decoded into a recipe.
*/
public void addRunnable(String name, Runnable task, String description, Object recipe, boolean recipeOverride) throws ConfigurationException {
SchedulerTask schedTask = m_tasks.get(name);
if (schedTask == null) {
schedTask = new SchedulerTask(name);
m_tasks.putIfAbsent(name, schedTask);
}
schedTask.updateTask(task, description, recipe, recipeOverride);
schedTask.process();
}
/**
* Removes a runnable from this scheduler.
*
* @param name
* The name of the runnable. If the name does not indicate a valid runnable, nothing is done.
*/
public void removeRunnable(String name) {
SchedulerTask schedTask = m_tasks.get(name);
if (schedTask != null) {
try {
schedTask.updateTask(null, null, null, false);
}
catch (ConfigurationException e) {
// Will not occur; a null recipe will not cause an exception.
}
if (!schedTask.process()) {
m_tasks.remove(name);
}
}
}
/**
* Updates the configuration of the scheduler. The scheduler expects the configuration to contain recipes for
* scheduling. The key of a property should be the name identifying a task and the value should be a string
* describing the scheduling recipe for this task.
*/
public void updated(Dictionary<String, ?> properties) throws ConfigurationException {
if (properties == null) {
return;
}
// first remove all the old schedules.
for (SchedulerTask schedTask : m_tasks.values()) {
schedTask.updateConfigurationRecipe(null);
}
// then apply the new ones
properties.remove(Constants.SERVICE_PID);
List<String> keys = Collections.list(properties.keys());
for (String name : keys) {
SchedulerTask schedTask = m_tasks.get(name);
if (schedTask == null) {
schedTask = new SchedulerTask(name);
m_tasks.putIfAbsent(name, schedTask);
}
try {
schedTask.updateConfigurationRecipe(properties.get(name));
}
catch (ConfigurationException ce) {
// This is most likely an illegal recipe, caused by an config property we don't understand.
// So, no problem.
m_log.log(LogService.LOG_INFO, name + "=" + properties.get(name) + " does not look like a valid schedule recipe.");
}
}
// and remove all tasks that now have no schedule or runnable
Iterator<String> i = m_tasks.keySet().iterator();
while (i.hasNext()) {
SchedulerTask schedTask = m_tasks.get(i.next());
if (!schedTask.process()) {
i.remove();
}
}
}
}