Java tutorial
/* Karma core - Core of the Karma application Copyright (C) 2004 Toolforge <www.toolforge.nl> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package nl.toolforge.karma.core.boot; import nl.toolforge.karma.core.ErrorCode; import nl.toolforge.karma.core.location.Location; import nl.toolforge.karma.core.location.LocationDescriptor; import nl.toolforge.karma.core.location.LocationException; import nl.toolforge.karma.core.location.LocationFactory; import org.apache.commons.digester.Digester; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.xml.sax.SAXException; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Properties; /** * <p>Configuration class for a <code>WorkingContext</code>. A <code>WorkingContextConfiguration</code> consists of the * following configuration: * * <ul> * <li>Properties that can be used by Karma. Three properties are mandatory : * <ul> * <li> * <code>project.baseDir</code>, indicating the directory where projects for a working context are stored. * </li> * <li> * <code>manifest-store.module</code>, which is the module name for the manifest store in a version control * system. Note that the format of the module name might be version control system dependent. * </li> * <li> * <code>location-store.module</code>, which is the module name for the location store in a version control * system. Note that the format of the module name might be version control system dependent. * </li> * </ul> * <li/> * <li>A manifest store <code>Location</code>, describing the version control system where the * <code>manifest-store.module</code> module can be found. * <li/> * <li>A location store <code>Location</code>, describing the version control system where the * <code>location-store.module</code> module can be found. * <li/> * </ul> * * <p>The configuration is not loaded automatically. By calling the {@link #load()}-method the configuration will be * loaded. * * @author D.A. Smedes * @version $Id$ */ public final class WorkingContextConfiguration { public static final ErrorCode CONFIGURATION_LOAD_ERROR = new ErrorCode("WCC-00001"); private static Log logger = LogFactory.getLog(WorkingContextConfiguration.class); // private File workingContextConfigurationFile = null; private ManifestStore manifestStore = null; private LocationStore locationStore = null; private Properties configuration = null; private WorkingContext workingContext = null; /** * Creates a configuration object using <code>configFile</code> as the configuration file. The configuration is * loaded by calling {@link #load()}. * * @param workingContext The <code>WorkingContext</code> for this configuration. */ public WorkingContextConfiguration(WorkingContext workingContext) { if (workingContext == null) { throw new IllegalArgumentException("Working context cannot be null."); } this.workingContext = workingContext; configuration = new Properties(); } private File getConfigFile() { return new File(workingContext.getWorkingContextConfigurationBaseDir(), "working-context.xml"); } /** * Returns the <code>LocationStore</code> for this configuration. Can be <code>null</code> if not configured properly. * * @return The <code>LocationStore</code> for this configuration. Can be <code>null</code> if not configured properly. */ public LocationStore getLocationStore() { return locationStore; } /** * Returns the <code>ManifestStore</code> for this configuration. Can be <code>null</code> if not configured properly. * * @return The <code>ManifestStore</code> for this configuration. Can be <code>null</code> if not configured properly. */ public ManifestStore getManifestStore() { return manifestStore; } /** * Sets the manifest store for this configuration. <code>null</code>s are allowed. * * @param manifestStore The manifest store for this configuration. */ public void setManifestStore(ManifestStore manifestStore) { this.manifestStore = manifestStore; } /** * Sets the location store for this configuration. <code>null</code>s are allowed. * * @param locationStore The location store for this configuration. */ public void setLocationStore(LocationStore locationStore) { this.locationStore = locationStore; } /** * * @param key * @return <code>null</code> if the property is not found. */ public String getProperty(String key) { if (configuration == null) { return null; } return configuration.getProperty(key); } /** * Adds or changes a property in the current configuration. When a property with key <code>name</code> exists, its * value is overwritten with <code>value</code>. */ public void setProperty(String name, String value) { configuration.setProperty(name, value); } /** * Thorough checks of this configuration is valid, and if it is not, returns the <code>ErrorCode</code> to indicate * what went wrong or <code>null</code> if nothing went wrong, and this configuration is ready to use. * * @return An <code>ErrorCode</code> indicating the exact failure or <code>null</code> of nothing it wrong. */ public ErrorCode check() { try { if (load()) { // ok } } catch (WorkingContextException e) { return CONFIGURATION_LOAD_ERROR; } return null; } /** * <p>Loads the configuration from <code>working-context.xml</code>. When the file did not exists, <code>false</code> * will be returned. Otherwise, this method returns <code>true</code> and the configuration succeeded. Note that * this method performs no validation on the configuration itself. This is left to the user. * * <p>When the configuration could be loaded, this method returns <code>true</code>. This is not to say that the * configuration is correct. The configuration should still be checked for correctness by the client. * * <p>Calling this method overwrites any properties already set for this configuration. * * @return <code>true</code> when the configuration could be loaded, <code>false</code> if it * couldn't. * * @throws WorkingContextException When an <code>IOException</code> or <code>SAXException</code> occurs, indicating * an error when reading a configuration file, which could result by the client in * specific actions (thus the exception and not the <code>true</code> or * <code>false</code>. */ public boolean load() throws WorkingContextException { configuration = new Properties(); // Read properties // Digester propertyDigester = getPropertyDigester(); List properties = null; try { properties = (List) propertyDigester.parse(getConfigFile()); } catch (SAXException e) { logger.error(e); throw new WorkingContextException( "XML error in configuration for working context `" + workingContext + "`. ", e); } catch (IOException e) { logger.error(e); throw new WorkingContextException(e); } for (Iterator i = properties.iterator(); i.hasNext();) { Property property = (Property) i.next(); configuration.put(property.getName(), property.getValue()); } // Read manifests-store and location-store data // Digester locationDigester = LocationDescriptor.getDigester(); List locations = null; try { locations = (List) locationDigester.parse(getConfigFile()); } catch (SAXException e) { logger.error(e); throw new WorkingContextException( "XML error in configuration for working context `" + workingContext + "`. ", e); } catch (IOException e) { logger.error(e); throw new WorkingContextException(e); } if (locations == null) { return false; } // Expecting exactly two items !!! Only 'manifest-store' and 'location-store' are accepted. // for (Iterator i = locations.iterator(); i.hasNext();) { LocationDescriptor descriptor = (LocationDescriptor) i.next(); Location location = null; try { location = LocationFactory.getInstance().createLocation(descriptor); } catch (LocationException e) { return false; } location.setWorkingContext(workingContext); if ("manifest-store".equals(location.getId())) { String moduleName = (String) configuration.get(WorkingContext.MANIFEST_STORE_MODULE); manifestStore = new ManifestStore(workingContext, moduleName, location); } else if ("location-store".equals(location.getId())) { String moduleName = (String) configuration.get(WorkingContext.LOCATION_STORE_MODULE); locationStore = new LocationStore(workingContext, moduleName, location); } else { logger.error("Invalid location element in `working-context.xml`. " + "Expecting a `manifest-store` and `location-store` entry."); return false; } } if (manifestStore == null || locationStore == null) { return false; } return true; } /** * Stores the all configuration items in <code>configuration</code> in the <code>working-context.xml</code> file. * * @throws WorkingContextException When storing failed. */ public void store() throws WorkingContextException { // todo this implementation can be made more efficient, but it's ok for now. if (configuration == null) { throw new NullPointerException("Configuration cannot be null."); } // Check the (mandatory) configuration // StringBuffer buffer = new StringBuffer(); buffer.append("<?xml version=\"1.0\"?>\n"); buffer.append( "<wc:working-context\n" + " xmlns:wc=\"http://www.toolforge.org/specifications/working-context\"\n" + " xmlns:loc=\"http://www.toolforge.org/specifications/location\">\n"); // <properties>-element // buffer.append(" <wc:properties>\n"); MessageFormat formatter = new MessageFormat(" <wc:property name=\"{0}\" value=\"{1}\"/>\n"); Enumeration e = configuration.propertyNames(); while (e.hasMoreElements()) { String key = (String) e.nextElement(); String value = configuration.getProperty(key); buffer.append(formatter.format(new String[] { key, value })); } buffer.append(" </wc:properties>\n\n"); // // </properties>-element if (manifestStore == null && locationStore == null) { // } else { buffer.append(" <loc:locations>\n"); if (manifestStore != null) { buffer.append(manifestStore.getLocation().asXML()); } if (locationStore != null) { buffer.append(locationStore.getLocation().asXML()); } buffer.append(" </loc:locations>\n"); } buffer.append("\n"); buffer.append("</wc:working-context>\n"); // Write to file `working-context.xml` // try { if (!getConfigFile().exists()) { getConfigFile().createNewFile(); } Writer writer = new BufferedWriter(new FileWriter(getConfigFile())); writer.write(buffer.toString()); writer.flush(); } catch (IOException ioe) { logger.error(ioe); throw new WorkingContextException(ioe); } } /* * The working-context configuration file is populated with elements from two different namespaces. One is the * working context stuff itself (all sorts of properties), the other is from the <code>location</code> namespace, * configuration for the manifest store and the location store. * * <p>This method gets a <code>Digester</code> for the general properties for the working context. */ private Digester getPropertyDigester() { Digester digester = new Digester(); digester.setNamespaceAware(true); digester.addObjectCreate("working-context/properties", ArrayList.class); digester.addObjectCreate("working-context/properties/property", Property.class); digester.addSetProperties("working-context/properties/property"); digester.addSetNext("working-context/properties/property", "add"); return digester; } }