View Javadoc

1   /*
2    Copyright (C) 2006 Grid Systems, S.A.
3   
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8   
9    This library is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   Lesser General Public License for more details.
13  
14   You should have received a copy of the GNU Lesser General Public
15   License along with this library; if not, write to the Free Software
16   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
17  */
18  package com.gridsystems.nextgrid.api.pom;
19  
20  import java.net.URI;
21  import java.util.ArrayList;
22  import java.util.Collections;
23  import java.util.HashMap;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.PriorityQueue;
27  
28  import nextgrid.api.env.Prioritiser;
29  import nextgrid.api.env.ProcessEnvironment;
30  import nextgrid.api.pom.AbstractProcess;
31  import nextgrid.api.pom.ControlProcess;
32  import nextgrid.api.pom.Process;
33  import nextgrid.api.pom.ProcessException;
34  import nextgrid.api.pom.Reference;
35  
36  /**
37   * Control Process.
38   * <p>
39   * A control process is a process container that adds implicit execution flow
40   * constraints to its children processes. These constraints may be implemented
41   * through dependencies among the processes.
42   * <p>
43   * In addition to this, a control process can introduce "hints" that the
44   * enactor may use to drive the evaluation process. For example, a conditional
45   * control structure may instruct the enactor to only evaluate a subset of its
46   * branches for considering it as valid.
47   *
48   * @author Rodrigo Ruiz
49   */
50  public abstract class ControlProcessImpl extends ProcessImpl
51    implements ControlProcess {
52  
53    /**
54     * Collection of children processes. As children processes will, at least,
55     * be ranked and sorted at some point of the workflow life-cycle, this
56     * collection must be implemented as a list.
57     */
58    private List<Process> children;
59  
60    /**
61     * Process local variables.
62     */
63    private Map<String, Reference<?>> localVars;
64  
65    /**
66     * Max number of children supported.
67     */
68    private final int maxChildren;
69  
70    /**
71     * Memoization flag.
72     */
73    private boolean memoize;
74  
75    /**
76     * Creates a new instance.
77     *
78     * @param maxChildren The max number of supported children processes
79     */
80    protected ControlProcessImpl(int maxChildren) {
81      super();
82      this.maxChildren = maxChildren;
83      this.children = new ArrayList<Process>();
84      this.localVars = new HashMap<String, Reference<?>>();
85    }
86  
87    /**
88     * {@inheritDoc}
89     */
90    public final Process findProcessById(URI id) {
91      if (id == null) {
92        return null;
93      } else if (this.getId().equals(id)) {
94        return this;
95      } else {
96        for (Process child : this.getChildren()) {
97          Process found = child.findProcessById(id);
98          if (found != null) {
99            return found;
100         }
101       }
102       return null;
103     }
104   }
105 
106   /**
107    * {@inheritDoc}
108    */
109   public final void prioritise(ProcessEnvironment env, PriorityQueue<Process> queue)
110     throws ProcessException {
111 
112     // Give a chance to global prioritisers
113     Prioritiser prioritiser = env.getPrioritiserFor(this.getId());
114     if (prioritiser != null) {
115       prioritiser.prioritise(this);
116     }
117 
118     for (Process p : getChildren()) {
119       p.prioritise(env, queue);
120     }
121   }
122 
123   /**
124    * {@inheritDoc}
125    */
126   public void discover(ProcessEnvironment env) throws ProcessException {
127     for (Process p : getChildren()) {
128       p.discover(env);
129     }
130   }
131 
132   /**
133    * {@inheritDoc}
134    */
135   @Override
136   public final void doEvaluate(ProcessEnvironment env) throws ProcessException {
137     discover(env);
138     PriorityQueue<Process> queue = prioritise(env);
139 
140     Process p = queue.poll();
141     while (p != null) {
142       p.evaluate(env);
143       if (p instanceof AbstractProcess) {
144         ENACTOR_LOG.debug("Prioritising...");
145         Process selected = ((AbstractProcess)p).getSelected();
146         if (selected != null) {
147           selected.prioritise(env, queue);
148         }
149         ENACTOR_LOG.debug("Prioritised");
150       }
151       p = queue.poll();
152     }
153   }
154 
155   /**
156    * {@inheritDoc}
157    */
158   public final boolean isMemoizeActive() {
159     return this.memoize;
160   }
161 
162   /**
163    * {@inheritDoc}
164    */
165   public final void setMemoizeActive(boolean memoizeActive) {
166     this.memoize = memoizeActive;
167   }
168 
169   // ---------------------------------------------------------------------------
170   // Child process management
171   // ---------------------------------------------------------------------------
172 
173   /**
174    * Gets the processes in the children list.
175    *
176    * @return An array of child processes
177    */
178   public final Process[] getChildren() {
179     synchronized (children) {
180       return children.toArray(new Process[children.size()]);
181     }
182   }
183 
184   /**
185    * Gets a child process by its index.
186    *
187    * @param index The child process position
188    * @return The process
189    */
190   public final Process getChildren(int index) {
191     synchronized (children) {
192       if (index >= children.size()) {
193         return null;
194       } else {
195         return children.get(index);
196       }
197     }
198   }
199 
200   /**
201    * Gets the number of child processes.
202    *
203    * @return the number of child processes
204    */
205   public final int getChildCount() {
206     synchronized (children) {
207       return children.size();
208     }
209   }
210 
211   /**
212    * Sets the size of the children list to a maximum of <tt>count</tt> items.
213    * <p>
214    * It correctly removes all items from position <tt>count</tt> to the end
215    * of the list. If the list is already smaller than <tt>count</tt>, it does
216    * nothing.
217    *
218    * @param count The max number of elements to maintain in the children list
219    */
220   public final void trim(int count) {
221     synchronized (this.children) {
222       int last = this.children.size() - 1;
223       while (last >= count) {
224         this.children.remove(last--);
225       }
226     }
227   }
228 
229   /**
230    * Sets the list of child processes for this process instance.
231    *
232    * @param children An array of processes
233    */
234   public final void setChildren(Process... children) {
235     synchronized (this.children) {
236       int pos = 0;
237       for (Process item : children) {
238         if (pos == this.children.size()) {
239           addChildren(pos++, item);
240         } else {
241           setChildren(pos++, item);
242         }
243       }
244       trim(pos);
245     }
246   }
247 
248   /**
249    * Sets a child process.
250    *
251    * @param index The position in the list
252    * @param child The process to set
253    * @throws UnsupportedOperationException
254    *         If the process does not support children
255    */
256   public final void setChildren(int index, Process child) {
257     if (maxChildren == 0) {
258       throw new UnsupportedOperationException("Children not supported");
259     }
260 
261     synchronized (children) {
262       if (maxChildren < 0 || index >= maxChildren) {
263         throw new IndexOutOfBoundsException("Bad index: " + index);
264       }
265 
266       Process previous;
267       if (index >= children.size()) {
268         children.add(child);
269         previous = null;
270       } else {
271         previous = children.set(index, child);
272       }
273 
274       if (child != null) {
275         child.setParent(this);
276       }
277       if (previous != null) {
278         if (previous.getParent() instanceof ControlProcess) {
279           ((ControlProcess)previous.getParent()).removeChild(previous);
280         }
281         previous.setParent(this);
282       }
283     }
284   }
285 
286   /**
287    * Inserts the specified element at the specified position in this list.
288    * Shifts the element currently at that position (if any) and any subsequent
289    * elements to the right (adds one to their indices).<p>
290    *
291    * @param  index
292    *         index at which the specified element is to be inserted.
293    *
294    * @param  child
295    *         element to be inserted.
296    *
297    * @throws UnsupportedOperationException
298    *         if the <tt>add</tt> method is not supported by this list.
299    *
300    * @throws ClassCastException
301    *         if the class of the specified element prevents it from being added
302    *         to this list.
303    *
304    * @throws IllegalArgumentException
305    *         if some aspect of the specified element prevents it from being
306    *         added to this list.
307    *
308    * @throws IndexOutOfBoundsException
309    *         index is out of range (<tt>index &lt; 0 || index &gt; size()</tt>).
310    */
311   public final void addChildren(int index, Process child) {
312     if (maxChildren == 0) {
313       throw new UnsupportedOperationException("Children not supported");
314     } else if (maxChildren > 0 && index != getChildCount()) {
315       // Insertions not allowed when children size is fixed
316       throw new IndexOutOfBoundsException("Bad index: " + index);
317     }
318 
319     synchronized (children) {
320       children.add(index, child);
321 
322       if (child != null) {
323         if (child.getParent() instanceof ControlProcess) {
324           ((ControlProcess)child.getParent()).removeChild(child);
325         }
326         child.setParent(this);
327       }
328     }
329   }
330 
331   /**
332    * Adds a set of processes to the child list.
333    *
334    * @param processes The processes to add
335    */
336   public final void addChildren(Process... processes) {
337     int pos = this.getChildCount();
338     for (Process item : processes) {
339       addChildren(pos++, item);
340     }
341   }
342 
343   /**
344    * Removes the element at the specified position in this list.
345    * Shifts any subsequent elements to the left (subtracts one from their
346    * indices). Returns the element that was removed from the list.
347    *
348    * @param  index
349    *         the index of the element to remove
350    *
351    * @return the element previously at the specified position
352    *
353    * @throws UnsupportedOperationException
354    *         if the <tt>remove</tt> method is not supported by this list.
355    *
356    * @throws IndexOutOfBoundsException
357    *         if the specified index is out of range (<tt>index &lt; 0 ||
358    *         index &gt;= size()</tt>).
359    */
360   public final Process removeChild(int index) {
361     if (maxChildren == 0) {
362       throw new UnsupportedOperationException("Children not supported");
363     } else if (maxChildren > 0 && index != getChildCount() - 1) {
364       // Removes not generally allowed when children size is fixed
365       throw new IndexOutOfBoundsException("Bad index: " + index);
366     }
367 
368     Process removed;
369     synchronized (children) {
370       removed = children.remove(index);
371       removed.setParent(null);
372     }
373 
374     return removed;
375   }
376 
377   /**
378    * Removes a process instance from the children list.
379    *
380    * @param child The process to remove
381    * @return <tt>true</tt> if it is removed
382    */
383   public final boolean removeChild(Process child) {
384     synchronized (children) {
385       int pos = children.indexOf(child);
386       if (pos == -1) {
387         return false;
388       } else {
389         removeChild(pos);
390         return true;
391       }
392     }
393   }
394 
395   /**
396    * {@inheritDoc}
397    */
398   public final Reference<?> getLocalVar(String name) {
399     return this.localVars.get(name);
400   }
401 
402   /**
403    * {@inheritDoc}
404    */
405   public final void setLocalVar(String name, Reference<?> value) {
406     this.localVars.put(name, value);
407   }
408 
409   /**
410    * {@inheritDoc}
411    */
412   public final Map<String, Reference<?>> getLocalVars() {
413     return Collections.unmodifiableMap(localVars);
414   }
415 
416   /**
417    * {@inheritDoc}
418    */
419   @Override public Process copy() {
420     ControlProcessImpl copy = (ControlProcessImpl)super.copy();
421 
422     copy.children = new ArrayList<Process>();
423     for (Process p : children) {
424       copy.children.add(p.copy());
425     }
426 
427     return copy;
428   }
429 
430   /**
431    * Resets all child processes.
432    * <p>
433    * This method can be used by loop constructs to prepare each iteration.
434    */
435   @Override protected final void resetChildren() {
436     for (Process p : this.getChildren()) {
437       p.reset();
438     }
439   }
440 }