com.almende.eve.scheduler.RunnableSchedulerFactory Maven / Gradle / Ivy
package com.almende.eve.scheduler;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import com.almende.eve.agent.AgentFactory;
import com.almende.eve.agent.annotation.Sender;
import com.almende.eve.rpc.RequestParams;
import com.almende.eve.rpc.jsonrpc.JSONRPCException;
import com.almende.eve.rpc.jsonrpc.JSONRequest;
import com.almende.eve.state.State;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
/**
* Documentation on Scheduling:
* http://docs.oracle.com/javase/1.5.0/docs/api/java
* /util/concurrent/ScheduledExecutorService.html
* http://www.javapractices.com/topic/TopicAction.do?Id=54
*/
public class RunnableSchedulerFactory implements SchedulerFactory {
private State state = null;
private String stateId = null;
private AgentFactory agentFactory = null;
private long count = 0;
private ScheduledExecutorService scheduler = Executors
.newScheduledThreadPool(10);
// {agentId: {taskId: task}}
private final Map> allTasks = new ConcurrentHashMap>();
private Logger logger = Logger.getLogger(this.getClass().getSimpleName());
/**
* This constructor is called when constructed by the AgentFactory
*
* @param agentFactory
* @param params
*/
public RunnableSchedulerFactory(AgentFactory agentFactory,
Map params) {
this(agentFactory, (params != null) ? (String) params.get("id") : null);
}
public RunnableSchedulerFactory(AgentFactory agentFactory, String id) {
this.agentFactory = agentFactory;
this.stateId = id;
init();
}
/**
* Perform initialization tasks.
*/
private void init() {
initState();
initTasks();
}
/**
* Initialize a state for the service, to persist the parameters of all open
* connections.
*
* @param id
*/
private void initState() {
// set a state for the service, where the service can
// persist its state.
if (stateId == null) {
stateId = "_runnablescheduler";
logger.info("No id specified for RunnableSchedulerFactory. "
+ "Using '" + stateId + "' as id.");
}
try {
// TODO: dangerous to use a generic state (can possibly conflict
// with the id a regular agent)
state = agentFactory.getStateFactory().get(stateId);
if (state == null) {
state = agentFactory.getStateFactory().create(stateId);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Get a scheduler for a specific agent
*
* @param agentId
*/
@Override
public Scheduler getScheduler(String agentId) {
return new RunnableScheduler(agentId);
}
@Override
public void destroyScheduler(String agentId) {
allTasks.remove(agentId);
}
/**
* Create a new unique taskId
*
* @return taskId
*/
private synchronized String createTaskId() {
count++;
long id = count;
return Long.toString(id);
}
// TODO: make the class Task serializable (and auto-restart when
// initializing again?)
private class Task {
private String agentId = null;
private String taskId = null;
private JSONRequest request = null;
private DateTime timestamp = null;
private ScheduledFuture> future = null;
/**
* Schedule a task
*
* @param agentId
* Id of the agent to be requested
* @param request
* A JSONRequest with method and params
* @param delay
* The delay in milliseconds
*/
Task(final String agentId, final JSONRequest request, long delay) {
// TODO: throw exceptions when agentId, request are null or delay <
// 0
this.agentId = agentId;
this.request = request;
start(delay);
}
/**
* Schedule a task
*
* @param params
* A Map with parameters: agentId, request (stringified
* JSONRequest), and timestamp (ISOdate)
* @throws IOException
* @throws JSONRPCException
* @throws JsonMappingException
* @throws JsonParseException
*/
Task(Map params) throws JsonParseException,
JsonMappingException, JSONRPCException, IOException {
// TODO: throw exceptions when agentId, request are null or delay <
// 0
agentId = params.get("agentId");
request = new JSONRequest(params.get("request"));
timestamp = new DateTime(params.get("timestamp"));
long delay = 0;
if (timestamp.isAfterNow()) {
delay = new Interval(DateTime.now(), timestamp)
.toDurationMillis();
}
start(delay);
}
/**
* Start task
*
* @param delay
* delay in milliseconds
*/
private void start(long delay) {
// create the task
timestamp = DateTime.now().plus(delay);
taskId = createTaskId();
future = scheduler.schedule(new Runnable() {
@Override
public void run() {
try {
RequestParams params = new RequestParams();
params.put(Sender.class, null); // TODO: provide itself
agentFactory.invoke(agentId, request, params);
} catch (Exception e) {
e.printStackTrace();
} finally {
remove();
}
}
}, delay, TimeUnit.MILLISECONDS);
// persist the task
store();
}
public String getTaskId() {
return taskId;
}
public void cancel() {
if (future != null) {
boolean mayInterruptIfRunning = false;
future.cancel(mayInterruptIfRunning);
}
remove();
}
/**
* Store this task in the global task list
*/
private void store() {
Map tasks = allTasks.get(agentId);
if (tasks == null) {
synchronized (allTasks) {
tasks = allTasks.get(agentId);
if (tasks == null) {
tasks = new ConcurrentHashMap();
allTasks.put(agentId, tasks);
}
}
}
tasks.put(taskId, this);
storeTasks();
}
/**
* Remove this task from the global task list
*/
private void remove() {
Map tasks = allTasks.get(agentId);
if (tasks != null) {
tasks.remove(taskId);
if (tasks.size() == 0) {
synchronized (tasks) {
synchronized (allTasks) {
if (tasks.size() == 0) {
allTasks.remove(agentId);
}
}
}
}
storeTasks();
}
}
public Map getParams() {
Map params = new HashMap();
params.put("agentId", agentId);
params.put("request", request.toString());
params.put("timestamp", timestamp.toString());
return params;
}
}
/**
* The RunnableSchedular class is the interface which the agents can
* interact with. It can only be instantiated by the factory using the
* method getSchedular(agentId).
*/
public class RunnableScheduler implements Scheduler {
private RunnableScheduler(String agentId) {
this.agentId = agentId;
}
/**
* Schedule a task
*
* @param request
* A JSONRequest with method and params
* @param delay
* The delay in milliseconds
* @return taskId
*/
public String createTask(JSONRequest request, long delay) {
Task task = new Task(agentId, request, delay);
return task.getTaskId();
}
/**
* Cancel a scheduled task by its id
*
* @param taskId
*/
public void cancelTask(String taskId) {
Map tasks = allTasks.get(agentId);
if (tasks != null) {
Task task = tasks.get(taskId);
if (task != null) {
task.cancel();
}
}
}
/**
* Retrieve a list with all scheduled tasks
*
* @return taskIds
*/
@Override
public synchronized Set getTasks() {
Map tasks = allTasks.get(agentId);
if (tasks != null) {
return tasks.keySet();
}
return new HashSet();
}
private String agentId = null;
}
/**
* load scheduled, persisted tasks
*/
// TODO: storing all running tasks in one state file is quite a bottleneck
// and not scalable!
private void initTasks() {
int taskCount = 0;
int failedTaskCount = 0;
try {
@SuppressWarnings("unchecked")
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy