Coverage report

  %line %branch
nl.toolforge.karma.core.cmd.util.DependencyHelper
0% 
0% 

 1  
 /*
 2  
 Karma core - Core of the Karma application
 3  
 Copyright (C) 2004  Toolforge <www.toolforge.nl>
 4  
 
 5  
 This library is free software; you can redistribute it and/or
 6  
 modify it under the terms of the GNU Lesser General Public
 7  
 License as published by the Free Software Foundation; either
 8  
 version 2.1 of the License, or (at your option) any later version.
 9  
 
 10  
 This library is distributed in the hope that it will be useful,
 11  
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 Lesser General Public License for more details.
 14  
 
 15  
 You should have received a copy of the GNU Lesser General Public
 16  
 License along with this library; if not, write to the Free Software
 17  
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 18  
 */
 19  
 package nl.toolforge.karma.core.cmd.util;
 20  
 
 21  
 import java.io.File;
 22  
 import java.io.FileWriter;
 23  
 import java.io.IOException;
 24  
 import java.util.HashSet;
 25  
 import java.util.Iterator;
 26  
 import java.util.LinkedHashSet;
 27  
 import java.util.Set;
 28  
 
 29  
 import net.sf.sillyexceptions.OutOfTheBlueException;
 30  
 
 31  
 import nl.toolforge.karma.core.Version;
 32  
 import nl.toolforge.karma.core.boot.WorkingContext;
 33  
 import nl.toolforge.karma.core.manifest.Manifest;
 34  
 import nl.toolforge.karma.core.manifest.ManifestException;
 35  
 import nl.toolforge.karma.core.module.Module;
 36  
 import nl.toolforge.karma.core.module.ModuleTypeException;
 37  
 import nl.toolforge.karma.core.scm.ModuleDependency;
 38  
 import nl.toolforge.karma.core.vc.VersionControlException;
 39  
 import nl.toolforge.karma.core.vc.cvsimpl.Utils;
 40  
 
 41  
 /**
 42  
  * Dependency management is heavily used by Karma. This helper class provides methods to resolve dependencies, check
 43  
  * them, etc.
 44  
  *
 45  
  * @author D.A. Smedes
 46  
  * @version $Id: DependencyHelper.java,v 1.28 2004/11/16 22:28:02 hippe Exp $
 47  
  */
 48  
 public final class DependencyHelper {
 49  
 
 50  
   public final static String MODULE_DEPENDENCIES_PROPERTIES = "module-dependencies.properties";
 51  
 
 52  0
   private Manifest manifest = null;
 53  
 
 54  0
   public DependencyHelper(Manifest currentManifest) {
 55  
 
 56  0
     if (currentManclass="keyword">ifest == null) {
 57  0
       throw new IllegalArgumentException("Manifest cannot be null.");
 58  
     }
 59  
 
 60  0
     this.manifest = currentManifest;
 61  0
   }
 62  
 
 63  
   /**
 64  
    * Returns the classpath for <code>module</code>, or an empty <code>String</code> if no dependencies exist.
 65  
    *
 66  
    * @param module The module for which a classpath should be determined.
 67  
    * @return See method description.
 68  
    */
 69  
   public String getClassPath(Module module) throws ModuleTypeException, DependencyException {
 70  
 
 71  0
     Set deps = getAllDependencies(module, false, false);
 72  0
     return DependencyPath.concat(deps, false, ';');
 73  
   }
 74  
 
 75  
   /**
 76  
    * Returns the classpath for <code>module</code>, or an empty <code>String</code> if no dependencies exist.
 77  
    * <p>
 78  
    * The classpath consists of:
 79  
    * <ul>
 80  
    *   <li>The classes of the module's dependencies.
 81  
    *   <li>The resources of the module's dependencies.
 82  
    *   <li>The test classes of the module's dependencies.
 83  0
    *   <li>The test resources of the module's dependencies.
 84  
    * </ul>
 85  
    * @param module The module for which the test classpath should be determined.
 86  0
    * @return See method description.
 87  0
    */
 88  0
   public String getTestClassPath(Module module) throws ModuleTypeException, DependencyException {
 89  0
 
 90  0
     Set deps = getAllDependencies(module, true, false);
 91  0
     return DependencyPath.concat(deps, false, ';');
 92  
   }
 93  
 
 94  
   public Set getAllDependencies(Module module, boolean doTest, class="keyword">boolean doPackage) throws ModuleTypeException, DependencyException {
 95  0
     Set all = new LinkedHashSet();
 96  0
     all.addAll(getModuleDependencies(module, doTest, doPackage));
 97  0
     all.addAll(getJarDependencies(module, doPackage));
 98  0
     return all;
 99  
   }
 100  
 
 101  
   /**
 102  
    * Gets a <code>Set</code> of {@link DependencyPath}s, each one identifying the path to a module dependency (a
 103  
    * dependency of <code>module</code> to another <code>Module</code>).
 104  0
    *
 105  0
    * @param module      The module for which a dependency-path should be determined.
 106  0
    * @param doTest      Whether to include test resources for all deps.
 107  
    * @param doPackage   Whether to include only the deps that are to be packaged or all deps.
 108  0
    * @param moduleType  Only return modules of the specified type. Return all types when null.
 109  0
    *
 110  0
    * @return See method description.
 111  0
    * @throws DependencyException  When a dependency for a module is not available.
 112  0
    */
 113  0
   public Set getModuleDependencies(Module module, boolean doTest, class="keyword">boolean doPackage, Module.Type moduleType) throws ModuleTypeException, DependencyException {
 114  0
     if (module == null) {
 115  0
       throw new IllegalArgumentException("Module cannot be null.");
 116  0
     }
 117  
 
 118  0
     Set s = new LinkedHashSet();
 119  
 
 120  0
     for (Iterator iterator = module.getDependencies().iterator(); iterator.hasNext();) {
 121  0
       ModuleDependency dep = (ModuleDependency) iterator.next();
 122  0
       if (dep.isModuleDependency()) {
 123  
 
 124  0
         try {
 125  0
           Module depModule = manifest.getModule(dep.getModule());
 126  0
           DependencyPath path;
 127  0
 
 128  0
           //when packaging we want to have the archive
 129  0
           //when we are not packaging, i.e. building or testing, then we want the classes.
 130  
           //todo: refactor the code below to minimize duplicate code.
 131  0
           if (doPackage) {
 132  0
             path = new DependencyPath(manifest.getBuildBaseDirectory(), class="keyword">new File(dep.getModule(), resolveArchiveName(depModule)));
 133  0
             if (!path.exists()) {
 134  0
               throw new DependencyException(DependencyException.DEPENDENCY_NOT_FOUND, class="keyword">new Object[]{dep.getModule()});
 135  0
             }
 136  0
             if ((!doPackage || dep.doPackage()) &&
 137  0
                 (moduleType == null || moduleType.equals(depModule.getType())) ) {
 138  0
               s.add(path);
 139  0
             }
 140  
           } else {
 141  0
             Set subSet = getModuleDependencies(depModule, doTest, doPackage, moduleType);
 142  0
             path = new DependencyPath(manifest.getBuildBaseDirectory(), class="keyword">new File(dep.getModule(), "build"));
 143  0
             if (!path.exists()) {
 144  0
               throw new DependencyException(DependencyException.DEPENDENCY_NOT_FOUND, class="keyword">new Object[]{dep.getModule()});
 145  
             }
 146  0
             if ((!doPackage || dep.doPackage()) &&
 147  
                 (moduleType == null || moduleType.equals(depModule.getType())) ) {
 148  0
               subSet.add(path);
 149  
             }
 150  0
             if (doTest) {
 151  
               //todo: in case of tests we need the resources as well.
 152  
               //In case of a test dependency the test classes are needed, as well as
 153  
               //the resources for running the tests.
 154  
               //for the time being only do this for the java source modules.
 155  0
               path = new DependencyPath(manifest.getBuildBaseDirectory(), class="keyword">new File(dep.getModule(), "test/classes"));
 156  0
               if (moduleType == null || moduleType.equals(depModule.getType()) ) {
 157  0
                 subSet.add(path);
 158  0
               }
 159  0
               if (moduleType != null && moduleType.equals(depModule.getType()) &&
 160  
                       moduleType.equals(Module.JAVA_SOURCE_MODULE)) {
 161  0
                 path = new DependencyPath(manifest.getBaseDirectory(), class="keyword">new File(module.getBaseDir().getPath(), "src/resources"));
 162  0
                 if (path.exists()) {
 163  0
                   subSet.add(path);
 164  
                 }
 165  0
                 path = new DependencyPath(manifest.getBaseDirectory(), class="keyword">new File(module.getBaseDir().getPath(), "test/resources"));
 166  0
                 if (path.exists()) {
 167  0
                   subSet.add(path);
 168  0
                 }
 169  
               }
 170  0
             }
 171  0
             s.addAll(subSet);
 172  0
           }
 173  0
         } catch (ManifestException me) {
 174  0
           if (me.getErrorCode().equals(Manclass="keyword">ifestException.MODULE_NOT_FOUND)) {
 175  0
             throw new DependencyException(DependencyException.MODULE_NOT_IN_MANIFEST, me.getMessageArguments());
 176  0
           } else {
 177  0
             throw new DependencyException(me.getErrorCode(), me.getMessageArguments());
 178  0
           }
 179  0
         }
 180  
       }
 181  0
     }
 182  0
     return s;
 183  0
   }
 184  0
 
 185  
   /**
 186  0
    * Gets a <code>Set</code> of {@link DependencyPath}s, each one identifying the path to a module dependency (a
 187  
    * dependency of <code>module</code> to another <code>Module</code>).
 188  
    *
 189  0
    * @param module     The module for which a dependency-path should be determined.
 190  0
    * @param doTest     Whether to include test resources for all deps.
 191  0
    * @param doPackage  Whether to include only the deps that are to be packaged or all deps.
 192  0
    *
 193  
    * @return See method description.
 194  0
    * @throws DependencyException  When a dependency for a module is not available.
 195  0
    */
 196  0
   public Set getModuleDependencies(Module module, boolean doTest, class="keyword">boolean doPackage) throws ModuleTypeException, DependencyException {
 197  0
     return getModuleDependencies(module, doTest, doPackage, null);
 198  0
   }
 199  
 
 200  0
   /**
 201  
    * Create a properties file that contains mappings from module name to
 202  
    * module name plus version. E.g. karma-core -> karma-core_0-1.
 203  
    * <p>
 204  
    * The properties file is called 'module-dependencies.properties' and is
 205  
    * stored in the build directory of the given module.
 206  
    * </p>
 207  
    */
 208  
   public void createModuleDependenciesFilter(Module module) throws DependencyException {
 209  0
     BuildEnvironment env = new BuildEnvironment(manifest, module);
 210  
 
 211  0
     FileWriter write1 = null;
 212  
     try {
 213  0
       Set moduleDeps = module.getDependencies();
 214  0
       Iterator it = moduleDeps.iterator();
 215  0
 
 216  0
       File moduleBuildDir = env.getModuleBuildDirectory();
 217  0
       moduleBuildDir.mkdirs();
 218  0
       File archivesProperties = new File(moduleBuildDir, MODULE_DEPENDENCIES_PROPERTIES);
 219  0
       archivesProperties.createNewFile();
 220  0
       write1 = new FileWriter(archivesProperties);
 221  
 
 222  0
       while (it.hasNext()) {
 223  0
         ModuleDependency dep = (ModuleDependency) it.next();
 224  0
         if (dep.isModuleDependency()) {
 225  0
           Module mod = manifest.getModule(dep.getModule());
 226  
 
 227  0
           write1.write(mod.getName()+"="+resolveArtifactName(mod)+"\n");
 228  
         }
 229  
       }
 230  0
     } catch (ManifestException me) {
 231  0
       me.printStackTrace();
 232  0
     } catch (IOException ioe) {
 233  0
       ioe.printStackTrace();
 234  
     } finally {
 235  0
       try {
 236  0
         write1.close();
 237  0
       } catch (Exception e) {
 238  0
         throw new OutOfTheBlueException("Unexpected exception when closing file writer.", e);
 239  0
       }
 240  
     }
 241  0
   }
 242  
 
 243  
   /**
 244  
    * Check whether a certain module has an other module as a dependency.
 245  
    *
 246  0
    * @param module      The module for which is checked whether it has <code>dependency</code> as a dependency.
 247  0
    * @param dependency  The module for which to check whether it is a dependency of the current module.
 248  
    * @param doPackage   Whether to include only the deps that are to be packaged or all deps.
 249  
    * @return Whether the given module had the other given module as a dependency.
 250  0
    */
 251  
   public boolean hasModuleDependency(Module module, Module dependency, class="keyword">boolean doPackage) {
 252  0
     Iterator it = module.getDependencies().iterator();
 253  
     ModuleDependency dep;
 254  0
     boolean found = false;
 255  
 
 256  0
     while (it.hasNext() && !found) {
 257  0
       dep = (ModuleDependency) it.next();
 258  0
       if (dep.isModuleDependency()) {
 259  0
         if (dep.getModule().equals(dependency.getName())) {
 260  0
           found = (!doPackage || dep.doPackage());
 261  
         }
 262  
       }
 263  0
     }
 264  
 
 265  0
     return found;
 266  
   }
 267  0
 
 268  
 
 269  
   /**
 270  0
    * <p>Gets a <code>Set</code> of {@link DependencyPath}s, each one identifying a <code>jar</code>-file. Jar files are looked
 271  0
    * up Maven-style (see {@link ModuleDependency}.
 272  
    *
 273  
    * @param module         The module for which jar dependencies should be determined.
 274  
    * @param doPackage      Indicate if the dependencies that are to be packaged (<code>&lt;package="true"&gt;</code>)
 275  0
    *                       should be included (<code>true</code>) or all dependencies should be included
 276  
    *                       (<code>false</code>).
 277  
    *
 278  
    * @return               A <code>Set</code> containing {@link DependencyPath}s
 279  
    *
 280  
    * @throws DependencyException
 281  
    *   When a jar dependency is not phsyically available on disk. A check is performed on the
 282  
    *   existence of the jar file in either the local jar repository ({@link WorkingContext#getLocalRepository()}) or in
 283  
    *   the lib module that is specified as being part of the manifest.
 284  
    */
 285  
   public Set getJarDependencies(Module module, boolean doPackage) throws DependencyException {
 286  
 
 287  0
     if (module == null) {
 288  0
       throw new IllegalArgumentException("Module cannot be null.");
 289  
     }
 290  
 
 291  0
     Set s = new LinkedHashSet();
 292  
 
 293  0
     for (Iterator iterator = module.getDependencies().iterator(); iterator.hasNext();) {
 294  
 
 295  0
       ModuleDependency dep = (ModuleDependency) iterator.next();
 296  0
 
 297  0
       if (dep.isLibModuleDependency() || !dep.isModuleDependency()) {
 298  0
         DependencyPath path;
 299  0
         if (dep.isLibModuleDependency()) {
 300  0
           //dep on jar in lib module. This one is relative to the base dir of the manifest.
 301  0
           path = new DependencyPath(manifest.getModuleBaseDirectory(), class="keyword">new File(dep.getJarDependency()));
 302  0
         } else {
 303  0
           //dep on jar in Maven-style repo.
 304  0
           path = new DependencyPath(WorkingContext.getLocalRepository(), class="keyword">new File(dep.getJarDependency()));
 305  0
         }
 306  0
         if (!path.exists()) {
 307  0
           // todo this bit could have to download the dependency, like maven does.
 308  0
           throw new DependencyException(DependencyException.DEPENDENCY_NOT_FOUND, class="keyword">new Object[]{dep.getJarDependency()});
 309  0
         }
 310  0
 
 311  0
         if (!doPackage || dep.doPackage()) {
 312  0
           s.add(path);
 313  0
         }
 314  
       }
 315  
     }
 316  0
     return s;
 317  
   }
 318  
 
 319  
   /**
 320  
    * Determines the correct artifact name for <code>module</code>.
 321  
    * The artifact-name is determined as follows:
 322  
    *
 323  
    * <ul>
 324  
    *   <li/>If the state of the module is <code>WORKING</code>, the artifact-name is
 325  
    *        <code>&lt;module-name&gt;-WORKING</code>.
 326  
    *   <li/>If the state of the module is <code>DYNAMIC</code>, the artifact-name is
 327  
    *        <code>&lt;module-name&gt;-&lt;latest-versions&gt;</code>.
 328  
    *   <li/>If the state of the module is <code>STATIC</code>, the artifact-name is
 329  
    *        <code>&lt;module-name&gt;-&lt;version&gt;</code>.
 330  
    * </ul>
 331  
    *
 332  
    * @param module  The module for which to determine the artifact name.
 333  
    * @return The artifact name
 334  
    */
 335  
   public String resolveArtifactName(Module module) throws DependencyException {
 336  
 
 337  0
     String artifact = module.getName() + "-";
 338  
 
 339  0
     String version = "";
 340  
     try {
 341  0
       if (manclass="keyword">ifest.getState(module).equals(Module.WORKING)) {
 342  0
         version = Module.WORKING.toString();
 343  0
       } else if (manclass="keyword">ifest.getState(module).equals(Module.DYNAMIC)) {
 344  0
         version = Utils.getLocalVersion(module).toString();
 345  0
       } else { // STATIC module
 346  0
         version = ((Module) module).getVersionAsString();
 347  0
       }
 348  0
       version = version.replaceAll(Version.VERSION_SEPARATOR_CHAR, ".");
 349  0
     } catch (VersionControlException v) {
 350  0
       throw new DependencyException(v.getErrorCode(), v.getMessageArguments());
 351  0
     }
 352  0
     artifact += version;
 353  
 
 354  0
     return artifact;
 355  
   }
 356  
 
 357  
   /**
 358  
    * <p>Determines the correct archive name for <code>module</code>. The archive
 359  
    * name is determined as follows:
 360  
    *
 361  
    * <ul>
 362  
    *   <li/>If the state of the module is <code>WORKING</code>, the archive-name is
 363  
    *        <code>&lt;module-name&gt;_WORKING.jar</code>.
 364  
    *   <li/>If the state of the module is <code>DYNAMIC</code>, the archive-name is
 365  
    *        <code>&lt;module-name&gt;_&lt;latest-versions&gt;.jar</code>.
 366  0
    *   <li/>If the state of the module is <code>STATIC</code>, the archive-name is
 367  
    *        <code>&lt;module-name&gt;_&lt;version&gt;.jar</code>.
 368  
    * </ul>
 369  
    *
 370  
    * <p>The extension is <code>.war</code> if the module is a
 371  
    * <code>webapp</code>-module and <code>.ear</code> if the module is an
 372  
    * <code>eapp</code>-module
 373  0
    *
 374  0
    * @param module A <code>SourceModule</code> instance.
 375  
    * @return The archive-name as determined the way as described above.
 376  
    */
 377  0
   public String resolveArchiveName(Module module) throws ModuleTypeException, DependencyException {
 378  
 
 379  0
     // todo introduce a method to determine if a module is webapp-module; maybe its own class.
 380  0
     //
 381  
     String extension;
 382  0
     if (module.getType().equals(Module.JAVA_WEB_APPLICATION)) {
 383  0
       extension = ".war";
 384  0
     } else if (module.getType().equals(Module.JAVA_ENTERPRISE_APPLICATION)) {
 385  0
       extension = ".ear";
 386  0
     } else if (module.getType().equals(Module.JAVA_SOURCE_MODULE)) {
 387  0
       extension = ".jar";
 388  0
     } else if (module.getType().equals(Module.OTHER_MODULE)) {
 389  0
       extension = ".zip";
 390  0
     } else {
 391  0
       extension = "";
 392  
     }
 393  0
     return resolveArtifactName(module) + extension;
 394  0
   }
 395  
 
 396  
 
 397  
   /**
 398  
    * Traverses the modules' dependencies, and traverses all module dependencies as well (recursively), calculating
 399  
    * the set of dependencies that are unique.  A dependency is not unique if the artifact-name already exists in the
 400  
    * set but with another version. This will result in a DependencyException.
 401  
    *
 402  
    * @param module
 403  
    *
 404  
    * @return A <code>Set</code> containing all dependencies, all the way down to the lowest
 405  
    */
 406  
   public Set getAllLevels(Module module) throws ManifestException, DependencyException {
 407  0
     return getLevels(module, null);
 408  
   }
 409  
 
 410  
   // Method that is called recursively to dive into a modules' dependency tree.
 411  
   //
 412  
   private Set getLevels(Module module, Set currentSet) throws ManifestException, DependencyException{
 413  
 
 414  0
     if (currentSet == null) {
 415  0
       currentSet = new HashSet();
 416  
     }
 417  
 
 418  0
     Set moduleDeps = module.getDependencies();
 419  
 
 420  0
     Iterator i = moduleDeps.iterator();
 421  0
     while (i.hasNext()) {
 422  
 
 423  0
       ModuleDependency dep = (ModuleDependency) i.next();
 424  
 
 425  0
       if (!currentSet.add(dep)) {
 426  
 //        todo ???????????????????????????????????????????????
 427  
 //        throw new DependencyException(DependencyException.DUPLICATE_ARTIFACT_VERSION);
 428  
       } else {
 429  0
         if (dep.isModuleDependency()) {
 430  0
           Module moduleDep = manifest.getModule(dep.getModule());
 431  0
           currentSet.addAll(getLevels(moduleDep, currentSet));
 432  
         }
 433  
       }
 434  
     }
 435  0
     return currentSet;
 436  
   }
 437  
 
 438  
 }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.