org.xwiki.tool.packager.PackageMojo.java Source code

Java tutorial

Introduction

Here is the source code for org.xwiki.tool.packager.PackageMojo.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This 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 software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.tool.packager;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectBuilder;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.runtime.RuntimeConstants;
import org.codehaus.plexus.archiver.zip.ZipUnArchiver;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.logging.console.ConsoleLogger;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.StringUtils;
import org.hibernate.cfg.Environment;
import org.xwiki.velocity.internal.log.SLF4JLogChute;

import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.tool.backup.Importer;

/**
 * Create a runnable XWiki instance using Jetty as the Servlet Container and HSQLDB as the Database.
 * 
 * @version $Id: c3f10c9396c2328f72e0b1420973267fcbc983a0 $
 * @since 3.4M1
 * @goal package
 * @phase package
 * @requiresProject
 * @requiresDependencyResolution runtime
 * @threadSafe
 */
public class PackageMojo extends AbstractMojo {
    /**
     * The maven project.
     * 
     * @parameter expression="${project}"
     * @required
     * @readonly
     */
    protected MavenProject project;

    /**
     * List of Remote Repositories used by the resolver.
     * 
     * @parameter expression="${project.remoteArtifactRepositories}"
     * @readonly
     * @required
     */
    protected List<ArtifactRepository> remoteRepos;

    /**
     * Project builder -- builds a model from a pom.xml.
     * 
     * @component role="org.apache.maven.project.MavenProjectBuilder"
     * @required
     * @readonly
     */
    protected MavenProjectBuilder mavenProjectBuilder;

    /**
     * Used to look up Artifacts in the remote repository.
     * 
     * @component
     */
    protected ArtifactFactory factory;

    /**
     * Used to look up Artifacts in the remote repository.
     * 
     * @component
     */
    protected ArtifactResolver resolver;

    /**
     * The directory where to create the packaging.
     * 
     * @parameter default-value="${project.build.directory}/xartmp"
     * @required
     */
    private File tmpXarDirectory;

    /**
     * The directory where to create the packaging.
     * 
     * @parameter default-value="${project.build.directory}/xwiki"
     * @required
     */
    private File outputPackageDirectory;

    /**
     * The directory where classes are put.
     * 
     * @parameter default-value="${project.build.outputDirectory}"
     * @required
     */
    private File outputClassesDirectory;

    /**
     * The directory where the HSQLDB database is generated.
     * 
     * @parameter default-value="${project.build.directory}/database"
     * @required
     */
    private File databaseDirectory;

    /**
     * Location of the local repository.
     * 
     * @parameter expression="${localRepository}"
     * @readonly
     * @required
     */
    private ArtifactRepository local;

    /**
     * @component
     */
    private ArtifactMetadataSource metadataSource;

    /**
     * The user under which the import should be done. If not user is specified then we import with backup pack.
     * For example {@code superadmin}.
     *
     * @parameter
     */
    private String importUser;

    /**
     * List of skin artifacts to include in the packaging.
     * 
     * @parameter
     */
    private List<SkinArtifactItem> skinArtifactItems;

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        // Step 1: Expand Jetty resources into the package output directory.
        getLog().info("Expanding Jetty Resources ...");
        expandJettyDistribution();

        // Step 2: Get the WAR dependencies and expand them in the package output directory.
        getLog().info("Expanding WAR dependencies ...");
        File webappsDirectory = new File(this.outputPackageDirectory, "webapps");
        for (Map.Entry<String, Artifact> warArtifactEntry : resolveWarArtifacts().entrySet()) {
            getLog().info("  ... Unzipping WAR: " + warArtifactEntry.getValue().getFile());
            unzip(warArtifactEntry.getValue().getFile(), new File(webappsDirectory, warArtifactEntry.getKey()));
        }

        // Step 3: Copy all JARs dependencies to the expanded WAR directory in WEB-INF/lib
        getLog().info("Copying JAR dependencies ...");
        File xwikiWebappDirectory = new File(webappsDirectory, "xwiki");
        File webInfDirectory = new File(xwikiWebappDirectory, "WEB-INF");
        File libDirectory = new File(webInfDirectory, "lib");
        createDirectory(libDirectory);
        for (Artifact artifact : resolveJarArtifacts()) {
            getLog().info("  ... Copying JAR: " + artifact.getFile());
            copyFile(artifact.getFile(), libDirectory);
        }

        // Step 4: Copy compiled classes in the WEB-INF/Classes directory. This allows the tests to provide custom
        // code, for example to override existing components for the test purpose. As an example the link
        // checker might want to override the HTTP Checker component so that checks are not done over the
        // internet since the tests need to execute in a stable environment to prevent false positives.
        getLog().info("Copying Java Classes ...");
        File classesDirectory = new File(webInfDirectory, "classes");
        if (this.outputClassesDirectory.exists()) {
            copyDirectory(this.outputClassesDirectory, classesDirectory);
        }

        // Step 5: Generate and copy config files.
        getLog().info("Copying Configuration files ...");
        generateConfigurationFiles(webInfDirectory);

        // Step 6: Copy HSQLDB JDBC Driver
        getLog().info("Copying HSQLDB JDBC Driver JAR ...");
        Artifact hsqldbArtifact = resolveHSQLDBArtifact();
        copyFile(hsqldbArtifact.getFile(), libDirectory);

        // Step 7: Unzip the specified Skins. If no skin is specified then unzip the Colibri skin only.
        getLog().info("Copying Skins ...");
        File skinsDirectory = new File(xwikiWebappDirectory, "skins");
        if (this.skinArtifactItems != null) {
            for (SkinArtifactItem skinArtifactItem : this.skinArtifactItems) {
                Artifact skinArtifact = resolveArtifactItem(skinArtifactItem);
                unzip(skinArtifact.getFile(), skinsDirectory);
            }
        } else {
            Artifact colibriArtifact = resolveArtifact("org.xwiki.platform", "xwiki-platform-colibri",
                    this.project.getVersion(), "zip");
            unzip(colibriArtifact.getFile(), skinsDirectory);
        }

        // Step 8: Import specified XAR files into the database
        getLog().info(String.format("Import XAR dependencies %s...",
                this.importUser == null ? "as a backup pack" : "using user [" + this.importUser + "]"));
        importXARs(webInfDirectory);
    }

    private void expandJettyDistribution() throws MojoExecutionException {
        Artifact jettyArtifact = resolveJettyArtifact();
        unzip(jettyArtifact.getFile(), this.outputPackageDirectory);

        // Replace maven properties in start shell scripts
        VelocityContext context = createVelocityContext();
        Collection<File> startFiles = org.apache.commons.io.FileUtils.listFiles(this.outputPackageDirectory,
                new WildcardFileFilter("start_xwiki*.*"), null);

        // Note: Init is done once even if this method is called several times...
        Velocity.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new SLF4JLogChute());
        Velocity.init();
        for (File startFile : startFiles) {
            getLog().info(String.format("  Replacing variables in [%s]...", startFile));
            try {
                String content = org.apache.commons.io.FileUtils.readFileToString(startFile);
                OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(startFile));
                Velocity.evaluate(context, writer, "", content);
                writer.close();
            } catch (Exception e) {
                // Failed to read or write file...
                throw new MojoExecutionException(
                        String.format("Failed to process start shell script [%s]", startFile), e);
            }
        }
    }

    private Artifact resolveArtifactItem(ArtifactItem artifactItem) throws MojoExecutionException {
        // Resolve the version and the type:
        // - if specified in the artifactItem, use them
        // - if not specified look for them in the project dependencies
        String version = artifactItem.getVersion();
        String type = artifactItem.getType();
        if (version == null || type == null) {
            Map<String, Artifact> artifacts = this.project.getArtifactMap();
            String key = ArtifactUtils.versionlessKey(artifactItem.getGroupId(), artifactItem.getArtifactId());
            if (artifacts.containsKey(key)) {
                if (version == null) {
                    version = artifacts.get(key).getVersion();
                }
                if (type == null) {
                    type = artifacts.get(key).getType();
                }
            } else {
                // Default to the project's version
                if (version == null) {
                    version = this.project.getVersion();
                }
                // Default to JAR
                if (type == null) {
                    type = "jar";
                }
            }
        }

        // Resolve the artifact
        Artifact artifact = this.factory.createArtifact(artifactItem.getGroupId(), artifactItem.getArtifactId(),
                version, "", type);
        resolveArtifact(artifact);
        return artifact;
    }

    private void generateConfigurationFiles(File configurationFileTargetDirectory) throws MojoExecutionException {
        VelocityContext context = createVelocityContext();
        Artifact configurationResourcesArtifact = this.factory.createArtifact("org.xwiki.platform",
                "xwiki-platform-tool-configuration-resources", this.project.getVersion(), "", "jar");
        resolveArtifact(configurationResourcesArtifact);

        configurationFileTargetDirectory.mkdirs();

        try {
            JarInputStream jarInputStream = new JarInputStream(
                    new FileInputStream(configurationResourcesArtifact.getFile()));
            JarEntry entry;
            while ((entry = jarInputStream.getNextJarEntry()) != null) {
                if (entry.getName().endsWith(".vm")) {

                    String fileName = entry.getName().replace(".vm", "");
                    File outputFile = new File(configurationFileTargetDirectory, fileName);
                    OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(outputFile));
                    getLog().info("Writing config file: " + outputFile);
                    // Note: Init is done once even if this method is called several times...
                    Velocity.init();
                    Velocity.evaluate(context, writer, "", IOUtils.toString(jarInputStream));
                    writer.close();
                    jarInputStream.closeEntry();
                }
            }
            // Flush and close all the streams
            jarInputStream.close();
        } catch (Exception e) {
            throw new MojoExecutionException("Failed to extract configuration files", e);
        }
    }

    private void importXARs(File webInfDirectory) throws MojoExecutionException {
        Set<Artifact> xarArtifacts = resolveXARs();
        if (!xarArtifacts.isEmpty()) {
            Importer importer = new Importer();
            // Make sure that we generate the Database in the right directory
            // TODO: In the future control completely the Hibernate config from inside the packager plugin and not in
            // the project using the packager plugin
            System.setProperty(Environment.URL,
                    "jdbc:hsqldb:file:" + this.databaseDirectory + "/xwiki_db;shutdown=true");

            XWikiContext xcontext;
            try {
                xcontext = importer.createXWikiContext("xwiki", new File(webInfDirectory, "hibernate.cfg.xml"));
            } catch (Exception e) {
                throw new MojoExecutionException("Failed to create context to import XAR files", e);
            }

            for (Artifact xarArtifact : xarArtifacts) {
                getLog().info("  ... Importing XAR file: " + xarArtifact.getFile());

                try {
                    importer.importXAR(xarArtifact.getFile(), this.importUser, xcontext);
                } catch (Exception e) {
                    throw new MojoExecutionException(
                            String.format("Failed to import XAR [%s]", xarArtifact.toString()), e);
                }
            }

            // We MUST shutdown HSQLDB because otherwise the last transactions will not be flushed
            // to disk and will be lost. In practice this means the last Document imported has a
            // very high chance of not making it...
            // TODO: Find a way to implement this generically for all databases and inside
            // XWikiHibernateStore (cf http://jira.xwiki.org/jira/browse/XWIKI-471).
            try {
                importer.shutdownHSQLDB(xcontext);
            } catch (Exception e) {
                throw new MojoExecutionException("Failed to shutdown hsqldb database", e);
            }

            // Copy database files to XWiki's data directory.
            File dataDir = new File(this.outputPackageDirectory, "data");
            copyDirectory(this.databaseDirectory, new File(dataDir, "database"));
        }
    }

    private Set<Artifact> resolveXARs() throws MojoExecutionException {
        Set<Artifact> xarArtifacts = new HashSet<Artifact>();

        Set<Artifact> artifacts = this.project.getArtifacts();
        if (artifacts != null) {
            for (Artifact artifact : artifacts) {
                if (artifact.getType().equals("xar")) {
                    xarArtifacts.add(artifact);
                    resolveArtifact(artifact);
                }
            }
        }

        return xarArtifacts;
    }

    private Artifact resolveHSQLDBArtifact() throws MojoExecutionException {
        Artifact hsqldbArtifact = null;

        Set<Artifact> artifacts = this.project.getArtifacts();
        if (artifacts != null) {
            for (Artifact artifact : artifacts) {
                if (artifact.getType().equals("jar") && artifact.getGroupId().equals("org.hsqldb")
                        && artifact.getArtifactId().equals("hsqldb")) {
                    hsqldbArtifact = artifact;
                    break;
                }
            }
        }

        // If the HSQLDB artifact wasn't defined, try to resolve the default HSQLDB JAR artifact
        if (hsqldbArtifact == null) {
            hsqldbArtifact = this.factory.createArtifact("org.hsqldb", "hsqldb", "2.2.9", "", "jar");
        }

        if (hsqldbArtifact != null) {
            resolveArtifact(hsqldbArtifact);
        } else {
            throw new MojoExecutionException("Failed to locate the HSQLDB artifact in either the project "
                    + "dependency list or using the specific [hsqldb:hsqldb] artifact name");
        }

        return hsqldbArtifact;
    }

    private Artifact resolveJettyArtifact() throws MojoExecutionException {
        Artifact jettyArtifact = null;

        Set<Artifact> artifacts = this.project.getArtifacts();
        if (artifacts != null) {
            for (Artifact artifact : artifacts) {
                if (artifact.getType().equals("zip")
                        && artifact.getArtifactId().equals("xwiki-platform-tool-jetty-resources")) {
                    jettyArtifact = artifact;
                    break;
                }
            }
        }

        // If the Jetty artifact wasn't defined, try to resolve the default Jetty artifact
        if (jettyArtifact == null) {
            jettyArtifact = this.factory.createArtifact("org.xwiki.platform", "xwiki-platform-tool-jetty-resources",
                    this.project.getVersion(), "", "zip");
        }

        if (jettyArtifact != null) {
            resolveArtifact(jettyArtifact);
        } else {
            throw new MojoExecutionException("Failed to locate the Jetty artifact in either the project "
                    + "dependency list or using the specific [xwiki-platform-tool-jetty-resources] artifact name");
        }

        return jettyArtifact;
    }

    private Map<String, Artifact> resolveWarArtifacts() throws MojoExecutionException {
        Map<String, Artifact> warArtifacts = new HashMap<String, Artifact>();

        Set<Artifact> artifacts = this.project.getArtifacts();
        if (artifacts != null) {
            for (Artifact artifact : artifacts) {
                if (artifact.getType().equals("war")) {
                    String id;
                    if (artifact.getArtifactId().equals("xwiki-platform-web")) {
                        id = "xwiki";
                    } else if (artifact.getArtifactId().equals("xwiki-platform-tool-rootwebapp")) {
                        id = "root";
                    } else {
                        id = artifact.getArtifactId();
                    }
                    warArtifacts.put(id, artifact);
                    break;
                }
            }
        }

        // If the WAR artifacts weren't defined, try to resolve the default Web artifacts.
        if (warArtifacts.isEmpty()) {
            warArtifacts.put("xwiki", this.factory.createArtifact("org.xwiki.platform", "xwiki-platform-web",
                    this.project.getVersion(), "", "war"));
            warArtifacts.put("root", this.factory.createArtifact("org.xwiki.platform",
                    "xwiki-platform-tool-rootwebapp", this.project.getVersion(), "", "war"));
        }

        if (!warArtifacts.isEmpty()) {
            for (Artifact warArtifact : warArtifacts.values()) {
                resolveArtifact(warArtifact);
            }
        } else {
            throw new MojoExecutionException("Failed to locate any XWiki WAR artifact in either the project "
                    + "dependency list or using the specific [xwiki-platform-web]/[xwiki-platform-tool-rootwebapp] "
                    + "artifact names");
        }

        return warArtifacts;
    }

    private Collection<Artifact> resolveJarArtifacts() throws MojoExecutionException {
        Set<Artifact> jarArtifacts = new HashSet<Artifact>();

        Set<Artifact> artifacts = this.project.getArtifacts();
        if (artifacts != null) {
            for (Artifact artifact : artifacts) {
                if (artifact.getType().equals("jar")) {
                    jarArtifacts.add(artifact);
                    // Note that we don't need to resolve transitively since getArtifacts() above will already
                    // contain all transitive dependencies.
                    resolveArtifact(artifact);
                }
            }
        }

        // Add mandatory dependencies if they're not explicitly specified.
        jarArtifacts.addAll(getMandatoryJarArtifacts());

        // Resolve all artifacts transitively in one go.
        return resolveTransitively(jarArtifacts);
    }

    private Set<Artifact> getMandatoryJarArtifacts() throws MojoExecutionException {
        Set<Artifact> mandatoryTopLevelArtifacts = new HashSet<Artifact>();

        mandatoryTopLevelArtifacts.add(this.factory.createArtifact("org.xwiki.platform", "xwiki-platform-oldcore",
                this.project.getVersion(), null, "jar"));

        // Required Plugins
        mandatoryTopLevelArtifacts.add(this.factory.createArtifact("org.xwiki.platform",
                "xwiki-platform-skin-skinx", this.project.getVersion(), null, "jar"));

        // We shouldn't need those but right now it's mandatory since they are defined in the default web.xml file we
        // provide. We'll be able to remove them when we start using Servlet 3.0 -->
        mandatoryTopLevelArtifacts.add(this.factory.createArtifact("org.xwiki.platform",
                "xwiki-platform-wysiwyg-server", this.project.getVersion(), null, "jar"));
        mandatoryTopLevelArtifacts.add(this.factory.createArtifact("org.xwiki.platform",
                "xwiki-platform-wysiwyg-client", this.project.getVersion(), null, "jar"));
        mandatoryTopLevelArtifacts.add(this.factory.createArtifact("org.xwiki.platform",
                "xwiki-platform-webdav-server", this.project.getVersion(), null, "jar"));
        mandatoryTopLevelArtifacts.add(this.factory.createArtifact("org.xwiki.platform",
                "xwiki-platform-rest-server", this.project.getVersion(), null, "jar"));
        mandatoryTopLevelArtifacts.add(this.factory.createArtifact("org.xwiki.platform", "xwiki-platform-gwt-api",
                this.project.getVersion(), null, "jar"));

        // Needed by platform-web but since we don't have any dep in platform-web's pom.xml at the moment (duplication
        // issue with XE/XEM and platform-web) we need to include it here FTM... Solution: get a better maven WAR plugin
        // with proper merge feature and then remove this...
        mandatoryTopLevelArtifacts.add(this.factory.createArtifact("org.xwiki.platform",
                "xwiki-platform-uiextension-api", this.project.getVersion(), null, "jar"));
        mandatoryTopLevelArtifacts.add(this.factory.createArtifact("org.xwiki.platform",
                "xwiki-platform-localization-script", this.project.getVersion(), null, "jar"));
        mandatoryTopLevelArtifacts.add(this.factory.createArtifact("org.xwiki.platform",
                "xwiki-platform-localization-source-legacy", this.project.getVersion(), null, "jar"));
        mandatoryTopLevelArtifacts.add(this.factory.createArtifact("org.xwiki.platform",
                "xwiki-platform-security-bridge", this.project.getVersion(), null, "jar"));

        // Ensures all logging goes through SLF4J and Logback.
        mandatoryTopLevelArtifacts.add(this.factory.createArtifact("org.xwiki.commons",
                "xwiki-commons-logging-logback", this.getXWikiCommonsVersion(), null, "jar"));
        // Get the logging artifact versions from the top level XWiki Commons POM
        MavenProject pomProject = getTopLevelPOMProject();
        mandatoryTopLevelArtifacts.add(this.factory.createArtifact("org.slf4j", "jcl-over-slf4j",
                getDependencyManagementVersion(pomProject, "org.slf4j", "jcl-over-slf4j"), null, "jar"));
        mandatoryTopLevelArtifacts.add(this.factory.createArtifact("org.slf4j", "log4j-over-slf4j",
                getDependencyManagementVersion(pomProject, "org.slf4j", "log4j-over-slf4j"), null, "jar"));

        return mandatoryTopLevelArtifacts;
    }

    private Set<Artifact> resolveTransitively(Set<Artifact> artifacts) throws MojoExecutionException {
        Set<Artifact> resolvedArtifacts = new HashSet<Artifact>();
        try {
            AndArtifactFilter filter = new AndArtifactFilter();
            filter.add(new ScopeArtifactFilter("runtime"));

            // - Exclude JCL and LOG4J since we want all logging to go through SLF4J. Note that we're excluding
            // log4j-<version>.jar but keeping log4j-over-slf4j-<version>.jar
            // - Exclude batik-js to prevent conflict with the patched version of Rhino used by yuicompressor used for
            // JSX. See http://jira.xwiki.org/jira/browse/XWIKI-6151 for more details.
            filter.add(new ExcludesArtifactFilter(Arrays.asList("org.apache.xmlgraphic:batik-js",
                    "commons-logging:commons-logging", "commons-logging:commons-logging-api", "log4j:log4j")));

            ArtifactResolutionResult arr = this.resolver.resolveTransitively(artifacts, this.project.getArtifact(),
                    this.project.getManagedVersionMap(), this.local, this.remoteRepos, this.metadataSource, filter);
            resolvedArtifacts.addAll(arr.getArtifacts());
        } catch (Exception e) {
            throw new MojoExecutionException("Failed to resolve mandatory artifacts", e);
        }

        return resolvedArtifacts;
    }

    private MavenProject getTopLevelPOMProject() throws MojoExecutionException {
        MavenProject pomProject;
        Artifact pomArtifact = this.factory.createProjectArtifact("org.xwiki.commons", "xwiki-commons",
                this.getXWikiCommonsVersion());
        try {
            pomProject = this.mavenProjectBuilder.buildFromRepository(pomArtifact, this.remoteRepos, this.local);
        } catch (ProjectBuildingException e) {
            throw new MojoExecutionException(String.format("Failed to build project for [%s]", pomArtifact), e);
        }
        return pomProject;
    }

    /**
     * @return the version of the XWiki Commons project, taken from the {@code commons.version} property, defaulting to
     *         the current project version of this property is not defined
     */
    private String getXWikiCommonsVersion() {
        return this.project.getProperties().getProperty("commons.version", this.project.getVersion());
    }

    private String getDependencyManagementVersion(MavenProject project, String groupId, String artifactId)
            throws MojoExecutionException {
        for (Object dependencyObject : project.getDependencyManagement().getDependencies()) {
            Dependency dependency = (Dependency) dependencyObject;
            if (dependency.getGroupId().equals(groupId) && dependency.getArtifactId().equals(artifactId)) {
                return dependency.getVersion();
            }
        }
        throw new MojoExecutionException(
                String.format("Failed to find artifact [%s:%s] in dependency management " + "for [%s]", groupId,
                        artifactId, project.toString()));
    }

    /**
     * Create the passed directory if it doesn't already exist.
     * 
     * @param directory the directory to create
     */
    private void createDirectory(File directory) {
        if (!directory.exists()) {
            directory.mkdirs();
        }
    }

    private void copyDirectory(File sourceDirectory, File targetDirectory) throws MojoExecutionException {
        createDirectory(targetDirectory);
        try {
            FileUtils.copyDirectoryStructureIfModified(sourceDirectory, targetDirectory);
        } catch (IOException e) {
            throw new MojoExecutionException(
                    String.format("Failed to copy directory [%] to [%]", sourceDirectory, targetDirectory), e);
        }
    }

    private void copyFile(File source, File targetDirectory) throws MojoExecutionException {
        try {
            FileUtils.copyFileToDirectoryIfModified(source, targetDirectory);
        } catch (IOException e) {
            throw new MojoExecutionException(
                    String.format("Failed to copy file [%] to [%]", source, targetDirectory), e);
        }
    }

    private void unzip(File source, File targetDirectory) throws MojoExecutionException {
        createDirectory(targetDirectory);
        try {
            ZipUnArchiver unArchiver = new ZipUnArchiver();
            unArchiver.enableLogging(new ConsoleLogger(Logger.LEVEL_ERROR, "Package"));
            unArchiver.setSourceFile(source);
            unArchiver.setDestDirectory(targetDirectory);
            unArchiver.setOverwrite(true);
            unArchiver.extract();
        } catch (Exception e) {
            throw new MojoExecutionException(
                    String.format("Error unpacking file [%s] into [%s]", source, targetDirectory), e);
        }
    }

    private void resolveArtifact(Artifact artifact) throws MojoExecutionException {
        try {
            this.resolver.resolve(artifact, this.remoteRepos, this.local);
        } catch (Exception e) {
            throw new MojoExecutionException(String.format("Failed to resolve artifact [%s]", artifact), e);
        }
    }

    private Artifact resolveArtifact(String groupId, String artifactId, String version, String type)
            throws MojoExecutionException {
        Artifact artifact = this.factory.createArtifact(groupId, artifactId, version, "", type);
        resolveArtifact(artifact);
        return artifact;
    }

    private VelocityContext createVelocityContext() {
        Properties properties = new Properties();
        properties.putAll(getDefaultConfigurationProperties());
        final Properties projectProperties = this.project.getProperties();
        for (Object key : projectProperties.keySet()) {
            properties.put(key.toString(), projectProperties.get(key).toString());
        }

        VelocityContext context = new VelocityContext(properties);

        String inceptionYear = this.project.getInceptionYear();
        String year = new SimpleDateFormat("yyyy").format(new Date());

        if (StringUtils.isEmpty(inceptionYear)) {
            inceptionYear = year;
        }
        context.put("project", this.project);
        context.put("presentYear", year);

        if (inceptionYear.equals(year)) {
            context.put("projectTimespan", year);
        } else {
            context.put("projectTimespan", inceptionYear + "-" + year);
        }

        return context;
    }

    private Properties getDefaultConfigurationProperties() {
        Properties props = new Properties();

        // Default configuration data for hibernate.cfg.xml
        props.setProperty("xwikiDbConnectionUrl",
                "jdbc:hsqldb:file:${environment.permanentDirectory}/database/xwiki_db;shutdown=true");
        props.setProperty("xwikiDbConnectionUsername", "sa");
        props.setProperty("xwikiDbConnectionPassword", "");
        props.setProperty("xwikiDbConnectionDriverClass", "org.hsqldb.jdbcDriver");
        props.setProperty("xwikiDbDialect", "org.hibernate.dialect.HSQLDialect");
        props.setProperty("xwikiDbHbmXwiki", "xwiki.hbm.xml");
        props.setProperty("xwikiDbHbmFeeds", "feeds.hbm.xml");

        // Default configuration data for xwiki.cfg
        props.setProperty("xwikiCfgPlugins",
                "com.xpn.xwiki.plugin.skinx.JsSkinExtensionPlugin,\\"
                        + "        com.xpn.xwiki.plugin.skinx.JsSkinFileExtensionPlugin,\\"
                        + "        com.xpn.xwiki.plugin.skinx.CssSkinExtensionPlugin,\\"
                        + "        com.xpn.xwiki.plugin.skinx.CssSkinFileExtensionPlugin,\\"
                        + "        com.xpn.xwiki.plugin.skinx.LinkExtensionPlugin");
        props.setProperty("xwikiCfgVirtual", "0");
        props.setProperty("xwikiCfgVirtualUsepath", "0");
        props.setProperty("xwikiCfgEditCommentMandatory", "0");
        props.setProperty("xwikiCfgDefaultSkin", "colibri");
        props.setProperty("xwikiCfgDefaultBaseSkin", "colibri");
        props.setProperty("xwikiCfgEncoding", "UTF-8");

        // Other default configuration properties
        props.setProperty("xwikiDataDir", "data");

        return props;
    }
}