| 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 |