1
2
3
4
5
6
7
8
9
10
11 package org.astrogrid.jes.jobscheduler.impl.groovy;
12
13 import org.apache.commons.logging.Log;
14 import org.apache.commons.logging.LogFactory;
15 import org.codehaus.groovy.control.CompilationFailedException;
16
17 import groovy.lang.Script;
18
19 import java.io.IOException;
20 import java.lang.ref.SoftReference;
21 import java.util.HashMap;
22 import java.util.Iterator;
23 import java.util.Map;
24
25 /*** Container for rules.
26 * was previously storing rules in a hashmap. Need to add some extra methods on the side of this - but existing code expects to see a hashmap - hence
27 * this class extends rather than contains a hashmap.
28 *
29 * @modified optimized selection of next valid rule, by pre-compiling all triggers into a large script that evaluates to the key of the rule to fire next.
30 * so we get to replace a linear traverse and evaluate each with a computation, followed by indexing into a map.
31 * @author Noel Winstanley nw@jb.man.ac.uk 05-Nov-2004
32 *
33 */
34 public class RuleStore extends HashMap {
35 /***
36 * Commons Logger for this class
37 */
38 private static final Log logger = LogFactory.getLog(RuleStore.class);
39
40 /*** computed script that calculates id of next runnable rule */
41 protected String indexScript;
42 /***
43 * cached compiled up version of this
44 */
45 protected transient SoftReference cachedIndexScript = null;
46
47 /*** Construct a new RuleStore
48 *
49 */
50 public RuleStore() {
51 super();
52 }
53
54 /*** Construct a new RuleStore
55 * @param m
56 */
57 public RuleStore(Map m) {
58 super(m);
59 }
60
61 /*** add a rule to the rulestore */
62 public void add( Rule r) {
63 invalidateCache();
64 super.put(r.getName(),r);
65 }
66
67
68
69 /*** called whenever an update occurs, to signify cache has been invalidated.
70 *
71 */
72 private void invalidateCache() {
73
74 indexScript = null;
75 if (cachedIndexScript != null) {
76 cachedIndexScript.clear();
77 }
78 cachedIndexScript = null;
79 }
80
81 public void clear() {
82 invalidateCache();
83 super.clear();
84 }
85 public Object put(Object key, Object value) {
86 invalidateCache();
87 return super.put(key, value);
88 }
89 public void putAll(Map m) {
90 invalidateCache();
91 super.putAll(m);
92 }
93 public Object remove(Object key) {
94 invalidateCache();
95 return super.remove(key);
96 }
97 /*** find a triggered rule that can be validly executed
98 * @param stateMap
99 *
100 * @return a triggered rule, or null if no rules are currently triggered.
101 * @throws ScriptEngineException if evaluati;on of triggers fails in some way.
102 */
103 public Rule findNext(JesShell shell, ActivityStatusStore stateMap) throws ScriptEngineException {
104 if (indexScript == null) {
105 logger.info("computing index");
106 computeIndexScript();
107 }
108 Script s = null;
109 if (cachedIndexScript != null) {
110 s = (Script)cachedIndexScript.get();
111 }
112 if (s == null) {
113 logger.info("compiling up index");
114 try {
115 s = shell.compileRuleScript(this.indexScript);
116 } catch (CompilationFailedException e) {
117 logger.error("failed to compile index",e);
118 throw new ScriptEngineException("failed to compile index",e);
119 } catch (IOException e) {
120 logger.error("failed to compile index",e);
121 throw new ScriptEngineException("failed to compile index",e);
122 }
123 cachedIndexScript = new SoftReference(s);
124 }
125 String id = shell.evaluateIndex(s,stateMap);
126 if (id == null) {
127 return null;
128 }
129 Rule r = (Rule)get(id);
130 if (r == null) {
131 throw new RuleNotFoundException(id);
132 }
133 return r;
134 }
135
136 /*** generates a function with an if-clause based on each rule's trigger. */
137 protected void computeIndexScript() {
138 StringBuffer script = new StringBuffer();
139 script.append("def index() {");
140 for (Iterator i = values().iterator(); i.hasNext();) {
141 Rule r = (Rule)i.next();
142 script.append("if (\n");
143 script.append(r.getTrigger());
144 script.append("\n){\nreturn '");
145 script.append(r.getName());
146 script.append("';\n}\n");
147 }
148
149 script.append("return null;");
150 script.append("}\nindex()");
151 this.indexScript = script.toString();
152 }
153
154
155 }
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182