org.reficio.p2.P2Mojo.java Source code

Java tutorial

Introduction

Here is the source code for org.reficio.p2.P2Mojo.java

Source

/**
 * Copyright (c) 2012 Reficio (TM) - Reestablish your software! All Rights Reserved.
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.reficio.p2;

import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.AbstractMojoExecutionException;
import org.apache.maven.plugin.BuildPluginManager;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.context.Context;
import org.codehaus.plexus.context.ContextException;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
import org.eclipse.sisu.equinox.EquinoxServiceFactory;
import org.eclipse.sisu.equinox.launching.internal.P2ApplicationLauncher;
import org.reficio.p2.bundler.ArtifactBundler;
import org.reficio.p2.bundler.ArtifactBundlerInstructions;
import org.reficio.p2.bundler.ArtifactBundlerRequest;
import org.reficio.p2.bundler.impl.AquteBundler;
import org.reficio.p2.logger.Logger;
import org.reficio.p2.publisher.BundlePublisher;
import org.reficio.p2.publisher.CategoryPublisher;
import org.reficio.p2.resolver.ArtifactResolutionRequest;
import org.reficio.p2.resolver.ArtifactResolutionResult;
import org.reficio.p2.resolver.ArtifactResolver;
import org.reficio.p2.resolver.ResolvedArtifact;
import org.reficio.p2.resolver.impl.AetherResolver;
import org.reficio.p2.utils.JarUtils;
import org.reficio.p2.utils.Utils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;

/**
 * Main plugin class
 *
 * @author Tom Bujok (tom.bujok@gmail.com)<br>
 *         Reficio (TM) - Reestablish your software!<br>
 *         http://www.reficio.org
 * @since 1.0.0
 */
@Mojo(name = "site", defaultPhase = LifecyclePhase.COMPILE, requiresDependencyResolution = ResolutionScope.RUNTIME, requiresDependencyCollection = ResolutionScope.RUNTIME)
public class P2Mojo extends AbstractMojo implements Contextualizable {

    private static final String BUNDLES_TOP_FOLDER = "/source";
    private static final String FEATURES_DESTINATION_FOLDER = BUNDLES_TOP_FOLDER + "/features";
    private static final String BUNDLES_DESTINATION_FOLDER = BUNDLES_TOP_FOLDER + "/plugins";
    private static final String DEFAULT_CATEGORY_FILE = "category.xml";
    private static final String DEFAULT_CATEGORY_CLASSPATH_LOCATION = "/";

    @Parameter(defaultValue = "${project}", required = true, readonly = true)
    private MavenProject project;

    @Parameter(defaultValue = "${session}", required = true, readonly = true)
    private MavenSession session;

    /**
     * @component
     * @required
     */
    @Component
    @Requirement
    private BuildPluginManager pluginManager;

    @Parameter(defaultValue = "${project.build.directory}", required = true)
    private String buildDirectory;

    @Parameter(defaultValue = "${project.build.directory}/repository", required = true)
    private String destinationDirectory;

    /**
     * @component
     * @required
     */
    @Component
    @Requirement
    private EquinoxServiceFactory p2;

    /**
     * @component
     * @required
     */
    @Component
    @Requirement
    private P2ApplicationLauncher launcher;

    /**
     * Specifies a file containing category definitions.
     *
     */
    @Parameter(defaultValue = "")
    private String categoryFileURL;

    /**
     * Optional line of additional arguments passed to the p2 application launcher.
     *
     */
    @Parameter(defaultValue = "false")
    private boolean pedantic;

    /**
     * Specifies whether to compress generated update site.
     *
     */
    @Parameter(defaultValue = "true")
    private boolean compressSite;

    /**
     * Kill the forked process after a certain number of seconds. If set to 0, wait forever for the
     * process, never timing out.
     *
     */
    @Parameter(defaultValue = "0", alias = "p2.timeout")
    private int forkedProcessTimeoutInSeconds;

    /**
     * Specifies additional arguments to p2Launcher, for example -consoleLog -debug -verbose
     *
     * @parameter default-value=""
     */
    @Parameter(defaultValue = "")
    private String additionalArgs;

    /**
     * Dependency injection container - used to get some components programatically
     */
    private PlexusContainer container;

    /**
     * Aether Repository System
     * Declared as raw Object type as different objects are injected in different Maven versions:
     * * 3.0.0 and above -> org.sonatype.aether...
     * * 3.1.0 and above -> org.eclipse.aether...
     */
    private Object repoSystem;

    /**
     * The current repository/network configuration of Maven.
     *
     */
    @Parameter(defaultValue = "${repositorySystemSession}", readonly = true, required = true)
    private Object repoSession;

    /**
     * The project's remote repositories to use for the resolution of project dependencies.
     *
     * @parameter default-value="${project.remoteProjectRepositories}"
     * @required
     * @readonly
     */
    @Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly = true, required = true)
    private List<Object> projectRepos;

    /**
     */
    @Parameter(readonly = true)
    private List<P2Artifact> artifacts;

    /**
     * A list of artifacts that are eclipse features
     * 
     */
    @Parameter(readonly = true)
    private List<P2FeatureArtifact> featureArtifacts;

    /**
     * A list of definitions of eclipse features
     * 
     */
    @Parameter(readonly = true)
    private List<P2FeatureDefinition> featureDefinitions;

    /**
     * Logger retrieved from the Maven internals.
     * It's the recommended way to do it...
     */
    private Log log = getLog();

    /**
     * Folder which the jar files bundled by the ArtifactBundler will be copied to
     */
    private File bundlesDestinationFolder;

    /**
     * Folder which the feature jar files bundled by the ArtifactBundler will be copied to
     */
    private File featuresDestinationFolder;

    /**
     * Processing entry point.
     * Method that orchestrates the execution of the plugin.
     */
    @Override
    public void execute() {
        try {
            initializeEnvironment();
            initializeRepositorySystem();
            processArtifacts(this.artifacts);
            processFeatures();
            executeP2PublisherPlugin();
            executeCategoryPublisher();
            cleanupEnvironment();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void initializeEnvironment() throws IOException {
        Logger.initialize(log);
        bundlesDestinationFolder = new File(buildDirectory, BUNDLES_DESTINATION_FOLDER);
        featuresDestinationFolder = new File(buildDirectory, FEATURES_DESTINATION_FOLDER);
        FileUtils.deleteDirectory(new File(buildDirectory, BUNDLES_TOP_FOLDER));
        bundlesDestinationFolder.mkdirs();
        featuresDestinationFolder.mkdirs();

    }

    private void initializeRepositorySystem() {
        if (repoSystem == null) {
            repoSystem = lookup("org.eclipse.aether.RepositorySystem");
        }
        if (repoSystem == null) {
            repoSystem = lookup("org.sonatype.aether.RepositorySystem");
        }
        Preconditions.checkNotNull(repoSystem, "Could not initialize RepositorySystem");
    }

    private Object lookup(String role) {
        try {
            return container.lookup(role);
        } catch (ComponentLookupException ex) {
        }
        return null;
    }

    private Map<P2Artifact, ArtifactBundlerInstructions> processArtifacts(List<P2Artifact> artifacts) {
        Map<P2Artifact, ArtifactBundlerInstructions> bundlerInstructions = new HashMap<P2Artifact, ArtifactBundlerInstructions>();
        // first resolve all artifacts
        Multimap<P2Artifact, ResolvedArtifact> resolvedArtifacts = resolveArtifacts(artifacts);
        // then bundle the artifacts including the transitive dependencies (if specified so)
        if (null != artifacts) {
            for (P2Artifact p2Artifact : artifacts) {
                for (ResolvedArtifact resolvedArtifact : resolvedArtifacts.get(p2Artifact)) {
                    String timestamp = Utils.getTimeStamp();
                    ArtifactBundlerInstructions abi = bundleArtifact(p2Artifact, resolvedArtifact, timestamp);
                    bundlerInstructions.put(p2Artifact, abi);
                }
            }
        }
        return bundlerInstructions;
    }

    private void processFeatures() {
        // artifacts should already have been resolved by processArtifacts()
        Multimap<P2FeatureArtifact, ResolvedArtifact> resolvedFeatures = resolveFeatures();
        // then bundle the features including the transitive dependencies (if specified so)
        log.info("Resolved " + resolvedFeatures.size() + " feature artifacts");
        if (null != featureArtifacts) {
            for (P2FeatureArtifact p2Feature : featureArtifacts) {
                for (ResolvedArtifact resolvedArtifact : resolvedFeatures.get(p2Feature)) {
                    handleFeatureArtifact(p2Feature, resolvedArtifact);
                }
            }
        }
        if (null != featureDefinitions) {
            for (P2FeatureDefinition p2Feature : featureDefinitions) {
                this.createFeature(p2Feature);
            }
        }
    }

    private Multimap<P2Artifact, ResolvedArtifact> resolveArtifacts(List<P2Artifact> artifacts) {
        Multimap<P2Artifact, ResolvedArtifact> resolvedArtifacts = ArrayListMultimap.create();
        if (null != artifacts) {
            for (P2Artifact p2Artifact : artifacts) {
                logResolving(p2Artifact);
                ArtifactResolutionResult resolutionResult = resolveArtifact(p2Artifact);
                resolvedArtifacts.putAll(p2Artifact, resolutionResult.getResolvedArtifacts());
            }
        }
        return resolvedArtifacts;
    }

    private Multimap<P2FeatureArtifact, ResolvedArtifact> resolveFeatures() {
        Multimap<P2FeatureArtifact, ResolvedArtifact> resolvedFeatureArtifacts = ArrayListMultimap.create();
        if (null != featureArtifacts) {
            for (P2FeatureArtifact p2Feature : featureArtifacts) {
                logResolving(p2Feature);
                ArtifactResolutionResult resolutionResult = resolveFeatureArtifact(p2Feature);
                resolvedFeatureArtifacts.putAll(p2Feature, resolutionResult.getResolvedArtifacts());
            }
        }
        return resolvedFeatureArtifacts;
    }

    private void logResolving(IP2Artifact p2) {
        log.info(String.format("Resolving artifact=[%s] transitive=[%s] source=[%s]", p2.getId(),
                p2.shouldIncludeTransitive(), p2.shouldIncludeSources()));
    }

    private ArtifactResolutionResult resolveArtifact(P2Artifact p2Artifact) {
        ArtifactResolutionRequest resolutionRequest = ArtifactResolutionRequest.builder()
                .rootArtifactId(p2Artifact.getId()).resolveSource(p2Artifact.shouldIncludeSources())
                .resolveTransitive(p2Artifact.shouldIncludeTransitive()).excludes(p2Artifact.getExcludes()).build();
        ArtifactResolutionResult resolutionResult = getArtifactResolver().resolve(resolutionRequest);
        logResolved(resolutionRequest, resolutionResult);
        return resolutionResult;
    }

    private ArtifactResolutionResult resolveFeatureArtifact(P2FeatureArtifact p2Artifact) {
        ArtifactResolutionRequest resolutionRequest = ArtifactResolutionRequest.builder()
                .rootArtifactId(p2Artifact.getId()).resolveSource(p2Artifact.shouldIncludeSources())
                .resolveTransitive(p2Artifact.shouldIncludeTransitive()).excludes(p2Artifact.getExcludes()).build();
        ArtifactResolutionResult resolutionResult = getArtifactResolver().resolve(resolutionRequest);
        logResolved(resolutionRequest, resolutionResult);
        return resolutionResult;
    }

    private ArtifactResolver getArtifactResolver() {
        return new AetherResolver(repoSystem, repoSession, projectRepos);
    }

    private void logResolved(ArtifactResolutionRequest resolutionRequest,
            ArtifactResolutionResult resolutionResult) {
        for (ResolvedArtifact resolvedArtifact : resolutionResult.getResolvedArtifacts()) {
            log.info("\t [JAR] " + resolvedArtifact.getArtifact());
            if (resolvedArtifact.getSourceArtifact() != null) {
                log.info("\t [SRC] " + resolvedArtifact.getSourceArtifact().toString());
            } else if (resolutionRequest.isResolveSource()) {
                log.warn("\t [SRC] Failed to resolve source for artifact "
                        + resolvedArtifact.getArtifact().toString());
            }
        }
    }

    private void handleFeatureArtifact(P2FeatureArtifact p2FeatureArtifact, ResolvedArtifact resolvedArtifact) {
        log.debug("Handling feature artifact" + p2FeatureArtifact.getId());
        ArtifactBundlerRequest bundlerRequest = P2Helper.createBundlerRequest(p2FeatureArtifact, resolvedArtifact,
                featuresDestinationFolder);
        try {
            File inputFile = bundlerRequest.getBinaryInputFile();
            File outputFile = bundlerRequest.getBinaryOutputFile();
            //This will also copy the input to the output
            String timestamp = Utils.getTimeStamp();
            JarUtils.adjustFeatureXml(inputFile, outputFile, this.bundlesDestinationFolder, log, timestamp);

            log.info("Copied " + inputFile + " to " + outputFile);
        } catch (Exception ex) {
            throw new RuntimeException(
                    "Error while bundling jar or source: " + bundlerRequest.getBinaryInputFile().getName(), ex);
        }
    }

    private void createFeature(P2FeatureDefinition p2featureDefinition) {
        try {
            Map<P2Artifact, ArtifactBundlerInstructions> bi = this
                    .processArtifacts(p2featureDefinition.getArtifacts());

            if (null == p2featureDefinition.getFeatureFile()) {
                //we must be generating the feature file from the pom
                String timestamp = Utils.getTimeStamp();
                p2featureDefinition.setVersion(Utils.mavenToEclipse(p2featureDefinition.getVersion(), timestamp));

                FeatureBuilder featureBuilder = new FeatureBuilder(p2featureDefinition, bi);
                featureBuilder.generate(this.featuresDestinationFolder);
                if (p2featureDefinition.getGenerateSourceFeature()) {
                    featureBuilder.generateSourceFeature(this.featuresDestinationFolder);
                }
            } else {
                //given a feature file, so build using tycho
                File basedir = p2featureDefinition.getFeatureFile().getParentFile();
                TychoFeatureBuilder builder = new TychoFeatureBuilder(p2featureDefinition.getFeatureFile(),
                        this.featuresDestinationFolder.getAbsolutePath(), "test.feature", "1.0.0", project,
                        this.session, this.pluginManager);
                builder.execute();
            }

            log.info("Created feature " + p2featureDefinition.getId());

        } catch (Exception e) {
            log.error(e);
        }
    }

    private ArtifactBundlerInstructions bundleArtifact(P2Artifact p2Artifact, ResolvedArtifact resolvedArtifact,
            String timestamp) {
        log.info("Bundling Artifact " + p2Artifact.getId());
        P2Validator.validateBundleRequest(p2Artifact, resolvedArtifact);
        ArtifactBundler bundler = getArtifactBundler();
        ArtifactBundlerInstructions bundlerInstructions = P2Helper.createBundlerInstructions(p2Artifact,
                resolvedArtifact, timestamp);
        ArtifactBundlerRequest bundlerRequest = P2Helper.createBundlerRequest(p2Artifact, resolvedArtifact,
                bundlesDestinationFolder);
        bundler.execute(bundlerRequest, bundlerInstructions);
        return bundlerInstructions;
    }

    private ArtifactBundler getArtifactBundler() {
        return new AquteBundler(pedantic);
    }

    private void executeP2PublisherPlugin() throws IOException, MojoExecutionException {
        prepareDestinationDirectory();
        BundlePublisher publisher = BundlePublisher.builder().mavenProject(project).mavenSession(session)
                .buildPluginManager(pluginManager).compressSite(compressSite).additionalArgs(additionalArgs)
                .build();
        publisher.execute();
    }

    private void prepareDestinationDirectory() throws IOException {
        FileUtils.deleteDirectory(new File(destinationDirectory));
    }

    private void executeCategoryPublisher() throws AbstractMojoExecutionException, IOException {
        prepareCategoryLocationFile();
        CategoryPublisher publisher = CategoryPublisher.builder().p2ApplicationLauncher(launcher)
                .additionalArgs(additionalArgs).forkedProcessTimeoutInSeconds(forkedProcessTimeoutInSeconds)
                .categoryFileLocation(categoryFileURL).metadataRepositoryLocation(destinationDirectory).build();
        publisher.execute();
    }

    private void prepareCategoryLocationFile() throws IOException {
        if (StringUtils.isBlank(categoryFileURL)) {
            InputStream is = getClass()
                    .getResourceAsStream(DEFAULT_CATEGORY_CLASSPATH_LOCATION + DEFAULT_CATEGORY_FILE);
            File destinationFolder = new File(destinationDirectory);
            destinationFolder.mkdirs();
            File categoryDefinitionFile = new File(destinationFolder, DEFAULT_CATEGORY_FILE);
            FileWriter writer = new FileWriter(categoryDefinitionFile);
            IOUtils.copy(is, writer, "UTF-8");
            IOUtils.closeQuietly(writer);
            categoryFileURL = categoryDefinitionFile.getAbsolutePath();
        }
    }

    private void cleanupEnvironment() throws IOException {
        File workFolder = new File(buildDirectory, BUNDLES_TOP_FOLDER);
        try {
            FileUtils.deleteDirectory(workFolder);
        } catch (IOException ex) {
            log.warn("Cannot cleanup the work folder " + workFolder.getAbsolutePath());
        }
    }

    @Override
    public void contextualize(Context context) throws ContextException {
        this.container = (PlexusContainer) context.get(PlexusConstants.PLEXUS_KEY);
    }

}