Coverage report

  %line %branch
nl.toolforge.karma.core.boot.WorkingContextConfiguration
60% 
95% 

 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.boot;
 20  
 
 21  
 import nl.toolforge.karma.core.ErrorCode;
 22  
 import nl.toolforge.karma.core.location.Location;
 23  
 import nl.toolforge.karma.core.location.LocationDescriptor;
 24  
 import nl.toolforge.karma.core.location.LocationException;
 25  
 import nl.toolforge.karma.core.location.LocationFactory;
 26  
 import org.apache.commons.digester.Digester;
 27  
 import org.apache.commons.logging.Log;
 28  
 import org.apache.commons.logging.LogFactory;
 29  
 import org.xml.sax.SAXException;
 30  
 
 31  
 import java.io.BufferedWriter;
 32  
 import java.io.File;
 33  
 import java.io.FileWriter;
 34  
 import java.io.IOException;
 35  
 import java.io.Writer;
 36  
 import java.text.MessageFormat;
 37  
 import java.util.ArrayList;
 38  
 import java.util.Enumeration;
 39  
 import java.util.Iterator;
 40  
 import java.util.List;
 41  
 import java.util.Properties;
 42  
 
 43  
 /**
 44  
  * <p>Configuration class for a <code>WorkingContext</code>. A <code>WorkingContextConfiguration</code> consists of the
 45  
  * following configuration:
 46  
  *
 47  
  * <ul>
 48  
  *   <li>Properties that can be used by Karma. Three properties are mandatory :
 49  
  *     <ul>
 50  
  *       <li>
 51  
  *         <code>project.baseDir</code>, indicating the directory where projects for a working context are stored.
 52  
  *       </li>
 53  
  *       <li>
 54  520
  *         <code>manifest-store.module</code>, which is the module name for the manifest store in a version control
 55  
  *         system. Note that the format of the module name might be version control system dependent.
 56  720
  *       </li>
 57  
  *       <li>
 58  720
  *         <code>location-store.module</code>, which is the module name for the location store in a version control
 59  720
  *         system. Note that the format of the module name might be version control system dependent.
 60  
  *       </li>
 61  720
  *     </ul>
 62  
  *   <li/>
 63  720
  *   <li>A manifest store <code>Location</code>, describing the version control system where the
 64  
  *       <code>manifest-store.module</code> module can be found.
 65  720
  *   <li/>
 66  720
  *   <li>A location store <code>Location</code>, describing the version control system where the
 67  720
  *       <code>location-store.module</code> module can be found.
 68  720
  *   <li/>
 69  
  * </ul>
 70  
  *
 71  10
  * <p>The configuration is not loaded automatically. By calling the {@link #load()}-method the configuration will be
 72  
  * loaded.
 73  
  *
 74  
  * @author D.A. Smedes
 75  0
  * @version $Id: WorkingContextConfiguration.java,v 1.6 2004/11/03 20:54:14 asmedes Exp $
 76  0
  */
 77  
 public final class WorkingContextConfiguration {
 78  
 
 79  180
   public static final ErrorCode CONFIGURATION_LOAD_ERROR = new ErrorCode("WCC-00001");
 80  12
 
 81  690
   private static Log logger = LogFactory.getLog(WorkingContextConfiguration.class);
 82  46
 
 83  45
 //  private File workingContextConfigurationFile = null;
 84  35
 
 85  525
   private ManifestStore manifestStore = null;
 86  560
   private LocationStore locationStore = null;
 87  35
 
 88  525
   private Properties configuration = null;
 89  35
 
 90  525
   private WorkingContext workingContext = null;
 91  35
 
 92  
 
 93  7330
 
 94  
   /**
 95  
    * Creates a configuration object using <code>configFile</code> as the configuration file. The configuration is
 96  
    * loaded by calling {@link #load()}.
 97  7330
    *
 98  
    * @param workingContext The <code>WorkingContext</code> for this configuration.
 99  
    */
 100  525
   public WorkingContextConfiguration(WorkingContext workingContext) {
 101  35
 
 102  525
     if (workingContext == null) {
 103  50
       throw new IllegalArgumentException("Working context cannot be null.");
 104  1
     }
 105  384
 
 106  894
     this.workingContext = workingContext;
 107  544
     configuration = new Properties();
 108  510
   }
 109  
 
 110  64
   private File getConfigFile() {
 111  960
     return new File(workingContext.getWorkingContextConfigurationBaseDir(), "working-context.xml");
 112  
   }
 113  
 
 114  63
   /**
 115  63
    * Returns the <code>LocationStore</code> for this configuration. Can be <code>null</code> if not configured properly.
 116  
    *
 117  
    * @return The <code>LocationStore</code> for this configuration. Can be <code>null</code> if not configured properly.
 118  710
    */
 119  0
   public LocationStore getLocationStore() {
 120  448
     return locationStore;
 121  
   }
 122  710
 
 123  0
   /**
 124  710
    * Returns the <code>ManifestStore</code> for this configuration. Can be <code>null</code> if not configured properly.
 125  
    *
 126  63
    * @return The <code>ManifestStore</code> for this configuration. Can be <code>null</code> if not configured properly.
 127  773
    */
 128  
   public ManifestStore getManifestStore() {
 129  602
     return manifestStore;
 130  
   }
 131  
 
 132  
   /**
 133  
    * Sets the manifest store for this configuration. <code>null</code>s are allowed.
 134  
    *
 135  
    * @param manifestStore The manifest store for this configuration.
 136  710
    */
 137  
   public void setManifestStore(ManifestStore manifestStore) {
 138  837
     this.manifestStore = manifestStore;
 139  2967
   }
 140  2840
 
 141  
   /**
 142  127
    * Sets the location store for this configuration. <code>null</code>s are allowed.
 143  127
    *
 144  
    * @param locationStore The location store for this configuration.
 145  710
    */
 146  
   public void setLocationStore(LocationStore locationStore) {
 147  710
     this.locationStore = locationStore;
 148  0
   }
 149  
 
 150  710
 
 151  
   /**
 152  32
    *
 153  64
    * @param key
 154  32
    * @return <code>null</code> if the property is not found.
 155  
    */
 156  
   public String getProperty(String key) {
 157  
 
 158  1778
     if (configuration == null) {
 159  710
       return null;
 160  
     }
 161  710
 
 162  1778
     return configuration.getProperty(key);
 163  
   }
 164  
 
 165  
 
 166  
 
 167  
   /**
 168  710
    * Adds or changes a property in the current configuration. When a property with key <code>name</code> exists, its
 169  
    * value is overwritten with <code>value</code>.
 170  730
    */
 171  
   public void setProperty(String name, String value) {
 172  1178
     configuration.setProperty(name, value);
 173  448
   }
 174  730
 
 175  
 
 176  
 
 177  
   /**
 178  730
    * Thorough checks of this configuration is valid, and if it is not, returns the <code>ErrorCode</code> to indicate
 179  
    * what went wrong or <code>null</code> if nothing went wrong, and this configuration is ready to use.
 180  730
    *
 181  710
    * @return An <code>ErrorCode</code> indicating the exact failure or <code>null</code> of nothing it wrong.
 182  710
    */
 183  
   public ErrorCode check() {
 184  
 
 185  710
     try {
 186  690
       if (load()) {
 187  690
         // ok
 188  20
       }
 189  0
     } catch (WorkingContextException e) {
 190  20
       return CONFIGURATION_LOAD_ERROR;
 191  20
     }
 192  20
 
 193  0
     return null;
 194  
   }
 195  20
 
 196  
 
 197  
 
 198  20
 
 199  
 
 200  32
   /**
 201  32
    * <p>Loads the configuration from <code>working-context.xml</code>. When the file did not exists, <code>false</code>
 202  
    * will be returned. Otherwise, this method returns <code>true</code> and the configuration succeeded. Note that
 203  
    * this method performs no validation on the configuration itself. This is left to the user.
 204  32
    *
 205  32
    * <p>When the configuration could be loaded, this method returns <code>true</code>. This is not to say that the
 206  32
    * configuration is correct. The configuration should still be checked for correctness by the client.
 207  52
    *
 208  
    * <p>Calling this method overwrites any properties already set for this configuration.
 209  32
    *
 210  32
    * @return                         <code>true</code> when the configuration could be loaded, <code>false</code> if it
 211  20
    *                                 couldn't.
 212  
    *
 213  
    * @throws WorkingContextException When an <code>IOException</code> or <code>SAXException</code> occurs, indicating
 214  
    *                                 an error when reading a configuration file, which could result by the client in
 215  
    *                                 specific actions (thus the exception and not the <code>true</code> or
 216  32
    *                                 <code>false</code>.
 217  32
    */
 218  32
   public boolean load() throws WorkingContextException {
 219  160
 
 220  704
     configuration = new Properties();
 221  128
 
 222  
     // Read properties
 223  
     //
 224  448
     Digester propertyDigester = getPropertyDigester();
 225  32
 
 226  480
     List properties = null;
 227  32
 
 228  32
     try {
 229  448
       properties = (List) propertyDigester.parse(getConfigFile());
 230  32
     } catch (SAXException e) {
 231  32
       logger.error(e);
 232  0
       throw new WorkingContextException("XML error in configuration for working context `" + workingContext + "`. ", e);
 233  0
     } catch (IOException e) {
 234  0
       logger.error(e);
 235  0
       throw new WorkingContextException(e);
 236  448
     }
 237  32
 
 238  480
     for (Iterator i = properties.iterator(); i.hasNext();) {
 239  1824
       Property property = (Property) i.next();
 240  1824
       configuration.put(property.getName(), property.getValue());
 241  
     }
 242  
 
 243  
     // Read manifests-store and location-store data
 244  
     //
 245  480
     Digester locationDigester = LocationDescriptor.getDigester();
 246  32
 
 247  512
     List locations = null;
 248  64
 
 249  64
     try {
 250  512
       locations = (List) locationDigester.parse(getConfigFile());
 251  64
     } catch (SAXException e) {
 252  64
       logger.error(e);
 253  0
       throw new WorkingContextException("XML error in configuration for working context `" + workingContext + "`. ", e);
 254  64
     } catch (IOException e) {
 255  64
       logger.error(e);
 256  64
       throw new WorkingContextException(e);
 257  544
     }
 258  32
 
 259  480
     if (locations == null) {
 260  64
       return false;
 261  32
     }
 262  
 
 263  
     // Expecting exactly two items !!! Only 'manifest-store' and 'location-store' are accepted.
 264  
     //
 265  448
     for (Iterator i = locations.iterator(); i.hasNext();) {
 266  
 
 267  896
       LocationDescriptor descriptor = (LocationDescriptor) i.next();
 268  
 
 269  896
       Location location = null;
 270  32
       try {
 271  928
         location = LocationFactory.getInstance().createLocation(descriptor);
 272  0
       } catch (LocationException e) {
 273  0
         return false;
 274  928
       }
 275  32
 
 276  896
       location.setWorkingContext(workingContext);
 277  
 
 278  896
       if ("manifest-store".equals(location.getId())) {
 279  
 
 280  448
         String moduleName = (String) configuration.get(WorkingContext.MANIFEST_STORE_MODULE);
 281  448
         manifestStore = new ManifestStore(workingContext, moduleName, location);
 282  
 
 283  448
       } else if ("location-store".equals(location.getId())) {
 284  
 
 285  448
         String moduleName = (String) configuration.get(WorkingContext.LOCATION_STORE_MODULE);
 286  448
         locationStore = new LocationStore(workingContext, moduleName, location);
 287  
 
 288  
       } else {
 289  0
         logger.error(
 290  
             "Invalid location element in `working-context.xml`. " +
 291  
             "Expecting a `manifest-store` and `location-store` entry.");
 292  0
         return false;
 293  
       }
 294  
     }
 295  
 
 296  448
     if (manclass="keyword">ifestStore == null || locationStore == null) {
 297  0
       return false;
 298  710
     }
 299  
 
 300  1158
     return true;
 301  
   }
 302  710
 
 303  
   /**
 304  710
    * Stores the all configuration items in <code>configuration</code> in the <code>working-context.xml</code> file.
 305  710
    *
 306  710
    * @throws WorkingContextException When storing failed.
 307  
    */
 308  710
   public void store() throws WorkingContextException {
 309  
 
 310  
     // todo this implementation can be made more efficient, but it's ok for now.
 311  
 
 312  0
     if (configuration == null) {
 313  0
       throw new NullPointerException("Configuration cannot be null.");
 314  
     }
 315  
 
 316  
     // Check the (mandatory) configuration
 317  
     //
 318  
 
 319  0
     StringBuffer buffer = new StringBuffer();
 320  0
     buffer.append("<?xml version=\ŕ.0\"?>\n");
 321  
 
 322  0
     buffer.append(
 323  
         "<wc:working-context\n" +
 324  
         "  xmlns:wc=\"http://www.toolforge.org/specifications/working-context\"\n" +
 325  
         "  xmlns:loc=\"http://www.toolforge.org/specifications/location\">\n");
 326  
 
 327  
     // <properties>-element
 328  
     //
 329  0
     buffer.append("  <wc:properties>\n");
 330  
 
 331  0
     MessageFormat formatter = new MessageFormat("    <wc:property name=\"{0}\" value=\"{1}\"/>\n");
 332  
 
 333  0
     Enumeration e = configuration.propertyNames();
 334  
 
 335  0
     while (e.hasMoreElements()) {
 336  
 
 337  0
       String key = (String) e.nextElement();
 338  0
       String value = configuration.getProperty(key);
 339  
 
 340  0
       buffer.append(formatter.format(new String[]{key, value}));
 341  
     }
 342  
 
 343  0
     buffer.append("  </wc:properties>\n\n");
 344  
     //
 345  
     // </properties>-element
 346  
 
 347  0
     if (manclass="keyword">ifestStore == null && locationStore == null) {
 348  
       //
 349  
     } else {
 350  
 
 351  0
       buffer.append("  <loc:locations>\n");
 352  
 
 353  0
       if (manclass="keyword">ifestStore != null) {
 354  0
         buffer.append(manifestStore.getLocation().asXML());
 355  
       }
 356  0
       if (locationStore != null) {
 357  0
         buffer.append(locationStore.getLocation().asXML());
 358  
       }
 359  
 
 360  0
       buffer.append("  </loc:locations>\n");
 361  
 
 362  
     }
 363  
 
 364  0
     buffer.append("\n");
 365  
 
 366  0
     buffer.append("</wc:working-context>\n");
 367  
 
 368  32
     // Write to file `working-context.xml`
 369  32
     //
 370  32
     try {
 371  32
       if (!getConfigFile().exists()) {
 372  32
         getConfigFile().createNewFile();
 373  32
       }
 374  32
       Writer writer = new BufferedWriter(class="keyword">new FileWriter(getConfigFile()));
 375  64
       writer.write(buffer.toString());
 376  64
       writer.flush();
 377  32
     } catch (IOException ioe) {
 378  32
       logger.error(ioe);
 379  32
       throw new WorkingContextException(ioe);
 380  0
     }
 381  0
   }
 382  
 
 383  
 
 384  
 
 385  
   /*
 386  
   * The working-context configuration file is populated with elements from two different namespaces. One is the
 387  
   * working context stuff itself (all sorts of properties), the other is from the <code>location</code> namespace,
 388  
   * configuration for the manifest store and the location store.
 389  
   *
 390  
   * <p>This method gets a <code>Digester</code> for the general properties for the working context.
 391  
   */
 392  
   private Digester getPropertyDigester() {
 393  
 
 394  448
     Digester digester = new Digester();
 395  
 
 396  448
     digester.setNamespaceAware(true);
 397  
 
 398  448
     digester.addObjectCreate("working-context/properties", ArrayList.class);
 399  
 
 400  448
     digester.addObjectCreate("working-context/properties/property", Property.class);
 401  448
     digester.addSetProperties("working-context/properties/property");
 402  448
     digester.addSetNext("working-context/properties/property", "add");
 403  
 
 404  448
     return digester;
 405  
   }
 406  
 }

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