|
|||||||||||||||||||
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover | |||||||||||||||||||
Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
Orderer.java | 100% | 97.3% | 100% | 98.3% |
|
1 |
// Copyright 2004 The Apache Software Foundation
|
|
2 |
//
|
|
3 |
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4 |
// you may not use this file except in compliance with the License.
|
|
5 |
// You may obtain a copy of the License at
|
|
6 |
//
|
|
7 |
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8 |
//
|
|
9 |
// Unless required by applicable law or agreed to in writing, software
|
|
10 |
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12 |
// See the License for the specific language governing permissions and
|
|
13 |
// limitations under the License.
|
|
14 |
|
|
15 |
package org.apache.hivemind.order;
|
|
16 |
|
|
17 |
import java.util.ArrayList;
|
|
18 |
import java.util.Collections;
|
|
19 |
import java.util.HashMap;
|
|
20 |
import java.util.Iterator;
|
|
21 |
import java.util.List;
|
|
22 |
import java.util.Map;
|
|
23 |
|
|
24 |
import org.apache.commons.logging.Log;
|
|
25 |
import org.apache.commons.logging.LogFactory;
|
|
26 |
import org.apache.hivemind.ApplicationRuntimeException;
|
|
27 |
import org.apache.hivemind.ErrorHandler;
|
|
28 |
import org.apache.hivemind.HiveMind;
|
|
29 |
import org.apache.hivemind.util.StringUtils;
|
|
30 |
|
|
31 |
/**
|
|
32 |
* Used to order objects into an "execution" order. Each object must have a name. It may specify a
|
|
33 |
* list of pre-requisites and a list of post-requisites.
|
|
34 |
*
|
|
35 |
* @author Howard Lewis Ship
|
|
36 |
*/
|
|
37 |
public class Orderer |
|
38 |
{ |
|
39 |
private final Log _log;
|
|
40 |
private final ErrorHandler _errorHandler;
|
|
41 |
private final String _objectType;
|
|
42 |
private List _orderingsList = null; |
|
43 |
private Map _orderingsMap = null; |
|
44 |
private Map _nodeMap = null; |
|
45 |
private Node _leader;
|
|
46 |
private Node _trailer;
|
|
47 |
|
|
48 |
/**
|
|
49 |
* Creates an instance using <code>org.apache.hivemind.order.Orderer</code> as the Log.
|
|
50 |
*/
|
|
51 | 10 |
public Orderer(ErrorHandler errorHandler, String objectType)
|
52 |
{ |
|
53 | 10 |
this(LogFactory.getLog(Orderer.class), errorHandler, objectType); |
54 |
} |
|
55 |
|
|
56 |
/**
|
|
57 |
* Creates a new instance, but directs all debug and error logging output to the provided log.
|
|
58 |
*
|
|
59 |
* @param log
|
|
60 |
* Used for logging any errors
|
|
61 |
* @param objectType
|
|
62 |
* user presentable name for the type of object to be ordered; used in some error
|
|
63 |
* messages
|
|
64 |
*/
|
|
65 | 33 |
public Orderer(Log log, ErrorHandler errorHandler, String objectType)
|
66 |
{ |
|
67 | 33 |
_log = log; |
68 | 33 |
_errorHandler = errorHandler; |
69 | 33 |
_objectType = objectType; |
70 |
|
|
71 | 33 |
HiveMind.checkNullParameter("log", log);
|
72 | 33 |
HiveMind.checkNullParameter("errorHandler", errorHandler);
|
73 | 33 |
HiveMind.checkNullParameter("objectType", objectType);
|
74 |
} |
|
75 |
|
|
76 |
/**
|
|
77 |
* Adds a new object. All invocations of {@link #add(Object, String, String, String)}should
|
|
78 |
* occur before invoking {@link #getOrderedObjects()}.
|
|
79 |
*
|
|
80 |
* @param object
|
|
81 |
* an object to be sorted into order based on prereqs and postreqs
|
|
82 |
* @param name
|
|
83 |
* a unique name for the
|
|
84 |
* @param prereqs
|
|
85 |
* a comma-separated list of the names of objects that should precede this object in
|
|
86 |
* the list (or null)
|
|
87 |
* @param postreqs
|
|
88 |
* a comma-separated list of the names of objects that should follow this object in
|
|
89 |
* the list (or null)
|
|
90 |
*/
|
|
91 | 77 |
public void add(Object object, String name, String prereqs, String postreqs) |
92 |
{ |
|
93 | 77 |
if (_orderingsMap == null) |
94 |
{ |
|
95 | 32 |
_orderingsMap = new HashMap();
|
96 | 32 |
_orderingsList = new ArrayList();
|
97 |
} |
|
98 |
|
|
99 | 77 |
ObjectOrdering o = getOrderable(name); |
100 |
|
|
101 | 77 |
if (o != null) |
102 |
{ |
|
103 | 1 |
_errorHandler.error(_log, OrdererMessages.duplicateName(_objectType, name, object, o |
104 |
.getObject()), HiveMind.getLocation(object), null);
|
|
105 |
|
|
106 | 1 |
return;
|
107 |
} |
|
108 |
|
|
109 | 76 |
o = new ObjectOrdering(object, name, prereqs, postreqs);
|
110 |
|
|
111 | 76 |
_orderingsMap.put(name, o); |
112 | 76 |
_orderingsList.add(o); |
113 |
} |
|
114 |
|
|
115 | 193 |
private ObjectOrdering getOrderable(String name)
|
116 |
{ |
|
117 | 193 |
return (ObjectOrdering) _orderingsMap.get(name);
|
118 |
} |
|
119 |
|
|
120 |
/**
|
|
121 |
* Uses the information provided by {@link #add(Object, String, String, String)}to order the
|
|
122 |
* objects into an appropriate order based on the pre- and post-reqts provided. Errors such as
|
|
123 |
* cyclic dependencies or unrecognized names are logged and ignored.
|
|
124 |
*/
|
|
125 | 33 |
public List getOrderedObjects()
|
126 |
{ |
|
127 | 33 |
if (_orderingsMap == null) |
128 | 1 |
return Collections.EMPTY_LIST;
|
129 |
|
|
130 | 32 |
try
|
131 |
{ |
|
132 | 32 |
_nodeMap = new HashMap();
|
133 |
|
|
134 | 32 |
initializeGraph(); |
135 |
|
|
136 | 32 |
return _trailer.getOrder();
|
137 |
} |
|
138 |
finally
|
|
139 |
{ |
|
140 | 32 |
_nodeMap = null;
|
141 | 32 |
_leader = null;
|
142 | 32 |
_trailer = null;
|
143 |
} |
|
144 |
} |
|
145 |
|
|
146 | 32 |
private void initializeGraph() |
147 |
{ |
|
148 | 32 |
addNodes(); |
149 |
|
|
150 | 32 |
if (_leader == null) |
151 | 28 |
_leader = new Node(null, "*-leader-*"); |
152 |
|
|
153 | 32 |
if (_trailer == null) |
154 | 30 |
_trailer = new Node(null, "*-trailer-*"); |
155 |
|
|
156 | 32 |
addDependencies(); |
157 |
} |
|
158 |
|
|
159 | 114 |
private Node getNode(String name)
|
160 |
{ |
|
161 | 114 |
return (Node) _nodeMap.get(getOrderable(name));
|
162 |
} |
|
163 |
|
|
164 | 32 |
private void addNodes() |
165 |
{ |
|
166 | 32 |
Iterator i = _orderingsList.iterator(); |
167 |
|
|
168 | 32 |
while (i.hasNext())
|
169 |
{ |
|
170 | 76 |
ObjectOrdering o = (ObjectOrdering) i.next(); |
171 |
|
|
172 | 76 |
Node node = new Node(o.getObject(), o.getName());
|
173 |
|
|
174 | 76 |
_nodeMap.put(o, node); |
175 |
|
|
176 | 76 |
if ("*".equals(o.getPostreqs())) |
177 |
{ |
|
178 | 5 |
if (_leader == null) |
179 | 4 |
_leader = node; |
180 |
else
|
|
181 | 1 |
_errorHandler.error(_log, OrdererMessages.dupeLeader(_objectType, o, |
182 |
getOrderable(_leader.getName())), HiveMind.getLocation(o.getObject()), |
|
183 |
null);
|
|
184 |
} |
|
185 |
|
|
186 | 76 |
if ("*".equals(o.getPrereqs())) |
187 |
{ |
|
188 | 3 |
if (_trailer == null) |
189 | 2 |
_trailer = node; |
190 |
else
|
|
191 | 1 |
_errorHandler.error(_log, OrdererMessages.dupeTrailer(_objectType, o, |
192 |
getOrderable(_trailer.getName())), HiveMind.getLocation(o.getObject()), |
|
193 |
null);
|
|
194 |
} |
|
195 |
|
|
196 |
} |
|
197 |
} |
|
198 |
|
|
199 | 32 |
private void addDependencies() |
200 |
{ |
|
201 | 32 |
Iterator i = _orderingsList.iterator(); |
202 |
|
|
203 | 32 |
while (i.hasNext())
|
204 |
{ |
|
205 | 76 |
ObjectOrdering o = (ObjectOrdering) i.next(); |
206 |
|
|
207 | 76 |
addDependencies(o, getNode(o.getName())); |
208 |
} |
|
209 |
} |
|
210 |
|
|
211 | 76 |
private void addDependencies(ObjectOrdering orderable, Node node) |
212 |
{ |
|
213 | 76 |
addPreRequisites(orderable, node); |
214 | 76 |
addPostRequisites(orderable, node); |
215 |
|
|
216 | 76 |
try
|
217 |
{ |
|
218 | 76 |
if (node != _leader)
|
219 | 72 |
node.addDependency(_leader); |
220 |
|
|
221 | 76 |
if (node != _trailer)
|
222 | 74 |
_trailer.addDependency(node); |
223 |
} |
|
224 |
catch (ApplicationRuntimeException ex)
|
|
225 |
{ |
|
226 |
// This code is unreachable ... but nonetheless.
|
|
227 |
|
|
228 | 0 |
String name = node.getName(); |
229 | 0 |
ObjectOrdering trigger = getOrderable(name); |
230 |
|
|
231 | 0 |
_errorHandler.error(_log, OrdererMessages.dependencyCycle(_objectType, orderable, ex), |
232 |
HiveMind.getLocation(trigger.getObject()), ex); |
|
233 |
} |
|
234 |
} |
|
235 |
|
|
236 | 76 |
private void addPreRequisites(ObjectOrdering ordering, Node node) |
237 |
{ |
|
238 | 76 |
String prereqs = ordering.getPrereqs(); |
239 |
|
|
240 | 76 |
if ("*".equals(prereqs)) |
241 | 3 |
return;
|
242 |
|
|
243 | 73 |
String[] names = StringUtils.split(prereqs); |
244 |
|
|
245 | 73 |
for (int i = 0; i < names.length; i++) |
246 |
{ |
|
247 | 14 |
String prename = names[i]; |
248 |
|
|
249 | 14 |
Node prenode = getNode(prename); |
250 |
|
|
251 | 14 |
if (prenode == null) |
252 |
{ |
|
253 | 1 |
_errorHandler.error(_log, OrdererMessages.badDependency(_objectType, prename, |
254 |
ordering), HiveMind.getLocation(ordering.getObject()), null);
|
|
255 | 1 |
continue;
|
256 |
} |
|
257 |
else
|
|
258 |
{ |
|
259 | 13 |
try
|
260 |
{ |
|
261 | 13 |
node.addDependency(prenode); |
262 |
} |
|
263 |
catch (ApplicationRuntimeException ex)
|
|
264 |
{ |
|
265 | 1 |
_errorHandler.error(_log, OrdererMessages.dependencyCycle(_objectType, |
266 |
ordering, ex), HiveMind.getLocation(ordering.getObject()), ex); |
|
267 |
} |
|
268 |
} |
|
269 |
} |
|
270 |
} |
|
271 |
|
|
272 | 76 |
private void addPostRequisites(ObjectOrdering ordering, Node node) |
273 |
{ |
|
274 | 76 |
String postreqs = ordering.getPostreqs(); |
275 |
|
|
276 | 76 |
if ("*".equals(postreqs)) |
277 | 5 |
return;
|
278 |
|
|
279 | 71 |
String[] names = StringUtils.split(postreqs); |
280 |
|
|
281 | 71 |
for (int i = 0; i < names.length; i++) |
282 |
{ |
|
283 | 24 |
String postname = names[i]; |
284 |
|
|
285 | 24 |
Node postnode = getNode(postname); |
286 |
|
|
287 | 24 |
if (postnode == null) |
288 |
{ |
|
289 | 1 |
_errorHandler.error(_log, OrdererMessages.badDependency(_objectType, postname, |
290 |
ordering), HiveMind.getLocation(ordering.getObject()), null);
|
|
291 |
} |
|
292 |
else
|
|
293 |
{ |
|
294 | 23 |
try
|
295 |
{ |
|
296 | 23 |
postnode.addDependency(node); |
297 |
} |
|
298 |
catch (ApplicationRuntimeException ex)
|
|
299 |
{ |
|
300 | 1 |
_errorHandler.error(_log, OrdererMessages.dependencyCycle(_objectType, |
301 |
ordering, ex), HiveMind.getLocation(ordering.getObject()), ex); |
|
302 |
} |
|
303 |
} |
|
304 |
} |
|
305 |
} |
|
306 |
|
|
307 |
private static class Node |
|
308 |
{ |
|
309 |
private Object _object;
|
|
310 |
private String _name;
|
|
311 |
private List _dependencies;
|
|
312 |
|
|
313 | 134 |
public Node(Object o, String name)
|
314 |
{ |
|
315 | 134 |
_object = o; |
316 | 134 |
_name = name; |
317 | 134 |
_dependencies = new ArrayList();
|
318 |
} |
|
319 |
|
|
320 | 2 |
public String getName()
|
321 |
{ |
|
322 | 2 |
return _name;
|
323 |
} |
|
324 |
|
|
325 | 182 |
public void addDependency(Node n) |
326 |
{ |
|
327 | 182 |
if (n.isReachable(this)) |
328 | 2 |
throw new ApplicationRuntimeException( |
329 |
"A cycle has been detected from the initial object [" + _name + "]", |
|
330 |
HiveMind.getLocation(_object), null);
|
|
331 |
else
|
|
332 | 180 |
_dependencies.add(n); |
333 |
} |
|
334 |
|
|
335 | 377 |
private boolean isReachable(Node n) |
336 |
{ |
|
337 | 377 |
boolean reachable = (n == this); |
338 | 377 |
Iterator i = _dependencies.iterator(); |
339 |
|
|
340 | 377 |
while (i.hasNext() && !reachable)
|
341 |
{ |
|
342 | 197 |
Node dep = (Node) i.next(); |
343 | 197 |
reachable = (dep == n) ? true : dep.isReachable(n);
|
344 |
} |
|
345 |
|
|
346 | 377 |
return reachable;
|
347 |
} |
|
348 |
|
|
349 | 32 |
public List getOrder()
|
350 |
{ |
|
351 | 32 |
List result = new ArrayList();
|
352 | 32 |
fillOrder(result); |
353 |
|
|
354 | 32 |
return result;
|
355 |
} |
|
356 |
|
|
357 | 212 |
private void fillOrder(List result) |
358 |
{ |
|
359 | 212 |
if (result.contains(_object))
|
360 | 44 |
return;
|
361 |
|
|
362 | 168 |
Iterator i = _dependencies.iterator(); |
363 |
|
|
364 | 168 |
while (i.hasNext())
|
365 |
{ |
|
366 | 180 |
Node dep = (Node) i.next(); |
367 | 180 |
dep.fillOrder(result); |
368 |
} |
|
369 |
|
|
370 | 168 |
if (_object != null) |
371 | 76 |
result.add(_object); |
372 |
} |
|
373 |
} |
|
374 |
|
|
375 |
} |
|