org.expretio.maven.plugins.capnp.CapnProtoMojo.java Source code

Java tutorial

Introduction

Here is the source code for org.expretio.maven.plugins.capnp.CapnProtoMojo.java

Source

/*
 *  Copyright (c) 2015 ExPretio Technologies, Inc. and contributors
 *  Licensed under the MIT License:
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */
package org.expretio.maven.plugins.capnp;

import static com.google.common.io.Files.*;
import static org.expretio.maven.plugins.capnp.util.JavaPlatform.*;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
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.util.Scanner;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.expretio.maven.plugins.capnp.util.JavaPlatform;
import org.expretio.maven.plugins.capnp.util.NativesManager;
import org.expretio.maven.plugins.capnp.util.NativesManager.NativesInfo;
import org.expretio.maven.plugins.capnp.util.NativesManagerException;
import org.sonatype.plexus.build.incremental.BuildContext;
import org.sonatype.plexus.build.incremental.DefaultBuildContext;

import com.google.common.base.Splitter;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Lists;
import com.google.common.collect.Table;
import com.google.common.io.ByteStreams;

@Mojo(name = "generate", defaultPhase = LifecyclePhase.GENERATE_SOURCES, threadSafe = true, requiresProject = true, requiresDependencyResolution = ResolutionScope.RUNTIME, requiresOnline = false)
public class CapnProtoMojo extends AbstractMojo {
    private static final String NATIVES_DEPENDENCY_VERSION_DEFAULT = "0.5.3";
    private static final String AUTO_CLASSIFIER_DEFAULT = "auto";

    private static final String NATIVES_GROUP_ID = "org.expretio.capnp";
    private static final String NATIVES_ARTIFACT_ID = "capnp-natives";
    private static final String NATIVES_INDEX_CLASSIFIER = "capnp-natives-index";

    @Component
    private BuildContext buildContext = new DefaultBuildContext();

    @Component
    private RepositorySystem repositorySystem;

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

    @Parameter(defaultValue = "${repositorySystemSession}", readonly = true)
    private RepositorySystemSession repositorySession;

    @Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly = true)
    private List<RemoteRepository> remoteRepository;

    @Parameter(defaultValue = "true")
    private boolean verbose;

    /**
     * Output directory of generated java classes.
     */
    @Parameter(defaultValue = "${project.build.directory}/generated-sources/capnp", required = true)
    private File outputDirectory;

    /**
     * Base directory of definition schemas.
     */
    @Parameter(defaultValue = "src/main/capnp", required = true)
    private File schemaDirectory;

    /**
     * Compilation process working directory.
     */
    @Parameter(defaultValue = "${project.build.directory}/capnp-work", required = true)
    private File workDirectory;

    /**
     * File extension of definition schemas.
     */
    @Parameter(defaultValue = "capnp")
    private String schemaFileExtension;

    /**
     * Explicitly specified definition schema files. If none, all files matching <code>schemaFileExtension<code> under
     * <code>schemaDirectory<code> will be compiled. Files must be specified relatively from
     * <code>schemaDirectory<code>.
     *
     * @see #schemaFileExtension
     * @see #schemaDirectory
     */
    @Parameter
    private String[] schemas;

    /**
     * Supplementary import directories. Note: <code>schemaDirectory</code> is implicitly considered as an import
     * directory.
     *
     * @see #schemaDirectory
     */
    @Parameter
    private File[] importDirectories;

    /**
     * Version of the <code>org.expretio.maven:capnp-natives</code> dependency.
     */
    @Parameter(defaultValue = NATIVES_DEPENDENCY_VERSION_DEFAULT, required = true)
    private String nativeDependencyVersion;

    /**
     * Classifier of the <code>org.expretio.maven:capnp-natives</code> dependency, forcing the targeted platform when
     * specified. It is recommended to use the default value, which adjusts the classifier to current platform
     * automatically.
     */
    @Parameter(defaultValue = AUTO_CLASSIFIER_DEFAULT, required = true)
    private String nativeDependencyClassifier;

    /**
     * Set to false to configure manually the <code>org.expretio.maven:capnp-natives</code> dependency.
     */
    @Parameter(defaultValue = "true", required = true)
    private boolean handleNativeDependency;

    private final NativesManager nativesManager = new NativesManager();

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        mavenProject.addCompileSourceRoot(outputDirectory.getAbsolutePath());

        Scanner scanner = buildContext.newScanner(schemaDirectory);
        scanner.scan();

        if (scanner.getIncludedFiles().length < 1) {
            return;
        }

        if (handleNativeDependency) {
            doHandleNativesDependency();
        }

        nativesManager.registerAllDescriptors();

        workDirectory.mkdirs();

        NativesInfo currentNativesInfo = nativesManager.getNativesInfoForCurrentPlatform();

        CapnpCompiler compiler = CapnpCompiler.builder().setOutputDirectory(outputDirectory)
                .setSchemaDirectory(schemaDirectory).setWorkDirectory(workDirectory)
                .setCapnpFile(copyResource(currentNativesInfo.getCapnpUrl(), workDirectory))
                .setCapnpcJavaFile(copyResource(currentNativesInfo.getCapnpcJavaUrl(), workDirectory))
                .setCapnpJavaSchemaFile(copyResource(currentNativesInfo.getCapnpJavaSchemaUrl(), workDirectory))
                .addSchemas(getSchemas()).addImportDirectories(getImportDirectories()).setVerbose(verbose).build();

        compiler.compile();
    }

    private void doHandleNativesDependency() throws MojoExecutionException {
        String classifier;

        if (nativeDependencyClassifier.equals(AUTO_CLASSIFIER_DEFAULT)) {
            Table<String, String, String> indexTable = HashBasedTable.create();

            try {
                XMLConfiguration index = new XMLConfiguration();

                index.load(resolve(createNativesIndexArtifact()));

                for (HierarchicalConfiguration indexEntry : index.configurationsAt("entry")) {
                    String osName = indexEntry.getString("os-name");
                    String archNames = indexEntry.getString("arch-names");
                    String mavenClassifier = indexEntry.getString("maven-classifier");

                    for (String archName : Splitter.on(',').omitEmptyStrings().trimResults().split(archNames)) {
                        indexTable.put(osName.toUpperCase(), getCanonicalArchitecture(archName), mavenClassifier);
                    }
                }

                classifier = indexTable.get(JavaPlatform.getCurrentOs().toString(),
                        getCanonicalArchitecture(JavaPlatform.getCurrentArch()));
            } catch (Exception e) {
                throw new NativesManagerException(e);
            }
        } else {
            classifier = nativeDependencyClassifier;
        }

        nativesManager.addResourceUrl(resolve(createNativesArtifact(classifier)));
    }

    private Artifact createNativesArtifact(String classifier) throws MojoExecutionException {
        return new DefaultArtifact(NATIVES_GROUP_ID, NATIVES_ARTIFACT_ID, classifier, "jar",
                nativeDependencyVersion);
    }

    private Artifact createNativesIndexArtifact() {
        return new DefaultArtifact(NATIVES_GROUP_ID, NATIVES_ARTIFACT_ID, NATIVES_INDEX_CLASSIFIER, "xml",
                nativeDependencyVersion);
    }

    private URL resolve(Artifact artifact) throws MojoExecutionException {
        ArtifactRequest request = new ArtifactRequest(artifact, remoteRepository, null);

        try {
            return repositorySystem.resolveArtifact(repositorySession, request).getArtifact().getFile().toURI()
                    .toURL();
        } catch (ArtifactResolutionException | MalformedURLException e) {
            throw new MojoExecutionException("Cannot resolve artifact: " + artifact, e);
        }
    }

    private Collection<String> getSchemas() {
        if (schemas == null) {
            return getAllSchemas();
        }

        return Arrays.asList(schemas);
    }

    private Collection<String> getAllSchemas() {
        List<String> allSchemas = Lists.newArrayList();

        for (File file : fileTreeTraverser().preOrderTraversal(schemaDirectory)) {
            if (isSchema(file)) {
                allSchemas.add(relativize(file.toPath()));
            }
        }

        return allSchemas;
    }

    private Collection<File> getImportDirectories() {
        if (importDirectories == null) {
            return Collections.EMPTY_LIST;
        }

        return Arrays.asList(importDirectories);
    }

    private boolean isSchema(File file) {
        return (!file.isDirectory() && file.getName().endsWith("." + schemaFileExtension));
    }

    private String relativize(Path path) {
        // capnp native program is not compatible with windows file separator
        return schemaDirectory.toPath().relativize(path).toString().replace('\\', '/');
    }

    private File copyResource(URL source, File target) throws MojoExecutionException {
        try {
            String fileName = new File(source.getPath()).getName();
            File targetFile = new File(target, fileName);

            try (InputStream in = new BufferedInputStream(source.openStream());
                    OutputStream out = new BufferedOutputStream(new FileOutputStream(targetFile));) {
                ByteStreams.copy(in, out);

                targetFile.setExecutable(true);

                return targetFile;
            }
        } catch (Exception e) {
            throw new MojoExecutionException("Unable to copy natives to work directory: " + workDirectory, e);
        }
    }
}