nl.toolforge.karma.core.module.BaseModule.java Source code

Java tutorial

Introduction

Here is the source code for nl.toolforge.karma.core.module.BaseModule.java

Source

/*
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.module;

import net.sf.sillyexceptions.OutOfTheBlueException;
import nl.toolforge.core.util.file.MyFileUtils;
import nl.toolforge.karma.core.KarmaRuntimeException;
import nl.toolforge.karma.core.Version;
import nl.toolforge.karma.core.history.ModuleHistory;
import nl.toolforge.karma.core.history.ModuleHistoryEvent;
import nl.toolforge.karma.core.history.ModuleHistoryException;
import nl.toolforge.karma.core.history.ModuleHistoryFactory;
import nl.toolforge.karma.core.location.Location;
import nl.toolforge.karma.core.manifest.ManifestException;
import nl.toolforge.karma.core.module.template.ModuleLayoutTemplate;
import nl.toolforge.karma.core.scm.digester.ModuleDependencyCreationFactory;
import nl.toolforge.karma.core.vc.AuthenticationException;
import nl.toolforge.karma.core.vc.Authenticator;
import nl.toolforge.karma.core.vc.Authenticators;
import nl.toolforge.karma.core.vc.DevelopmentLine;
import nl.toolforge.karma.core.vc.PatchLine;
import nl.toolforge.karma.core.vc.RunnerFactory;
import nl.toolforge.karma.core.vc.VersionControlException;
import nl.toolforge.karma.core.vc.cvsimpl.CVSRunner;
import org.apache.commons.digester.Digester;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.SAXException;

import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.PatternSyntaxException;

/**
 * The name says it all. This class is the base (template) for a module.
 *
 * @author D.A. Smedes
 * @version $Id$
 */
public abstract class BaseModule implements Module {

    protected static Log logger = LogFactory.getLog(BaseModule.class);

    private Location location = null;
    private String name = null;

    private File baseDir = null;

    private Version version = null;
    private Type moduleType = Module.UNKNOWN;
    private Set dependencies = new HashSet();
    private long dependencyXmlTimestamp = 0L;

    private boolean patchLine = false;
    private boolean developmentLine = false;

    public BaseModule(String name, Location location, Version version) {
        this(name, location);
        this.version = version;
    }

    public BaseModule(String name, Location location) {

        if (!name.matches(ModuleDigester.NAME_PATTERN_STRING)) {
            throw new PatternSyntaxException(
                    "Pattern mismatch for 'name'. Should match " + ModuleDigester.NAME_PATTERN_STRING, name, -1);
        }
        if (location == null) {
            throw new IllegalArgumentException("Location cannot be null.");
        }

        this.name = name;
        this.location = location;
    }

    /**
     * Gets the modules' name.
     *
     * @see Module#getName
     */
    public final String getName() {
        return name;
    }

    /**
     * Gets the modules' location.
     *
     * @return See {@link nl.toolforge.karma.core.location.Location}, and all implementing classes.
     */
    public final Location getLocation() {
        return location;
    }

    public boolean equals(Object obj) {

        if (obj instanceof BaseModule) {
            if (((BaseModule) obj).getName().equals(getName())
                    && ((BaseModule) obj).getLocation().equals(getLocation())) {
                return true;
            }
        }
        return false;
    }

    public int hashCode() {
        return getName().hashCode() + getLocation().hashCode();
    }

    /**
     * Future functionality. Not yet supported. Returns <code>false</code>.
     *
     * @return <code>false</code>.
     */
    public final boolean hasDevelopmentLine() {
        return false;
    }

    public final void markDevelopmentLine(boolean mark) {
        developmentLine = mark;
    }

    public final DevelopmentLine getPatchLine() {
        return new PatchLine(getVersion());
    }

    public final void markPatchLine(boolean mark) {
        patchLine = true;
    }

    public final Version getVersion() {
        return version;
    }

    /**
     * If the module element in the manifest contains a <code>version</code> attribute, this method will return the
     * value of that attribute.
     *
     * @return The module version, or <code>N/A</code>, when no version number exists.
     */
    public final String getVersionAsString() {
        return (version == null ? "N/A" : version.getVersionNumber());
    }

    public final boolean hasVersion() {
        return version != null;
    }

    /**
     * Checks if this module has been patched (and is thus part of a <code>ReleaseManifest</code>).
     *
     * @return <code>true</code> when this module has a <code>PatchLine</code> attached to it, <code>false</code> if it
     *         hasn't.
     */
    public final boolean hasPatchLine() {
        return patchLine;
    }

    /**
     * When initialized by <code>AbstractManifest</code>, a module is assigned its base directory, relative to the manifest. The
     * base directory is used internally for base-directory-aware methods.
     *
     * @param baseDir
     */
    public final void setBaseDir(File baseDir) {

        if (baseDir == null) {
            throw new IllegalArgumentException("If you use it, initialize it with a valid 'File' instance ...");
        }
        this.baseDir = baseDir;
    }

    public final File getBaseDir() {

        if (baseDir == null) {
            throw new KarmaRuntimeException("Basedir not set.");
        }
        return baseDir;
    }

    public abstract ModuleLayoutTemplate getLayoutTemplate();

    /**
     *
     * @param createComment
     * @throws VersionControlException
     * @throws AuthenticationException
     */
    public final void createRemote(Authenticator authenticator, String createComment)
            throws AuthenticationException, VersionControlException {

        // Create the layout and return its location.
        //
        File tmpDir = null;
        try {
            tmpDir = MyFileUtils.createTempDirectory();
        } catch (IOException e) {
            throw new KarmaRuntimeException("Could not create temporary directory.");
        }

        File moduleDir = new File(tmpDir, getName());
        moduleDir.mkdir();

        // Create the modules' layout
        //
        try {
            getLayoutTemplate().createLayout(moduleDir);
        } catch (IOException e) {
            // todo
            e.printStackTrace();
        }

        logger.debug("Created layout for module `" + getName() + "`");

        // Add the module to the version control system
        //
        setBaseDir(moduleDir);

        ModuleDescriptor descriptor = new ModuleDescriptor(this);
        try {
            descriptor.createFile(moduleDir);
        } catch (IOException e) {
            // todo
            e.printStackTrace();
        }

        // Prepare the module history
        //
        ModuleHistory history = null;
        try {
            history = ModuleHistoryFactory.getInstance(getBaseDir()).getModuleHistory(this);
        } catch (ModuleHistoryException e) {
            throw new OutOfTheBlueException("Module history does not yet exist, so this is impossible.");
        }

        ModuleHistoryEvent event = new ModuleHistoryEvent();
        event.setType(ModuleHistoryEvent.CREATE_MODULE_EVENT);
        event.setVersion(Version.INITIAL_VERSION);
        event.setDatetime(new Date());

        // Is a requirement.
        //
        Authenticator a = Authenticators.getAuthenticator(authenticator);

        event.setAuthor(a.getUsername());
        event.setComment(createComment);
        history.addEvent(event);
        try {
            history.save();
        } catch (ModuleHistoryException mhe) {
            logger.error("Troubles when saving the module history.", mhe);
        }

        CVSRunner runner = (CVSRunner) RunnerFactory.getRunner(getLocation());
        runner.addModule(this, createComment);

        try {
            FileUtils.deleteDirectory(tmpDir);
        } catch (IOException e) {
            logger.warn("Could not remove temporary directory. It is probably still locked by the OS.");
        }
    }

    /**
     * Reads <code>module-descriptor.xml</code>-file from the module base directory. Once read,
     * the module type is cached.
     * <p>If the base directory does not exist, <code>Module.UNKNOWN</code> is returned.
     *
     * @return The module type.
     * @throws nl.toolforge.karma.core.module.ModuleTypeException When <code>module-descriptor</code> is non-existing. This is possible when the
     *   module is not locally available.
     */
    public final Type getType() throws ModuleTypeException {

        try {
            getBaseDir();
        } catch (KarmaRuntimeException k) {
            return Module.UNKNOWN;
        }

        File moduleDescriptor = new File(getBaseDir(), Module.MODULE_DESCRIPTOR);
        if (!moduleDescriptor.exists()) {
            throw new ModuleTypeException(ModuleTypeException.MISSING_MODULE_DESCRIPTOR);
        }

        if (moduleType.equals(Module.UNKNOWN)) {
            Digester digester = new Digester();

            digester.addObjectCreate("module-descriptor", Module.Type.class);
            digester.addCallMethod("module-descriptor/type", "setType", 0);

            try {
                moduleType = (Type) digester.parse(moduleDescriptor.getPath());
            } catch (IOException e) {
                throw new ModuleTypeException(ModuleTypeException.INVALID_MODULE_DESCRIPTOR);
            } catch (SAXException e) {
                throw new ModuleTypeException(ModuleTypeException.INVALID_MODULE_DESCRIPTOR);
            }
        }
        return moduleType;
    }

    /**
     * See {@link Module#getDependencies}. This implementation throws a <code>KarmaRuntimeException</code> when the
     *  modules' <code>dependencies.xml</code> could not be parsed properly. When no dependencies have been specified, or
     * when the file does not exist, the method returns an empty <code>Set</code>.
     * <p>
     * The method only rereads the dependencies when the dependencies.xml has been changed.
     *
     * @return A <code>Set</code> containing {@link nl.toolforge.karma.core.scm.ModuleDependency} instances.
     */
    public final Set getDependencies() {

        File dependencyXml = new File(getBaseDir(), "dependencies.xml");

        if (!dependencyXml.exists()) {
            dependencies = new HashSet();
        } else if (dependencyXml.lastModified() != dependencyXmlTimestamp) {
            // Read in the base dependency structure of a Maven project.xml file
            //
            Digester digester = new Digester();

            digester.addObjectCreate("*/dependencies", HashSet.class);
            digester.addFactoryCreate("*/dependency", ModuleDependencyCreationFactory.class);
            digester.addSetNext("*/dependency", "add");

            try {

                dependencyXmlTimestamp = dependencyXml.lastModified();
                dependencies = (Set) digester.parse(dependencyXml);
            } catch (IOException e) {
                dependencies = new HashSet();
            } catch (SAXException e) {
                throw new KarmaRuntimeException(ManifestException.DEPENDENCY_FILE_LOAD_ERROR,
                        new Object[] { getName() });
            }
        }

        return dependencies;
    }

    /**
     * Returns the module name.
     *
     * @return
     */
    public String toString() {
        return getName();
    }

}