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

org.smallmind.spark.singularity.maven.GenerateSingularityMojo Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2007 through 2024 David Berkman
 *
 * This file is part of the SmallMind Code Project.
 *
 * The SmallMind Code Project is free software, you can redistribute
 * it and/or modify it under either, at your discretion...
 *
 * 1) The terms of GNU Affero General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 *
 * ...or...
 *
 * 2) The terms of the Apache License, Version 2.0.
 *
 * The SmallMind Code Project 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 or Apache License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * and the Apache License along with the SmallMind Code Project. If not, see
 *  or .
 *
 * Additional permission under the GNU Affero GPL version 3 section 7
 * ------------------------------------------------------------------
 * If you modify this Program, or any covered work, by linking or
 * combining it with other code, such other code is not for that reason
 * alone subject to any of the requirements of the GNU Affero GPL
 * version 3.
 */
package org.smallmind.spark.singularity.maven;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
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.smallmind.nutsnbolts.zip.CompressionType;
import org.smallmind.spark.singularity.boot.SingularityEntryPoint;
import org.smallmind.spark.singularity.boot.SingularityIndex;

// Generates Singularity based one jar applications
@Mojo(name = "generate-singularity", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true)
public class GenerateSingularityMojo extends AbstractMojo {

  @Parameter(readonly = true, property = "project")
  private MavenProject project;
  @Parameter
  private Exclusion[] exclusions;
  @Parameter(defaultValue = "singularity")
  private String singularityBuildDir;
  @Parameter
  private String mainClass;
  @Parameter(defaultValue = "false")
  private boolean verbose;
  @Parameter(defaultValue = "false")
  private boolean skip;
  @Component
  ArtifactFactory artifactFactory;
  @Parameter(readonly = true, property = "plugin.artifacts")
  protected List pluginArtifacts;

  public void execute ()
    throws MojoExecutionException {

    if (!skip) {

      Artifact applicationArtifact;
      SingularityIndex singularityIndex = new SingularityIndex();
      Path buildPath;
      Path libraryPath;
      Path indexPath;
      Path classesPath;
      Path compressedFile;
      boolean bootClassesFound = false;

      try {
        Files.createDirectories(buildPath = Paths.get(project.getBuild().getDirectory(), singularityBuildDir));
        Files.createDirectories(libraryPath = buildPath.resolve("META-INF").resolve("singularity").resolve("lib"));
        Files.createDirectories(indexPath = buildPath.resolve("META-INF").resolve("singularity").resolve("index"));
      } catch (IOException ioException) {
        throw new MojoExecutionException("Unable to create a build directory", ioException);
      }

      for (Artifact pluginArtifact : pluginArtifacts) {
        if (pluginArtifact.getGroupId().equals("org.smallmind") && pluginArtifact.getArtifactId().equals("spark-singularity-boot")) {
          try {
            copyBootClasses(singularityIndex, pluginArtifact.getFile(), buildPath);
          } catch (IOException ioException) {
            throw new MojoExecutionException("Problem in copying boot classes into the build directory", ioException);
          }

          bootClassesFound = true;
          break;
        }
      }
      if (!bootClassesFound) {
        throw new MojoExecutionException("Unable to locate the boot class dependencies");
      }

      for (Artifact artifact : project.getRuntimeArtifacts()) {

        boolean excluded = false;

        if ((exclusions != null) && (exclusions.length > 0)) {
          for (Exclusion exclusion : exclusions) {
            if (exclusion.matchesArtifact(artifact)) {
              excluded = true;
              break;
            }
          }
        }

        if (excluded) {
          if (verbose) {
            getLog().info(String.format("Excluded dependency(%s)...", artifact.getFile().getName()));
          }
        } else {
          if (verbose) {
            getLog().info(String.format("Copying dependency(%s)...", artifact.getFile().getName()));
          }

          try {

            JarFile jarFile = new JarFile(artifact.getFile());
            Enumeration jarEntryEnum = jarFile.entries();

            while (jarEntryEnum.hasMoreElements()) {
              singularityIndex.addInverseJarEntry(jarEntryEnum.nextElement().getName(), artifact.getFile().getName());
            }

            copyToDestination(artifact.getFile(), libraryPath.resolve(artifact.getFile().getName()));
          } catch (IOException ioException) {
            throw new MojoExecutionException(String.format("Problem in copying a dependency(%s) into the build directory", artifact), ioException);
          }
        }
      }

      if (Files.exists(classesPath = Paths.get(project.getBuild().getDirectory(), "classes"))) {
        if (verbose) {
          getLog().info("Copying classes directory...");
        }
        try {
          Files.walkFileTree(classesPath, new CopyFileVisitor(singularityIndex, buildPath));
        } catch (IOException ioException) {
          throw new MojoExecutionException("Unable to copy the classes directory into the build path", ioException);
        }
      }

      if (verbose) {
        getLog().info("Creating singularity index...");
      }
      try {
        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(indexPath.resolve("singularity.idx").toFile()))) {
          objectOutputStream.writeObject(singularityIndex);
        }
      } catch (IOException ioException) {
        throw new MojoExecutionException("Unable to write the singularity index", ioException);
      }

      try {

        Manifest manifest = new Manifest();
        Attributes attributes = manifest.getMainAttributes();

        attributes.put(Attributes.Name.MAIN_CLASS, SingularityEntryPoint.class.getName());
        attributes.put(new Attributes.Name("Singularity-Class"), mainClass);

        attributes.put(Attributes.Name.SPECIFICATION_TITLE, System.getProperty("java.vm.specification.name"));
        attributes.put(Attributes.Name.SPECIFICATION_VERSION, System.getProperty("java.vm.specification.version"));
        attributes.put(Attributes.Name.SPECIFICATION_VENDOR, System.getProperty("java.vm.specification.vendor"));
        attributes.put(Attributes.Name.IMPLEMENTATION_TITLE, System.getProperty("java.specification.name"));
        attributes.put(Attributes.Name.IMPLEMENTATION_VERSION, System.getProperty("java.specification.version"));
        attributes.put(Attributes.Name.IMPLEMENTATION_VENDOR, System.getProperty("java.specification.vendor"));

        attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");

        if (verbose) {
          getLog().info("Compressing output jar...");
        }

        CompressionType.JAR.compress(buildPath, compressedFile = Paths.get(project.getBuild().getDirectory(), constructArtifactName()), manifest);
      } catch (IOException ioException) {
        throw new MojoExecutionException("Problem constructing the executable jar", ioException);
      }

      applicationArtifact = artifactFactory.createArtifact(project.getGroupId(), project.getArtifactId(), project.getVersion(), "compile", "jar");
      applicationArtifact.setFile(compressedFile.toFile());

      project.addAttachedArtifact(applicationArtifact);
    }
  }

  private String constructArtifactName () {

    StringBuilder nameBuilder = new StringBuilder(project.getArtifactId()).append('-').append(project.getVersion());

    if (project.getArtifact().getClassifier() != null) {
      nameBuilder.append('-').append(project.getArtifact().getClassifier());
    }

    return nameBuilder.append(".jar").toString();
  }

  private void copyToDestination (File file, Path destinationPath)
    throws IOException {

    FileInputStream inputStream;
    FileOutputStream outputStream;
    FileChannel readChannel;
    FileChannel writeChannel;
    long bytesTransferred;
    long currentPosition = 0;

    readChannel = (inputStream = new FileInputStream(file)).getChannel();
    writeChannel = (outputStream = new FileOutputStream(destinationPath.toFile())).getChannel();
    while ((currentPosition < readChannel.size()) && (bytesTransferred = readChannel.transferTo(currentPosition, 8192, writeChannel)) >= 0) {
      currentPosition += bytesTransferred;
    }
    outputStream.close();
    inputStream.close();
  }

  private void copyBootClasses (SingularityIndex singularityIndex, File jarFile, Path destinationPath)
    throws IOException {

    byte[] buffer = new byte[1024];

    try (JarInputStream jarInputStream = new JarInputStream(new FileInputStream(jarFile))) {

      JarEntry jarEntry;

      while ((jarEntry = jarInputStream.getNextJarEntry()) != null) {
        if ((!jarEntry.isDirectory()) && jarEntry.getName().startsWith("org/smallmind/spark/singularity/boot/")) {
          if (verbose) {
            getLog().info(String.format("Copying boot class(%s)...", jarEntry.getName()));
          }

          Files.createDirectories(destinationPath.resolve(jarEntry.getName().substring(0, jarEntry.getName().lastIndexOf('/'))));

          try (OutputStream outputStream = Files.newOutputStream(destinationPath.resolve(jarEntry.getName()), StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {

            long totalBytesToRead = jarEntry.getSize();
            int totalBytesRead = 0;
            int bytesRead;

            do {
              bytesRead = jarInputStream.read(buffer, 0, (int)Math.min(buffer.length, totalBytesToRead - totalBytesRead));
              outputStream.write(buffer, 0, bytesRead);
              totalBytesRead += bytesRead;
            } while (totalBytesRead < totalBytesToRead);
          }

          singularityIndex.addFileName(jarEntry.getName());
        }
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy