org.opennms.maven.plugins.karaf.GenerateFeaturesXmlMojo.java Source code

Java tutorial

Introduction

Here is the source code for org.opennms.maven.plugins.karaf.GenerateFeaturesXmlMojo.java

Source

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2009-2012 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2012 The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * OpenNMS(R) 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with OpenNMS(R).  If not, see:
 *      http://www.gnu.org/licenses/
 *
 * For more information contact:
 *     OpenNMS(R) Licensing <license@opennms.org>
 *     http://www.opennms.org/
 *     http://www.opennms.com/
 *******************************************************************************/

package org.opennms.maven.plugins.karaf;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.jar.JarFile;

import javax.xml.bind.JAXBException;

import org.apache.karaf.features.BundleInfo;
import org.apache.karaf.features.internal.model.Feature;
import org.apache.karaf.features.internal.model.Features;
import org.apache.karaf.features.internal.model.JaxbUtil;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.util.IOUtil;
import org.opennms.maven.plugins.karaf.utils.MojoSupport;

/**
 * Goal which generates a karaf features.xml from maven
 * 
 * @goal generate-features-xml
 * @phase process-resources
 * @requiresDependencyResolution
 */
@SuppressWarnings("restriction")
public class GenerateFeaturesXmlMojo extends MojoSupport {
    static final List<String> DEFAULT_IGNORED_SCOPES = Collections
            .unmodifiableList(Arrays.asList(new String[] { "test", "provided" }));

    /**
     * (wrapper) The maven project.
     *
     * @parameter expression="${project}"
     * @required
     * @readonly
     */
    MavenProject project;

    /**
     * The maven project's helper.
     *
     * @component
     * @required
     * @readonly
     */
    protected MavenProjectHelper projectHelper;

    /**
     * The maven project's session.
     *
     * @component
     * @required
     * @readonly
     */
    protected PlexusContainer container;

    /**
     * The maven project's session.
     *
     * @component
     * @required
     * @readonly
     */
    protected MavenSession mavenSession;

    /**
     * Location of the file.
     * @parameter expression="${project.build.directory}/features/features.xml"
     */
    private File outputFile;

    /**
     * Fetch the contents of all referenced repositories and include their contents
     * in this feature repository.
     * @parameter
     */
    private boolean importRepositories;

    /**
     * Repository entries to skip when importing repository features into the current repository.
     * The value can either be the name of the repository or the fully-qualified URL of the feature
     * XML resource.
     * @parameter
     */
    private List<String> importRepositoryExclusions;

    /**
     * Name for the repository. This defaults to the artifact-id of the project.
     * @parameter
     */
    private String name;

    /**
     * Repository entries to include in the generated features.xml file.
     * @parameter
     */
    private List<String> repositories;

    /**
     * Configuration entries to include in the generated features.xml file.
     * @parameter
     */
    private List<Config> configs;
    /**
     * Configuration file references to include in the generated features.xml file.
     * @parameter
     */
    private List<Configfile> configfiles;

    /**
     * Features to include in the generated features.xml file.
     * @parameter
     */
    private List<String> features;

    /**
     * Bundles to include in the generated features.xml file.
     * @parameter
     */
    private List<String> bundles;

    /**
     * Scopes to ignore when processing dependencies.
     * @parameter
     */
    private List<String> ignoredScopes;

    /**
     * @parameter default-value="${localRepository}"
     */
    private org.apache.maven.artifact.repository.ArtifactRepository localRepository;

    public void execute() throws MojoExecutionException {
        if (localRepository != null && localRepository.getBasedir() != null) {
            System.setProperty("org.ops4j.pax.url.mvn.localRepository", localRepository.getBasedir());
        }

        String nameValue = (name == null || "".equals(name)) ? project.getArtifactId() : name;
        final FeaturesBuilder featuresBuilder = new FeaturesBuilder(nameValue, container, project, mavenSession);
        final FeatureBuilder projectFeatureBuilder = featuresBuilder.createFeature(nameValue, project.getVersion());
        if (importRepositories) {
            featuresBuilder.setImportRepositories(true);
            featuresBuilder.setImportRepositoryExclusions(importRepositoryExclusions);
        }
        if (project.getName() != nameValue) {
            projectFeatureBuilder.setDescription(project.getName());
        }
        projectFeatureBuilder.setDetails(project.getDescription());

        addRepositoriesFromConfiguration(featuresBuilder);
        addConfigFromConfiguration(projectFeatureBuilder);
        addConfigfileFromConfiguration(projectFeatureBuilder);
        addFeaturesFromConfiguration(projectFeatureBuilder);
        addBundlesFromConfiguration(projectFeatureBuilder);
        addDependenciesFromMaven(featuresBuilder, projectFeatureBuilder);

        final Features features = featuresBuilder.getFeatures();

        if (projectFeatureBuilder.isEmpty()) {
            features.getFeature().remove(projectFeatureBuilder.getFeature());
        }

        FileWriter writer = null;
        try {
            outputFile.getParentFile().mkdirs();
            writer = new FileWriter(outputFile);
            JaxbUtil.marshal(features, writer);
        } catch (final IOException e) {
            throw new MojoExecutionException("Unable to open outputFile (" + outputFile + ") for writing.", e);
        } catch (final JAXBException e) {
            throw new MojoExecutionException("Unable to marshal the feature XML to outputFile (" + outputFile + ")",
                    e);
        } finally {
            IOUtil.close(writer);
        }

        projectHelper.attachArtifact(project, "xml", "features", outputFile);
    }

    void addRepositoriesFromConfiguration(final FeaturesBuilder featuresBuilder) throws MojoExecutionException {
        getLog().debug("checking for repository entries in the <configuration> tag");
        if (repositories != null) {
            for (final String repository : repositories) {
                getLog().debug("found repository " + repository);
                featuresBuilder.addRepository(repository);
            }
        }
    }

    void addConfigFromConfiguration(final FeatureBuilder featureBuilder) {
        getLog().debug("checking for config entries in the <configuration> tag");
        if (configs != null) {
            for (final Config config : configs) {
                getLog().debug("found config " + config);
                featureBuilder.addConfig(config.getName(), config.getContents());
            }
        }
    }

    void addConfigfileFromConfiguration(final FeatureBuilder featureBuilder) {
        getLog().debug("checking for configfiles in the <configuration> tag");
        if (configfiles != null) {
            for (final Configfile configfile : configfiles) {
                getLog().debug("found configfile " + configfile);
                featureBuilder.addConfigFile(configfile.getLocation(), configfile.getFinalname());
            }
        }
    }

    void addFeaturesFromConfiguration(final FeatureBuilder featureBuilder) {
        getLog().debug("checking for features in the <configuration> tag");
        if (features != null) {
            for (final String feature : features) {
                getLog().debug("found feature " + feature);
                addFeature(featureBuilder, feature);
            }
        }
    }

    void addBundlesFromConfiguration(final FeatureBuilder featureBuilder) throws MojoExecutionException {
        getLog().debug("checking for bundles in the <configuration> tag");
        if (bundles != null) {
            for (final String bundle : bundles) {
                getLog().debug("found bundle " + bundle);
                addBundle(featureBuilder, bundle);
            }
        }
    }

    void addDependenciesFromMaven(final FeaturesBuilder featuresBuilder, final FeatureBuilder projectFeatureBuilder)
            throws MojoExecutionException {
        getLog().debug("project = " + project);

        addLocalDependencies(featuresBuilder, projectFeatureBuilder, project);
    }

    void addLocalDependencies(final FeaturesBuilder featuresBuilder, final FeatureBuilder projectFeatureBuilder,
            final MavenProject project) throws MojoExecutionException {

        for (final Dependency dependency : project.getDependencies()) {
            getLog().debug("getting artifact for dependency " + dependency);

            if (getIgnoredScopes().contains(dependency.getScope())) {
                getLog().debug(
                        "Dependency " + dependency + " is in scope: " + dependency.getScope() + ", ignoring.");
                continue;
            }

            org.apache.maven.artifact.Artifact matched = null;
            for (final org.apache.maven.artifact.Artifact art : project.getArtifacts()) {
                if (!dependency.getGroupId().equals(art.getGroupId())) {
                    continue;
                }
                if (!dependency.getArtifactId().equals(art.getArtifactId())) {
                    continue;
                }
                if (!dependency.getVersion().equals(art.getBaseVersion())) {
                    continue;
                }
                if (!dependency.getType().equals(art.getType())) {
                    continue;
                }
                if (dependency.getClassifier() == null && art.getClassifier() != null) {
                    continue;
                }
                if (dependency.getClassifier() != null && !dependency.getClassifier().equals(art.getClassifier())) {
                    continue;
                }

                matched = art;
                break;
            }

            if (matched == null) {
                throw new MojoExecutionException("Unable to match artifact for dependency: " + dependency);
            } else {
                getLog().debug("Found match for dependency: " + dependency);
                try {
                    addBundleArtifact(featuresBuilder, projectFeatureBuilder, matched);
                } catch (final Exception e) {
                    throw new MojoExecutionException(
                            "An error occurred while adding artifact " + matched + " to the features file.", e);
                }
            }
        }

    }

    void addFeature(final FeatureBuilder featureBuilder, final String feature) {
        getLog().debug("addFeature: " + feature);

        if (feature.contains("/")) {
            final String[] featureInfo = feature.split("/");
            if (featureInfo == null || featureInfo.length != 2) {
                getLog().debug(
                        "Invalid feature '" + feature + "'. Must be just a feature name, or feature/version.");
            } else {
                featureBuilder.addFeature(featureInfo[0], featureInfo[1]);
            }
        } else {
            // no version specified, add directly
            featureBuilder.addFeature(feature);
        }
    }

    void addBundle(final FeatureBuilder featureBuilder, final String bundle) throws MojoExecutionException {
        getLog().debug("addBundle: " + bundle);

        if (bundle.contains("@")) {
            final String[] bundleInfo = bundle.split("@");
            if (bundleInfo == null || bundleInfo.length != 2) {
                getLog().debug("Invalid bundle '" + bundle
                        + "'. Must be a bundle specification, optionally followed by @ and an integer start level.");
            } else {
                final Integer startLevel;
                try {
                    startLevel = Integer.valueOf(bundleInfo[1]);
                } catch (final NumberFormatException e) {
                    throw new MojoExecutionException("Bundle specification ('" + bundle
                            + "') contained a start level, but it was unparseable.");
                }
                featureBuilder.addBundle(bundleInfo[0], startLevel);
            }
        } else {

            // no startLevel specified
            featureBuilder.addBundle(bundle);
        }
    }

    void addBundleArtifact(final FeaturesBuilder featuresBuilder, final FeatureBuilder projectFeatureBuilder,
            final Artifact artifact) throws IOException, JAXBException, MojoExecutionException {
        getLog().debug("addBundleArtifact: " + artifact);

        final File file = artifact.getFile();
        JarFile jf = null;
        try {
            jf = new JarFile(file);
        } catch (final Exception e) {
            // we just want to see if it's something with a manifest, ignore zip failures
        }

        if (isFeature(artifact)) {
            final Features features = readFeaturesFile(file);
            for (final Feature feature : features.getFeature()) {
                if (projectFeatureBuilder.getFeature().getName().equals(feature.getName())) {
                    getLog().warn("addBundleArtifact: Found feature named '" + feature.getName() + "' in artifact '"
                            + artifact + "', but we already have a feature with that name.  Skipping.");
                } else {
                    getLog().info(
                            "addBundleArtifact: Including feature '" + feature.getName() + "' from " + artifact);
                    featuresBuilder.addFeature(feature);
                }
            }
        } else if ("pom".equals(artifact.getType())) {
            getLog().debug("addBundleArtifact: " + artifact + " is a POM.  Skipping.");
            // skip POM dependencies that aren't features
            return;
        } else if (jf != null && jf.getManifest() != null && ManifestUtils.isBundle(jf.getManifest())) {
            final String bundleName = MavenUtil.artifactToMvn(artifact);
            projectFeatureBuilder.addBundle(bundleName);
        } else {
            boolean found = false;

            for (final BundleInfo bundle : projectFeatureBuilder.getFeature().getBundles()) {
                final Artifact bundleArtifact = MavenUtil.mvnToArtifact(bundle.getLocation().replace("wrap:", ""));
                if (bundleArtifact != null && bundleArtifact.toString().equals(artifact.toString())) {
                    found = true;
                    break;
                }
            }

            if (!found) {
                throw new MojoExecutionException("artifact " + artifact + " is not a bundle!");
            } else {
                getLog().debug(
                        "Added a non-bundle artifact, but it's already defined in the <bundles> configuration.  Skipping.");
            }
        }
    }

    List<String> getIgnoredScopes() {
        if (ignoredScopes == null) {
            return DEFAULT_IGNORED_SCOPES;
        }
        return ignoredScopes;
    }

    private boolean isFeature(final Artifact artifact) {
        if ("kar".equals(artifact.getType()) || "features".equals(artifact.getClassifier())) {
            return true;
        }
        return false;
    }

    private Features readFeaturesFile(final File featuresFile) throws JAXBException, IOException {
        Features features = null;
        InputStream in = null;
        try {
            in = new FileInputStream(featuresFile);
            features = JaxbUtil.unmarshal(in, false);
        } finally {
            IOUtil.close(in);
        }
        return features;
    }

    void setIgnoredScopes(final List<String> scopes) {
        ignoredScopes = scopes;
    }
}