View Javadoc
1   package de.fhdw.wtf.common.task;
2   
3   import java.util.ArrayList;
4   import java.util.Collection;
5   import java.util.Iterator;
6   import java.util.concurrent.Callable;
7   
8   import de.fhdw.wtf.common.exception.walker.CyclicDependencyException;
9   import de.fhdw.wtf.common.exception.walker.DependencyException;
10  import de.fhdw.wtf.common.task.result.ExceptionalTaskResult;
11  import de.fhdw.wtf.common.task.result.OKTaskResult;
12  import de.fhdw.wtf.common.task.result.TaskResult;
13  import de.fhdw.wtf.common.task.result.visitor.TaskResultVisitor;
14  
15  /**
16   * A dependency task is a piece of work that may have dependencies on other tasks completion and on which other tasks
17   * may depend on.
18   * 
19   * On successful completion this task checks possible starts of tasks depending on it and submit it.
20   * 
21   */
22  public abstract class DependencyTask implements Task, Callable<TaskResult> {
23  	
24  	/**
25  	 * Tasks that depend on the successful execution of this task.
26  	 */
27  	private final DependencyList dependencies;
28  	
29  	/**
30  	 * All dependency-tasks must have exited successfully, before this task may start its work.
31  	 */
32  	private final Collection<DependencyTask> observers;
33  	
34  	/**
35  	 * Instance of the task executor managing the delayed start of a task, depending on defined thread resources.
36  	 */
37  	private final TaskExecutor taskmanager;
38  	
39  	/**
40  	 * Constructor. This constructor introduces the new task directly to the task executor!
41  	 * 
42  	 * @see TaskExecutor#introduce(DependencyTask)
43  	 * 
44  	 * @param taskmanager
45  	 *            task executor, that organizes concurrent execution of tasks
46  	 */
47  	public DependencyTask(final TaskExecutor taskmanager) {
48  		this.dependencies = DependencyList.create(this);
49  		this.observers = new ArrayList<>();
50  		this.taskmanager = taskmanager;
51  		taskmanager.introduce(this);
52  	}
53  	
54  	@Override
55  	public String toString() {
56  		return this.getClass().getSimpleName();
57  	}
58  	
59  	@Override
60  	public final void start() {
61  		System.out.println("[submitted] " + this);
62  		this.taskmanager.submit(this);
63  	}
64  	
65  	/**
66  	 * Adds a new task as dependency for the current task.
67  	 * 
68  	 * @param a
69  	 *            must have completed successfully before this task starts its work
70  	 * @throws CyclicDependencyException
71  	 *             when cyclic dependencies are added
72  	 */
73  	public void addDependency(final DependencyTask a) throws CyclicDependencyException {
74  		
75  		if (this.contains(a)) {
76  			throw CyclicDependencyException.create(a.toString());
77  		} else {
78  			this.getDependencies().addDependency(a);
79  			a.register(this);
80  		}
81  		
82  	}
83  	
84  	/**
85  	 * Adds a new observer on the successful execution of this task.
86  	 * 
87  	 * @param a
88  	 *            wants to be notified
89  	 */
90  	private void register(final DependencyTask a) {
91  		this.observers.add(a);
92  	}
93  	
94  	/**
95  	 * This operation must be called by its owning task, when it has successfully ended its work.
96  	 */
97  	synchronized void notifyObservers() {
98  		final Iterator<DependencyTask> i = this.observers.iterator();
99  		while (i.hasNext()) {
100 			final DependencyTask current = i.next();
101 			current.updateFinished(this);
102 		}
103 	}
104 	
105 	/**
106 	 * Updates the list of dependencies because of <code>task</code> exited successful. It is no longer a dependency to
107 	 * be fulfilled.
108 	 * 
109 	 * @param task
110 	 *            successful executed task
111 	 */
112 	private synchronized void updateFinished(final DependencyTask task) {
113 		final boolean removed = this.getDependencies().remove(task);
114 		if (!removed) {
115 			throw new DependencyException(this.toString() + ": Task <" + task.toString()
116 					+ "> is not registered as dependency or already removed!!");
117 		}
118 		if (this.getDependencies().isEmpty()) {
119 			this.start();
120 		}
121 	}
122 	
123 	/**
124 	 * This tests whether the given task is a part of this task.
125 	 * 
126 	 * @param a
127 	 *            task to be compared with this task
128 	 * @return true, if a is a part of this task
129 	 */
130 	public boolean contains(final DependencyTask a) {
131 		return this.equals(a) || this.containsTransitive(a);
132 	}
133 	
134 	/**
135 	 * This tests whether the given task is a transitive part of this task.
136 	 * 
137 	 * @param a
138 	 *            task to be compared with this task
139 	 * @return true, if a is a transitive part of this task
140 	 */
141 	public abstract boolean containsTransitive(DependencyTask a);
142 	
143 	@Override
144 	public final TaskResult call() {
145 		System.out.println("[started] " + this);
146 		final TaskResult result = this.doWork();
147 		System.out.println("[ended] " + this);
148 		result.accept(new TaskResultVisitor() {
149 			@Override
150 			public void handleOkTaskResult(final OKTaskResult okTaskResult) {
151 				DependencyTask.this.notifyObservers();
152 			}
153 			
154 			@Override
155 			public void handleExceptionalTaskResult(final ExceptionalTaskResult exceptionalTaskResult) {
156 				// Nothing
157 			}
158 		});
159 		
160 		return result;
161 	}
162 	
163 	/**
164 	 * The tasks processes its work and produces a result in the end.
165 	 * 
166 	 * @return result of this task
167 	 */
168 	public abstract TaskResult doWork();
169 	
170 	/**
171 	 * Gets a list of dependencies - tasks that are required to be run before this task starts its work.
172 	 * 
173 	 * @return list of dependencies
174 	 */
175 	protected DependencyList getDependencies() {
176 		return this.dependencies;
177 	}
178 }