package jadex.commons.service.execution; import jadex.commons.Future; import jadex.commons.ICommand; import jadex.commons.IFuture; import jadex.commons.collection.SCollection; import jadex.commons.concurrent.Executor; import jadex.commons.concurrent.IExecutable; import jadex.commons.concurrent.IResultListener; import jadex.commons.service.BasicService; import jadex.commons.service.IServiceProvider; import jadex.commons.service.SServiceProvider; import jadex.commons.service.threadpool.IThreadPoolService; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * The synchronous execution service that executes all tasks in zero to one thread. */ public class SyncExecutionService extends BasicService implements IExecutionService { //-------- attributes -------- /** The queue of tasks to be executed. */ protected Set queue; /** The executor. */ protected Executor executor; /** The idle commands. */ protected Set idlecommands; /** Flag, indicating if executor is running. */ protected boolean running; /** The shutdown flag. */ protected boolean shutdown; /** The current task. */ protected IExecutable task; /** Flag that indicates that the current task has been removed. */ protected IExecutable removedtask; /** The removed listeners. */ protected List removedfut; /** The stop listener. */ protected IResultListener stoplistener; /** The provider. */ protected IServiceProvider provider; //-------- constructors -------- /** * Create a new synchronous executor service. */ public SyncExecutionService(IServiceProvider provider) { this(provider, null); } /** * Create a new synchronous executor service. */ public SyncExecutionService(IServiceProvider provider, Map properties) { super(provider.getId(), IExecutionService.class, properties); this.provider = provider; this.running = false; this.queue = SCollection.createLinkedHashSet(); this.removedfut = new ArrayList(); } //-------- methods -------- /** * Execute a task. Triggers the task to * be executed in future. * @param task The task to execute. */ public void execute(IExecutable task) { if(shutdown) return; // System.out.println("execute called: "+task); boolean added; synchronized(this) { // new RuntimeException().printStackTrace(System.out); added = queue.add(task); // System.out.println("Task added: "+queue); } // On change, wake up the main executor for executing tasks if(added) { if(running) executor.execute(); } } /** * Cancel a task. Triggers the task to * be not executed in future. * @param task The task to execute. */ public synchronized IFuture cancel(IExecutable task) { Future ret = new Future(); if(!isValid()) { ret.setException(new RuntimeException("Shutting down.")); } else { // Remove from scheduled tasks. synchronized(this) { // Is current task removed if(this.task == task) { removedtask = task; removedfut.add(ret); } else { queue.remove(task); ret.setResult(null); } } } return ret; } /** * Get the currently running or waiting tasks. */ public synchronized IExecutable[] getTasks() { return (IExecutable[])queue.toArray(new IExecutable[queue.size()]); } /** * Test if the service is valid. * @return True, if service can be used. */ public boolean isValid() { return running && !shutdown; } /** * Start the executor service. * Resumes all tasks. */ public synchronized IFuture startService() { final Future ret = new Future(); if(shutdown) { ret.setResult(null); return ret; } SServiceProvider.getService(provider, IThreadPoolService.class).addResultListener(new IResultListener() { public void resultAvailable(Object source, Object result) { executor = new Executor((IThreadPoolService)result, new IExecutable() { public boolean execute() { // Perform one task a time. // assert task==null; synchronized(SyncExecutionService.this) { if(running && !queue.isEmpty()) { // Hack!!! Is there a better way to get first element from queue without creating iterator? Iterator iterator = queue.iterator(); task = (IExecutable)iterator.next(); iterator.remove(); } } if(task!=null) { boolean again = false; try { again = task.execute(); } catch(Exception e) { System.out.println("Exception during executing task: "+task); e.printStackTrace(); } synchronized(SyncExecutionService.this) { // assert task!=null; if(removedtask==null) { if(again && running) { queue.add(task); } } else if(removedtask==task) { removedtask = null; for(int i=0; i<removedfut.size(); i++) ((Future)removedfut.get(i)).setResult(null); removedfut.clear(); } else { throw new RuntimeException("Removedtask!=task: "+task+" "+removedtask); } task = null; } } // When no more executables, inform idle commands. boolean perform = false; synchronized(SyncExecutionService.this) { if(running && queue.isEmpty()) { perform = idlecommands!=null; } } if(perform) { // System.out.println("Idle"); Iterator it = idlecommands.iterator(); while(it.hasNext()) { ((ICommand)it.next()).execute(null); } } // Perform next task when queue is not empty and service is running. synchronized(SyncExecutionService.this) { // todo: extract call from synchronized block if(stoplistener!=null) { stoplistener.resultAvailable(this, null); stoplistener = null; } return running && !queue.isEmpty(); } } }); ret.setResult(SyncExecutionService.this); } public void exceptionOccurred(Object source, Exception exception) { ret.setException(exception); } }); running = true; // Wake up the main executor for executing tasks executor.execute(); return ret; } /** * Shutdown the executor service. */ public synchronized IFuture shutdownService() { if(!isValid()) // if(!running || shutdown) { Future ret = new Future(); ret.setException(new RuntimeException("Not running.")); return ret; } this.running = false; this.shutdown = true; IFuture ret = executor.shutdown(); queue = null; return ret; } /** * Test if the executor is currently idle. * @return True, if idle. */ public synchronized boolean isIdle() { //System.out.println(running+" "+queue); //return running && queue.isEmpty(); return queue.isEmpty(); } /** * Add a command to be executed whenever the executor * is idle (i.e. no executables running). */ public void addIdleCommand(ICommand command) { if(idlecommands==null) { synchronized(this) { if(idlecommands==null) { idlecommands = SCollection.createLinkedHashSet(); } } } idlecommands.add(command); } /** * Remove a previously added idle command. */ public void removeIdleCommand(ICommand command) { if(idlecommands!=null) idlecommands.remove(command); } }