com.butterfill.plsqlcore.maven.Extract.java Source code

Java tutorial

Introduction

Here is the source code for com.butterfill.plsqlcore.maven.Extract.java

Source

/**
 * Copyright (C) 2008 Peter Butterfill.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.butterfill.plsqlcore.maven;

import com.butterfill.plsqlcore.maven.xml.PlsqlCoreIncludes;
import com.butterfill.plsqlcore.maven.xml.PlsqlCoreModule;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;

/**
 * Provides access to the extract functionality of plsql-core.
 * 
 * @goal Extract
 */
public class Extract extends AbstractMojo {

    /**
     * The JAXB un-marshaller - used when reading xml input.
     */
    private final Unmarshaller jaxbUnmarshaller;

    /**
     * The plsql-core includes file which can be null.
     * 
     * @parameter expression="${includesFile}"
     */
    private File includesFile;

    /**
     * The name of the output directory.
     * setOutputDirectory(String) will not set this to null.
     * 
     * @parameter expression="${outputDirectory}"
     */
    private String outputDirectory = "";

    /**
     * Flag to enable/disable execution of this task.
     * 
     * @parameter expression="${execute}"
     */
    private boolean execute = true;

    /**
     * Flag to enable/disable execution of this task when no network 
     * connection can be found.
     * 
     * @parameter expression="${executeIfNoNetwork}"
     */
    private boolean executeIfNoNetwork = false;

    /** 
     * Creates a new instance of Extract setting-up the JAXB un-marshaller 
     * used by this instance.
     * 
     * @throws Exception
     *   If we fail to set-up the JAXB un-marshaller used by this instance.
     */
    public Extract() throws Exception {
        jaxbUnmarshaller = JAXBContext.newInstance(PlsqlCoreIncludes.class, PlsqlCoreModule.class)
                .createUnmarshaller();

    }

    /**
     * Creates a directory with the specified name.
     * <br/>
     * There are 4 possible scenarios:
     * <ul>
     * <li>The directory exists. This is ok. No action needed,</li>
     * <li>Something that is not a directory exists with the specified name. This is an error,</li>
     * <li>The directory exists and we can create it or</li>
     * <li>The directory exists but we can't create it. This is an error.</li>
     * </ul>
     * 
     * @param name 
     *   The name of the directory to create.
     * @throws java.lang.Exception
     *   If something that is not a directory exists with the specified name
     *   or we fail to create the directory.
     */
    private void mkDir(final String name) throws Exception {
        final File dir = new File(outputDirectory + name);

        if (dir.exists()) {
            if (dir.isDirectory()) {
                getLog().info("directory exists: " + dir.getCanonicalPath());

            } else {
                throw new MojoExecutionException(dir.getCanonicalPath() + " is not a directory");

            }

        } else {
            if (dir.mkdirs()) {
                getLog().info("directory created: " + dir.getCanonicalPath());

            } else {
                throw new MojoExecutionException("failed to create directory: " + dir.getCanonicalPath());

            }

        }

    }

    /**
     * Returns the specified location in a standard format.
     * Leading and trailing white-space will be removed and a forward slash is
     * appended if the specified location does not already end with a forward 
     * slash.
     * 
     * @param location
     *   The location to format.
     * @return
     *   The specified location in a standard format.
     */
    private String formatLocation(String location) {
        location = location.trim();
        if (!location.endsWith("/")) {
            location += "/";
        }
        return location;
    }

    /**
     * Returns the module name taken from the specified module location.
     * Module locations always end with the module name.
     * <br/>
     * e.g.
     * constants is the module name for the location
     * http://plsql-core.googlecode.com/svn/trunk/plsql-core/src/main/sql/constants/
     * @param moduleLocation
     *   The module location.
     * @return
     *   The name of the module taken from the specified module location.
     */
    private String getModuleName(final String moduleLocation) {
        String[] bits = moduleLocation.split("/");
        return bits[bits.length - 1];
    }

    /**
     * Adds the PlsqlCoreModule identified by moduleLocation 
     * (and all of it's dependencies) to moduleMap.
     * If moduleMap already has an entry keyed by moduleLocation, this is a no-op.
     * 
     * @param moduleLocation
     *   The location of the module to add.
     * @param moduleMap
     *   The map to which the module should be added.
     * @throws java.lang.Exception
     *   If we fail to add the module. 
     *   This could be because we can't read the plsql-core-module.xml file 
     *   (which should be in the specified location).
     */
    private void addModule(final String moduleLocation, final Map<String, PlsqlCoreModule> moduleMap)
            throws Exception {

        getLog().debug("addModule() moduleLocation: " + moduleLocation);

        if (moduleMap.containsKey(moduleLocation)) {
            getLog().debug("not adding duplicate module location: " + moduleLocation);
            return;
        }

        URL baseUrl = new URL(moduleLocation);

        URLConnection urlConnection = new URL(baseUrl, "plsql-core-module.xml").openConnection();

        urlConnection.setUseCaches(false);

        PlsqlCoreModule plsqlCoreModule = (PlsqlCoreModule) jaxbUnmarshaller
                .unmarshal(urlConnection.getInputStream());

        moduleMap.put(moduleLocation, plsqlCoreModule);

        if (plsqlCoreModule.getDependencies() == null) {
            getLog().debug("module has no dependencies");
            return;
        }

        List<PlsqlCoreModule.Dependencies.Dependency> dependencies = plsqlCoreModule.getDependencies()
                .getDependency();

        for (PlsqlCoreModule.Dependencies.Dependency dependency : dependencies) {
            String dependencyLocation = formatLocation(dependency.getLocation());
            if (!moduleMap.containsKey(dependencyLocation)) {
                addModule(dependencyLocation, moduleMap);

            }

        }

    }

    /**
     * Calls ExtractorMain#extract having set the output directory name, 
     * passing in the includes file if it has been provided.
     * 
     * @see #setIncludesFile(java.io.File)
     * @see #setOutputDirectory(java.io.File) 
     * @throws org.apache.maven.plugin.MojoExecutionException
     *   If the extract fails.
     * @throws org.apache.maven.plugin.MojoFailureException
     *   If the extract fails.
     */
    public void execute() throws MojoExecutionException, MojoFailureException {

        if (!execute) {
            getLog().info("Execution has been disabled. Not extracting");
            return;
        }

        if (!executeIfNoNetwork) {
            try {
                URLConnection urlConnection = new URL("http://www.sun.com").openConnection();
                urlConnection.setUseCaches(false);
                urlConnection.getInputStream().close();
                // if we can get the input stream, the network connection is ok
                getLog().debug("Network OK");

            } catch (Exception ex) {
                // assume that any exceptions thrown mean we have no network connection
                getLog().warn("No Network (failed to connect to http://www.sun.com). Not extracting");
                return;

            }

        }

        try {
            // set-up a map to hold all modules that we'll extract.
            // each module will be keyed by it's location - so we can avoid adding duplicates
            Map<String, PlsqlCoreModule> moduleMap = new HashMap<String, PlsqlCoreModule>();

            // read the includes file via JAXB
            PlsqlCoreIncludes plsqlCoreIncludes = (PlsqlCoreIncludes) jaxbUnmarshaller.unmarshal(includesFile);

            // add each location to be included to the module map.
            // add module should take care of adding depencies
            for (String location : plsqlCoreIncludes.getPlsqlCoreModuleLocation()) {
                addModule(formatLocation(location), moduleMap);

            }

            // extract each module in the module map
            for (Map.Entry<String, PlsqlCoreModule> entry : moduleMap.entrySet()) {
                // get the location from the PlsqlCoreModule
                String location = formatLocation(entry.getValue().getLocation());

                // make sure the location from the PlsqlCoreModule matches the
                // loaction given in the includes file
                if (!entry.getKey().equals(location)) {
                    throw new MojoExecutionException("[Location Mismatch] " + includesFile.getName() + ":\""
                            + entry.getKey() + "\" plsql-core-module.xml:\"" + location + "\"");
                }

                // get the name of the module
                String moduleName = getModuleName(location);

                // setup output dir for this module
                mkDir(moduleName);

                // extract each file listed in the modules file set
                for (String fileName : entry.getValue().getFileSet().getFile()) {
                    getLog().info("Extracting: " + location + fileName);

                    URL baseUrl = new URL(location);

                    URLConnection urlConnection = new URL(baseUrl, fileName).openConnection();

                    urlConnection.setUseCaches(false);

                    BufferedReader reader = new BufferedReader(
                            new InputStreamReader(urlConnection.getInputStream()));

                    BufferedWriter writer = new BufferedWriter(new FileWriter(
                            outputDirectory + File.separator + moduleName + File.separator + fileName));

                    for (String line = reader.readLine(); line != null; line = reader.readLine()) {
                        writer.write(line);
                        writer.newLine();

                    }

                    writer.close();
                    reader.close();

                }

            }

        } catch (Exception ex) {
            // log a warning if the extract failed
            getLog().warn("extract failed", ex);
            // log the output dir
            getLog().info("outputDirectory: " + outputDirectory);
            // the name of the includes file
            try {
                getLog().info("includesFile: " + includesFile.getCanonicalPath());
            } catch (Exception nonCriticalException) {
                getLog().debug("failed to log name of includes file", nonCriticalException);
            }
            // and throw an exception so that the user knows something went wrong
            throw new MojoExecutionException("extract failed", ex);

        }

    }

    /**
     * Returns the includes file to use when extracting.
     * 
     * @return The includes file to use when extracting.
     */
    public File getIncludesFile() {
        return includesFile;
    }

    /**
     * Sets the includes file to use when extracting.
     * 
     * @param file
     *   The includes file to use when extracting.
     */
    public void setIncludesFile(final File file) {
        this.includesFile = file;
    }

    /**
     * Returns the output directory name to use when extracting.
     * 
     * @return The output directory name to use when extracting.
     */
    public String getOutputDirectory() {
        return outputDirectory;
    }

    /**
     * Sets the output directory name to use when extracting.
     * 
     * @param name
     *   The output directory name to use when extracting.
     */
    public void setOutputDirectory(final String name) {
        this.outputDirectory = (name == null) ? "" : name;

        if (this.outputDirectory.length() > 0 && !this.outputDirectory.endsWith(File.separator)
                && !this.outputDirectory.endsWith("/")) {
            this.outputDirectory += File.separator;

        }

    }

    /**
     * Returns true if this goal should execute (the default), false otherwise.
     * @return
     *   true if this goal should execute.
     */
    public boolean isExecute() {
        return execute;
    }

    /**
     * Set to false to disable execution of this goal.
     * 
     * @param execute
     *   Pass false to disable execution of this goal.
     */
    public void setExecute(final boolean execute) {
        this.execute = execute;
    }

    /**
     * Returns true if this goal should attempt to execute even when we have
     * no network connection, false (the default) otherwise.
     * @return
     *   true if this goal should attempt to execute even when we have no 
     *   netowrk connection.
     */
    public boolean isExecuteIfNoNetwork() {
        return executeIfNoNetwork;
    }

    /**
     * Set to true to make this goal attempt to execute even when we have
     * no network connection.
     * 
     * @param executeIfNoNetwork
     *   Pass true to make this goal attempt to execute even when we have
     *   no network connection.
     */
    public void setExecuteIfNoNetwork(final boolean executeIfNoNetwork) {
        this.executeIfNoNetwork = executeIfNoNetwork;
    }

} // End of class Extract