Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
DependencyEngine |
|
| 2.3684210526315788;2.368 | ||||
Node |
|
| 2.3684210526315788;2.368 |
1 | /* | |
2 | * Copyright 2001,2004 The Apache Software Foundation. | |
3 | * | |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | package org.apache.commons.jjar; | |
18 | ||
19 | import java.util.HashMap; | |
20 | import java.util.List; | |
21 | import java.util.Iterator; | |
22 | import java.util.ArrayList; | |
23 | import java.lang.Thread; | |
24 | ||
25 | /** | |
26 | * <p> | |
27 | * Simple class to figure out ordered dependency lists. Basic | |
28 | * idea is that you load it with datum consisting of a set | |
29 | * consisting of a package name and list of packages that | |
30 | * it's dependent upon. | |
31 | * </p> | |
32 | * | |
33 | * <p> | |
34 | * Then, you should be able to ask for the dependencies for any | |
35 | * package placed in there. | |
36 | * </p> | |
37 | * | |
38 | * <p> will detect loops at 'runtime', not loadtime. Just punts | |
39 | * when that happens | |
40 | * </p> | |
41 | * | |
42 | * <p> | |
43 | * This thing isn't close to threadsafe :) | |
44 | * </p> | |
45 | * | |
46 | * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> | |
47 | * @version $Id: DependencyEngine.java 155454 2005-02-26 13:23:34Z dirkv $ | |
48 | */ | |
49 | public class DependencyEngine | |
50 | { | |
51 | 0 | private HashMap projects = new HashMap(); |
52 | 0 | private ArrayList buildList = null; |
53 | ||
54 | /** | |
55 | * this is a real sucky solution to something I don't want to | |
56 | * think about right now... we use this to ensure that | |
57 | * the information in our graph is fresh | |
58 | */ | |
59 | 0 | private long currentTimestamp = -1; |
60 | ||
61 | /** | |
62 | * CTOR | |
63 | */ | |
64 | public DependencyEngine() | |
65 | 0 | { |
66 | 0 | } |
67 | ||
68 | /** | |
69 | * Reset the dependency engine, clear all entries | |
70 | * and start from scratch. | |
71 | */ | |
72 | public void reset() | |
73 | { | |
74 | 0 | projects = new HashMap(); |
75 | 0 | } |
76 | ||
77 | /** | |
78 | * returns a list of dependencies for a given package | |
79 | * with the target being excluded from the list. | |
80 | * | |
81 | * @param pkg package to get dependency list for | |
82 | * @return List list of dependencies, in order | |
83 | */ | |
84 | public List getDependencies( String pkg ) | |
85 | { | |
86 | 0 | return getDependencies(pkg, true); |
87 | } | |
88 | ||
89 | /** | |
90 | * returns a list of dependencies for a given package | |
91 | * allowing the exclusion/inclusion of the target package. | |
92 | * | |
93 | * @param pkg package to get dependency list for | |
94 | * @param excludeTarget boolean to control exclusion of target package | |
95 | * @return List list of dependencies, in order | |
96 | */ | |
97 | public List getDependencies( String pkg, boolean excludeTarget ) | |
98 | { | |
99 | 0 | buildList = new ArrayList(); |
100 | ||
101 | try | |
102 | { | |
103 | /* | |
104 | * if we are called in the same millisecond as our | |
105 | * last trip through, sleep as the sucky 'fresh graph' | |
106 | * solution depends on this, and this would be quite | |
107 | * an interesting time-dependent thing to debug :) | |
108 | */ | |
109 | 0 | if (System.currentTimeMillis() == currentTimestamp) |
110 | { | |
111 | 0 | Thread.sleep(1); |
112 | } | |
113 | ||
114 | /* | |
115 | * set the current time so we can see if our graph node | |
116 | * state is for this trip, or a previous trip. | |
117 | */ | |
118 | 0 | currentTimestamp = System.currentTimeMillis(); |
119 | ||
120 | /* | |
121 | * now, just do it | |
122 | */ | |
123 | 0 | doIt( pkg ); |
124 | } | |
125 | 0 | catch( Exception e ) |
126 | { | |
127 | 0 | System.out.println("DE.getDependencies() : " + pkg + " : " + e); |
128 | 0 | } |
129 | ||
130 | // The the multi project dep list this code is lopping | |
131 | // off the package stated as the target. Need a flag to | |
132 | // indicated whether you want the target included or | |
133 | // or. For a multi-project build like maven you need | |
134 | // the target because you actually want to build the | |
135 | // target. For the JJAR task you don't want the target | |
136 | // because you're just downloading JARs. | |
137 | 0 | if( excludeTarget && buildList.size() > 0) |
138 | { | |
139 | 0 | buildList.remove( buildList.size() - 1 ); |
140 | } | |
141 | ||
142 | 0 | return buildList; |
143 | } | |
144 | ||
145 | /** | |
146 | * Generates a dependency list for a set of packages. | |
147 | * | |
148 | * @param packages List of strings, each string is a package name | |
149 | * @return list of dependencies, in order | |
150 | */ | |
151 | public List getDependencies(List packages) | |
152 | { | |
153 | 0 | return getDependencies(packages, true); |
154 | } | |
155 | ||
156 | /** | |
157 | * Generates a dependency list for a set of packages | |
158 | * where there is the option to exclude/include the | |
159 | * target packages. | |
160 | * | |
161 | * @param packages List of strings, each string is a package name | |
162 | * @param excludeTarget boolean to exclude target | |
163 | * @return List list of dependencies, in order | |
164 | */ | |
165 | public List getDependencies( List packages, boolean excludeTarget ) | |
166 | { | |
167 | 0 | HashMap h = new HashMap(); |
168 | 0 | ArrayList l = new ArrayList(); |
169 | ||
170 | /* | |
171 | * for each package, get the dependency list | |
172 | * and drop them into the list if it's not already | |
173 | * in there | |
174 | */ | |
175 | ||
176 | 0 | for( Iterator i = packages.iterator(); i.hasNext(); ) |
177 | { | |
178 | 0 | String pkg = (String) i.next(); |
179 | ||
180 | 0 | List deps = getDependencies( pkg, excludeTarget ); |
181 | ||
182 | 0 | for (Iterator ii = deps.iterator(); ii.hasNext(); ) |
183 | { | |
184 | 0 | String dep = (String) ii.next(); |
185 | ||
186 | 0 | if ( h.get( dep ) == null) |
187 | { | |
188 | 0 | h.put(dep, dep); |
189 | 0 | l.add(dep); |
190 | } | |
191 | 0 | } |
192 | 0 | } |
193 | ||
194 | 0 | return l; |
195 | } | |
196 | ||
197 | ||
198 | /** | |
199 | * from previous use - generates a dependency list | |
200 | * spanning the entire tree. Returns a list | |
201 | * of names. | |
202 | */ | |
203 | public List generateNamelist() | |
204 | throws Exception | |
205 | { | |
206 | /* | |
207 | * get the project list | |
208 | */ | |
209 | ||
210 | 0 | buildList = new ArrayList(); |
211 | ||
212 | 0 | Iterator i = projects.keySet().iterator(); |
213 | ||
214 | 0 | while(i.hasNext()) |
215 | { | |
216 | 0 | String s = (String) i.next(); |
217 | ||
218 | /* | |
219 | * make them by name | |
220 | */ | |
221 | ||
222 | 0 | doIt( s ); |
223 | 0 | } |
224 | ||
225 | 0 | return buildList; |
226 | } | |
227 | ||
228 | /** | |
229 | * from previous use - generates a dependency list | |
230 | * spanning the entire tree. Returns a list | |
231 | * of cookies. | |
232 | */ | |
233 | public List generateCookielist() | |
234 | throws Exception | |
235 | { | |
236 | /* | |
237 | * get the project list | |
238 | */ | |
239 | ||
240 | 0 | List list = generateNamelist(); |
241 | 0 | ArrayList cookies = new ArrayList(); |
242 | ||
243 | 0 | Iterator i = list.iterator(); |
244 | ||
245 | 0 | while(i.hasNext()) |
246 | { | |
247 | 0 | String s = (String) i.next(); |
248 | 0 | Node n = (Node) projects.get( s ); |
249 | ||
250 | 0 | cookies.add( n.getCookie() ); |
251 | 0 | } |
252 | ||
253 | 0 | return cookies; |
254 | } | |
255 | ||
256 | /** | |
257 | * The recursive worker... | |
258 | */ | |
259 | void doIt( String current ) | |
260 | throws Exception | |
261 | { | |
262 | 0 | Node project = (Node) projects.get(current); |
263 | ||
264 | 0 | if (project == null) |
265 | { | |
266 | /* | |
267 | * we may have a dependency that isn't a project. | |
268 | * so what... (This shouldn't happen) | |
269 | */ | |
270 | ||
271 | 0 | buildList.add( current ); |
272 | 0 | return; |
273 | } | |
274 | ||
275 | /* | |
276 | * get the timestamp and compare. If not the same, reset | |
277 | */ | |
278 | ||
279 | 0 | if ( project.getTimestamp() != currentTimestamp) |
280 | { | |
281 | 0 | project.setStatus( Node.ZILCH ); |
282 | } | |
283 | ||
284 | 0 | project.setTimestamp( currentTimestamp ); |
285 | ||
286 | /* | |
287 | * check status of this one | |
288 | */ | |
289 | ||
290 | 0 | int status = project.getStatus(); |
291 | ||
292 | 0 | if ( status == Node.WORKING ) |
293 | { | |
294 | 0 | throw new Exception("Detected loop while trying to build " + current); |
295 | } | |
296 | 0 | else if ( status == Node.ZILCH ) |
297 | { | |
298 | /* | |
299 | * not working - so mark as working and start on the dependencies | |
300 | */ | |
301 | 0 | project.setStatus( Node.WORKING ); |
302 | ||
303 | /* | |
304 | * do we have any dependencies? | |
305 | */ | |
306 | 0 | Iterator deps = project.getDeps(); |
307 | ||
308 | /* | |
309 | * if so, work on each | |
310 | */ | |
311 | ||
312 | 0 | while( deps.hasNext() ) |
313 | { | |
314 | 0 | String dep = (String) deps.next(); |
315 | 0 | Node depnode = (Node) projects.get( dep ); |
316 | ||
317 | 0 | if (depnode == null) |
318 | { | |
319 | /* | |
320 | * we don't have this as a project, so | |
321 | * let the client try to build it... | |
322 | */ | |
323 | ||
324 | // System.out.println("Adding non-project dep build list : " + current ); | |
325 | ||
326 | 0 | buildList.add( dep ); |
327 | 0 | continue; |
328 | } | |
329 | ||
330 | /* | |
331 | * get the timestamp and compare. If not the same, reset | |
332 | */ | |
333 | ||
334 | 0 | if ( depnode.getTimestamp() != currentTimestamp) |
335 | { | |
336 | 0 | depnode.setStatus( Node.ZILCH ); |
337 | } | |
338 | ||
339 | 0 | depnode.setTimestamp( currentTimestamp ); |
340 | ||
341 | /* | |
342 | * now, look at the status of this dependency | |
343 | */ | |
344 | ||
345 | 0 | int depstatus = depnode.getStatus(); |
346 | ||
347 | 0 | if ( depstatus == Node.WORKING ) |
348 | { | |
349 | /* | |
350 | * gaak. loop! | |
351 | */ | |
352 | 0 | throw new Exception("LOOP : checking dep " + dep + " for current = " + current ); |
353 | } | |
354 | 0 | else if ( depstatus == Node.ZILCH ) |
355 | { | |
356 | // System.out.println(" trying to build " + current + " : need to build dep " + dep ); | |
357 | ||
358 | /* | |
359 | * recurse | |
360 | */ | |
361 | ||
362 | 0 | doIt( dep ); |
363 | } | |
364 | 0 | else if( depstatus == Node.DONE ) |
365 | { | |
366 | // can skip | |
367 | } | |
368 | 0 | } |
369 | ||
370 | /* | |
371 | * if all clear, can build and mark as done. We don't care | |
372 | * if the client couldn't do it for now. That may change. | |
373 | * the client can tell | |
374 | */ | |
375 | ||
376 | //System.out.println("Adding to build list : " + current ); | |
377 | ||
378 | 0 | buildList.add( current ); |
379 | 0 | project.setStatus( Node.DONE ); |
380 | ||
381 | 0 | return; |
382 | } | |
383 | ||
384 | /* | |
385 | * node is done | |
386 | */ | |
387 | ||
388 | 0 | return; |
389 | } | |
390 | ||
391 | public void addProject(String project, List dependencies) | |
392 | throws Exception | |
393 | { | |
394 | 0 | addProject(project, dependencies, project); |
395 | 0 | } |
396 | ||
397 | /** | |
398 | * Adds a project and it's associated dependencies. The dependencies | |
399 | * currently do not have to be projects themselves. | |
400 | * | |
401 | * @param project Name of project to add | |
402 | * @param dependencies java.util.List of project dependencies | |
403 | * @throws Exception in the even that it already has the project in the list | |
404 | */ | |
405 | public void addProject( String project, List dependencies, Object cookie ) | |
406 | throws Exception | |
407 | { | |
408 | /* | |
409 | * first, see if we have it | |
410 | */ | |
411 | 0 | Node n = (Node) projects.get( project ); |
412 | ||
413 | 0 | if (n != null) |
414 | { | |
415 | //System.out.println(" addProject() : rejecting duplicate : " + project ); | |
416 | 0 | throw new Exception("already have it..."); |
417 | } | |
418 | ||
419 | // System.out.println(" addProject() : adding project : " + project ); | |
420 | ||
421 | /* | |
422 | * make a new one and add the dependencies | |
423 | */ | |
424 | 0 | n = new Node( project, cookie ); |
425 | ||
426 | 0 | Iterator i = dependencies.iterator(); |
427 | ||
428 | 0 | while( i.hasNext() ) |
429 | { | |
430 | 0 | String dep = (String) i.next(); |
431 | ||
432 | 0 | if ( dep.equals( project ) ) |
433 | { | |
434 | // System.out.println(" addProject() : rejecting self- dependency : " + project ); | |
435 | } | |
436 | else | |
437 | { | |
438 | // System.out.println(" addProject() : adding dependency : " + dep + " for project : " + project ); | |
439 | 0 | n.addDep( dep ); |
440 | } | |
441 | 0 | } |
442 | ||
443 | /* | |
444 | * add to the pile | |
445 | */ | |
446 | ||
447 | 0 | projects.put( project, n ); |
448 | ||
449 | 0 | return; |
450 | } | |
451 | } | |
452 | ||
453 | class Node | |
454 | { | |
455 | 0 | public static int ZILCH = 0; |
456 | 0 | public static int WORKING = 1; |
457 | 0 | public static int DONE = 2; |
458 | ||
459 | 0 | private int status = ZILCH; |
460 | 0 | private ArrayList deps = new ArrayList(); |
461 | 0 | private String name = ""; |
462 | 0 | private Object cookie = null; |
463 | 0 | private long timestamp = 0; |
464 | ||
465 | public Node( String name, Object cookie) | |
466 | 0 | { |
467 | 0 | this.name = name; |
468 | 0 | this.cookie = cookie; |
469 | 0 | } |
470 | ||
471 | public Object getCookie() | |
472 | { | |
473 | 0 | return cookie; |
474 | } | |
475 | ||
476 | public void addDep( String dep ) | |
477 | { | |
478 | 0 | deps.add( dep ); |
479 | 0 | } |
480 | ||
481 | public Iterator getDeps() | |
482 | { | |
483 | 0 | return deps.iterator(); |
484 | } | |
485 | ||
486 | public void setStatus( int i ) | |
487 | { | |
488 | 0 | status = i; |
489 | 0 | } |
490 | ||
491 | public int getStatus() | |
492 | { | |
493 | 0 | return status; |
494 | } | |
495 | ||
496 | public long getTimestamp() | |
497 | { | |
498 | 0 | return timestamp; |
499 | } | |
500 | ||
501 | public void setTimestamp( long t) | |
502 | { | |
503 | 0 | timestamp = t; |
504 | 0 | } |
505 | } | |
506 | ||
507 |