All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.proofpoint.platform.BundlerPackager Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2010 Proofpoint, Inc.
 *
 * 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.proofpoint.platform;

import com.google.common.collect.ImmutableList;
import com.google.common.io.Closeables;
import com.google.common.io.Resources;
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.jruby.Ruby;
import org.jruby.RubyInstanceConfig;
import org.jruby.RubyObjectAdapter;
import org.jruby.javasupport.JavaEmbedUtils;
import org.jruby.runtime.builtin.IRubyObject;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;

import static com.google.common.io.Files.copy;
import static com.google.common.io.Files.createTempDir;
import static com.google.common.io.Files.deleteRecursively;
import static org.jruby.javasupport.JavaEmbedUtils.javaToRuby;

/**
 * @goal bundler-package
 */
public class BundlerPackager
        extends AbstractMojo
{
    /**
     * @parameter expression="${project}"
     * @required
     * @readonly
     */
    private MavenProject project;

    /**
     * The output directory of the assembled distribution file.
     *
     * @parameter default-value="${project.build.directory}"
     * @required
     */
    private File outputDirectory;

    public void execute()
            throws MojoExecutionException, MojoFailureException
    {
        URL bundlerLib = Resources.getResource("bundler/lib");
        checkNotNull(bundlerLib, "Couldn't find a gem repo that contains bundler.  Please ensure the Bundler Packaging plugin is properly built.");

        String gemfileLocation = locateFileInProjectRoot("Gemfile");
        String gemfileLockLocation = locateFileInProjectRoot("Gemfile.lock");

        Ruby runtime = JavaEmbedUtils.initialize(ImmutableList.of(bundlerLib.getPath()), createRuntimeConfig());
        RubyObjectAdapter adapter = JavaEmbedUtils.newObjectAdapter();

        IRubyObject gemRepositoryBuilder = createNewGemRepositoryBuilder(runtime);

        String workTempDirectoryPath = createTemporaryDirectory();
        String gemrepoTempDirectoryPath = createTemporaryDirectory();

        IRubyObject response;
        try {
            response = adapter.callMethod(gemRepositoryBuilder, "build_repository_using_bundler",
                    new IRubyObject[]{
                            javaToRuby(runtime, workTempDirectoryPath),
                            javaToRuby(runtime, gemrepoTempDirectoryPath),
                            javaToRuby(runtime, gemfileLocation),
                            javaToRuby(runtime, gemfileLockLocation)
                    });
        } catch (Exception e) {
            throw new MojoExecutionException("Gem repo jar was not properly constructed.  Please check the output for errors.  " +
                            "Try running bundle install manually to verify the contents of the Gemfile.  [" + gemfileLocation + "]", e);

        }

        String gemrepoGeneratedLocation = response.asJavaString();

        // try to eliminate directories that we don't need in the jar
        deleteRecursivelyIgnoringErrors(new File(gemrepoGeneratedLocation, "bin"));
        deleteRecursivelyIgnoringErrors(new File(gemrepoGeneratedLocation, "cache"));
        deleteRecursivelyIgnoringErrors(new File(gemrepoGeneratedLocation, "doc"));

        generateJarFile(new File(gemrepoGeneratedLocation), gemfileLocation);

        deleteRecursivelyIgnoringErrors(new File(workTempDirectoryPath));
        deleteRecursivelyIgnoringErrors(new File(gemrepoTempDirectoryPath));
    }

    private IRubyObject createNewGemRepositoryBuilder(Ruby runtime) throws MojoExecutionException {

        URL gemRepositoryBuilder = Resources.getResource("proofpoint/GemRepositoryBuilder.rb");

        InputStream gemRepositoryBuilderStream = null;
        try {
            try {
                gemRepositoryBuilderStream = gemRepositoryBuilder.openStream();
            }
            catch (Exception e) {
                throw new MojoExecutionException("Couldn't find GemRepositoryBuilder.rb.  Please ensure the Bundler Packaging plugin is properly built.");
            }

            runtime.loadFile("GemRepositoryBuilder", gemRepositoryBuilderStream, false);
        }
        finally {
            Closeables.closeQuietly(gemRepositoryBuilderStream);
        }

        return runtime.evalScriptlet("Proofpoint::GemToJarPackager::GemRepositoryBuilder.new");
    }

    private void deleteRecursivelyIgnoringErrors(File file)
    {
        try {
            deleteRecursively(file);
        }
        catch (IOException e) {
            getLog().warn("Failed to delete directory recursively", e);
        }
    }

    private String createTemporaryDirectory() throws MojoExecutionException {
        try {
            return createTempDir().getCanonicalPath();
        } catch (IOException e) {
            throw new MojoExecutionException("Error trying to create temporary directory for the gems, " +
                    "please ensure the plugin is properly built and the temporary directory [" +
                    System.getProperty("java.io.tmpdir") + "] isn't full and is writeable." );
        }
    }

    private String locateFileInProjectRoot(String fileName) throws MojoExecutionException {
        String fileLocation = getProjectBaseDir() + "/" + fileName;

        if (!(new File(fileLocation)).canRead())
        {
            throw new MojoExecutionException("No " + fileName + " was found in the root of your project.  " +
                    "Please ensure a " + fileName + " exists, is readable, and is in the root of your project structure.  "  +
                    "The project root appears to be at [" + getProjectBaseDir() + "].");
        }

        return fileLocation;
    }

    private String getProjectBaseDir() throws MojoExecutionException {
        try {
            return project.getBasedir().getCanonicalPath();
        }
        catch (IOException e) {
            throw new MojoExecutionException("Error trying to locate the project base directory, please ensure the plugin is properly built.");
        }
    }

    private void generateJarFile(File gemrepoDirectory, String gemfileLocation)
            throws MojoExecutionException
    {
        try {
            //noinspection ResultOfMethodCallIgnored
            outputDirectory.mkdirs();

            File gemrepoJarFile = new File(String.format("%s/%s-%s-gemrepo.jar", outputDirectory.getCanonicalPath(), project.getArtifactId(), project.getVersion()));
            getLog().info("Building gem repository jar: " + gemrepoJarFile.getName());

            Manifest manifest = new Manifest();
            JarOutputStream gemrepoJarOutputStream = new JarOutputStream(new FileOutputStream(gemrepoJarFile), manifest);

            addFilesToJarRecursivelyIgnoringSymlinks(gemrepoJarOutputStream, gemrepoDirectory, "");

            addFileToJar(gemrepoJarOutputStream, new File(gemfileLocation), "META-INF/");

            Closeables.closeQuietly(gemrepoJarOutputStream);
        } catch (IOException e) {
            throw new MojoExecutionException("Error trying to create the jar containing the gems repository, " +
                    "please ensure the plugin is properly built and the target directory [" +
                    outputDirectory.getPath() + "] exists or is creatable, is not full, and is writeable." );
        }
    }

    private void addFilesToJarRecursivelyIgnoringSymlinks(JarOutputStream jarOutputStream, File directory, String path) throws IOException
    {
        for(File child : directory.listFiles()) {
            if (!isSymbolicLink(child)) {

                addFileToJar(jarOutputStream, child, path);

                if(child.isDirectory()) {
                    addFilesToJarRecursivelyIgnoringSymlinks(jarOutputStream, child, path + child.getName() + "/");
                }
            }
        }
    }

    private void addFileToJar(JarOutputStream jarOutputStream, File fileToAdd, String path) throws IOException {
        String entryName = path + fileToAdd.getName() + (fileToAdd.isDirectory() ? "/" : "");
        getLog().debug("Adding entry [" + entryName + "] to the gem repository jar");
        JarEntry jarEntry = new JarEntry(entryName);
        jarEntry.setTime(fileToAdd.lastModified());
        jarOutputStream.putNextEntry(jarEntry);

        if (!fileToAdd.isDirectory()) {
            copy(fileToAdd, jarOutputStream);
        }
    }

    private boolean isSymbolicLink(File file)
    {
        try {
            File canonicalFile = file.getCanonicalFile();
            File absoluteFile = file.getAbsoluteFile();
            // a symbolic link has a different name between the canonical and absolute path
            return !canonicalFile.getName().equals(absoluteFile.getName()) ||
                    // or the canonical parent path is not the same as the files parent path
                    !canonicalFile.getParent().equals(file.getParentFile().getCanonicalPath());
        }
        catch (IOException e) {
            // error on the side of caution
            return true;
        }
    }

    private RubyInstanceConfig createRuntimeConfig()
    {
        RubyInstanceConfig config = new RubyInstanceConfig();
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        if (classLoader == null) {
            classLoader = getClass().getClassLoader();
        }
        config.setClassCache(JavaEmbedUtils.createClassCache(classLoader));

        URL resource = RubyInstanceConfig.class.getResource("/META-INF/jruby.home");
        if (resource != null && resource.getProtocol().equals("jar")) {
            try { // http://weblogs.java.net/blog/2007/04/25/how-convert-javaneturl-javaiofile
                config.setJRubyHome(resource.toURI().getSchemeSpecificPart());
            }
            catch (URISyntaxException e) {
                config.setJRubyHome(resource.getPath());
            }
        }

        return config;
    }

    private void checkNotNull(Object argument, String message)
            throws MojoExecutionException
    {
        if (argument == null)
        {
            throw new MojoExecutionException(message);
        }
    }

    public void setProject(MavenProject project)
    {
        this.project = project;
    }

    public void setOutputDirectory(File outputDirectory)
    {
        this.outputDirectory = outputDirectory;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy