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

net.jangaroo.jooc.mvnplugin.util.FileHelper Maven / Gradle / Ivy

The newest version!
package net.jangaroo.jooc.mvnplugin.util;

import net.jangaroo.jooc.mvnplugin.Type;
import net.jangaroo.jooc.mvnplugin.sencha.SenchaUtils;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.SystemUtils;
import org.apache.maven.archiver.MavenArchiveConfiguration;
import org.apache.maven.archiver.MavenArchiver;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.archiver.AbstractArchiver;
import org.codehaus.plexus.archiver.ArchivedFileSet;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.archiver.FileSet;
import org.codehaus.plexus.archiver.jar.JarArchiver;
import org.codehaus.plexus.archiver.util.AbstractFileSet;
import org.codehaus.plexus.archiver.util.DefaultFileSet;
import org.codehaus.plexus.archiver.zip.PlexusArchiverZipFileResourceCollection;
import org.codehaus.plexus.components.io.filemappers.FileMapper;
import org.codehaus.plexus.components.io.fileselectors.FileSelector;
import org.codehaus.plexus.components.io.fileselectors.IncludeExcludeFileSelector;
import org.codehaus.plexus.components.io.resources.PlexusIoFileResourceCollection;

import javax.annotation.Nonnull;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import static net.jangaroo.jooc.mvnplugin.sencha.SenchaUtils.APPS_DIRECTORY_NAME;
import static net.jangaroo.jooc.mvnplugin.sencha.SenchaUtils.EXT_DIRECTORY_NAME;
import static net.jangaroo.jooc.mvnplugin.sencha.SenchaUtils.PACKAGES_DIRECTORY_NAME;
import static net.jangaroo.jooc.mvnplugin.sencha.SenchaUtils.SENCHA_APP_TEMPLATE_ARTIFACT_ID;
import static net.jangaroo.jooc.mvnplugin.sencha.SenchaUtils.SENCHA_APP_TEMPLATE_GROUP_ID;
import static net.jangaroo.jooc.mvnplugin.sencha.SenchaUtils.SENCHA_TEST_APP_TEMPLATE_ARTIFACT_ID;
import static net.jangaroo.jooc.mvnplugin.sencha.SenchaUtils.SEPARATOR;
import static net.jangaroo.jooc.mvnplugin.sencha.SenchaUtils.getSenchaPackageName;
import static org.codehaus.plexus.archiver.util.DefaultArchivedFileSet.archivedFileSet;
import static org.codehaus.plexus.archiver.util.DefaultFileSet.fileSet;

public final class FileHelper {

  private FileHelper() {
    // hiding constructor of utility class
  }

  public static void copyFiles(File srcDir, File targetDir) throws MojoExecutionException {
    if (srcDir.exists()) {
      try {
        org.apache.commons.io.FileUtils.copyDirectory(srcDir, targetDir);
      } catch (IOException e) {
        throw new MojoExecutionException(String.format("Copying sencha sources from %s to %s failed.", srcDir, targetDir ), e);
      }
    }
  }

  public static void copyDirectory(@Nonnull File srcDir, @Nonnull File targetDir) throws MojoExecutionException {
    if (srcDir.exists()) {
      try {
        org.apache.commons.io.FileUtils.copyDirectoryToDirectory(srcDir, targetDir);
      } catch (IOException e) {
        throw new MojoExecutionException(String.format("Copying sencha sources from %s to %s failed.", srcDir, targetDir ), e);
      }
    }
  }

  public static void ensureDirectory(File dir) throws MojoExecutionException {
    if (!dir.exists() && !dir.mkdirs() && !dir.exists()) {
      throw new MojoExecutionException("could not create folder for directory " + dir);
    }
  }

  public static void copyFilesToDirectory(@Nonnull File source, @Nonnull File target, String matchPattern)
          throws MojoExecutionException {
    File[] files = source.listFiles();
    if (files == null || files.length == 0) {
      return;
    }
    Pattern pattern = null;
    if (matchPattern != null) {
      pattern = Pattern.compile(matchPattern);
    }
    for (File file: files) {
      if (file.isFile() && (pattern == null || pattern.matcher(file.getName()).matches())) {
        doCopyFile(file, target);
      }
    }
  }

  private static void doCopyFile(@Nonnull File source, @Nonnull File target) throws MojoExecutionException {
    try {
      Files.copy(source.toPath(),
              target.toPath().resolve(source.getName()),
              StandardCopyOption.REPLACE_EXISTING,
              StandardCopyOption.COPY_ATTRIBUTES,
              LinkOption.NOFOLLOW_LINKS);
    } catch (IOException e) {
      throw new MojoExecutionException("Could not copy files to janagaroo dir", e);
    }
  }

  public static void copyDirectories(@Nonnull File source, @Nonnull File target, Set excludeDirectories)
          throws MojoExecutionException {
    File[] files = source.listFiles();
    if (files == null || files.length == 0) {
      return;
    }
    for (File file: files) {
      if (file.isDirectory() && (excludeDirectories == null || !excludeDirectories.contains(file.getName()))) {
        copyDirectory(file, target);
      }
    }
  }

  public static String relativize(@Nonnull Path base, @Nonnull String path) {
    Path normalizedPath = Paths.get(path).normalize();
    return FilenameUtils.separatorsToUnix(base.relativize(normalizedPath).toString());
  }

  public static String relativize(@Nonnull Path base, @Nonnull File path) {
    Path normalizedPath = path.toPath().normalize();
    return FilenameUtils.separatorsToUnix(base.relativize(normalizedPath).toString());
  }

  public static String relativize(@Nonnull File base, @Nonnull File path) {
    return relativize(base.toPath(), path);
  }

  public static void createSymbolicLink(Path link, Path target) throws IOException {
    if (!SystemUtils.IS_OS_WINDOWS) {
      Files.createSymbolicLink(link, target);
    } else {
      // In Windows, users need special privileges to create symlinks. But there are less general symlinks that
      // only work within one volume (which is fine for our purpose) called junctions (for directories).
      // Creating directory junctions works without additional privileges. Unfortunately, the current implementation
      // of Files.createSymbolicLink() under Windows uses symlinks.
      // Thus, for windows, we always use mklink to create directory junctions (/J) in favor of symlinks (/D).
      // fall back to command line execution:
      DefaultExecutor executor = new DefaultExecutor();
      executor.setWorkingDirectory(link.toFile().getParentFile());
      // prevent command line tool output from appearing in Maven log:
      executor.setStreamHandler(new PumpStreamHandler(null));

      CommandLine mkLinkCommand = new CommandLine("CMD");
      mkLinkCommand.addArgument("/C");
      mkLinkCommand.addArgument("MKLINK");
      mkLinkCommand.addArgument("/J");
      mkLinkCommand.addArgument(link.getFileName().toString());
      mkLinkCommand.addArgument(target.toString());
      try {
        executor.execute(mkLinkCommand);
      } catch (IOException e) {
        throw new IOException("Error while invoking " + mkLinkCommand.toString(), e);
      }
    }
  }

  public static void createAppOrAppOverlayJar(MavenSession session,
                                              JarArchiver archiver,
                                              Map manifestEntries,
                                              ArtifactHandlerManager artifactHandlerManager) throws MojoExecutionException {
    FileHelper.createAppOrAppOverlayJar(session, archiver, manifestEntries, artifactHandlerManager, null);
  }

  public static void createAppOrAppOverlayJar(MavenSession session,
                                              JarArchiver archiver,
                                              Map manifestEntries,
                                              ArtifactHandlerManager artifactHandlerManager,
                                              String senchaAppBuild) throws MojoExecutionException {
    FileHelper.createAppOrAppOverlayJar(session, archiver, manifestEntries, artifactHandlerManager, senchaAppBuild, false);
  }

  public static void createPluginZip(MavenSession session,
                                     JarArchiver archiver,
                                     ArtifactHandlerManager artifactHandlerManager) throws MojoExecutionException {
    // TODO: transform plugins?
    FileHelper.createAppOrAppOverlayJar(session, archiver, null, artifactHandlerManager, null, true);
  }

  private static void createAppOrAppOverlayJar(MavenSession session,
                                               JarArchiver archiver,
                                               Map manifestEntries,
                                               ArtifactHandlerManager artifactHandlerManager,
                                               String senchaAppBuild,
                                               boolean zipMode) throws MojoExecutionException {

    MavenProject project = session.getCurrentProject();
    File appDir = new File(project.getBuild().getDirectory() + SenchaUtils.APP_TARGET_DIRECTORY);

    File jarFile = new File(project.getBuild().getDirectory(), project.getBuild().getFinalName() + (zipMode ? ".zip" : ".jar"));

    if (senchaAppBuild == null || SenchaUtils.DEVELOPMENT_PROFILE.equals(senchaAppBuild)) {
      // add the Jangaroo compiler resources to the resulting JAR
      DefaultFileSet fileSet = fileSet(appDir);
      if (!zipMode) {
        fileSet = fileSet.prefixed(MavenPluginHelper.META_INF_RESOURCES);
      }
      fileSet.setExcludes(new String[]{
              "**/build/temp/**",
              "**/" + PACKAGES_DIRECTORY_NAME + SEPARATOR + getSenchaPackageName(SENCHA_APP_TEMPLATE_GROUP_ID, SENCHA_APP_TEMPLATE_ARTIFACT_ID) + "/**",
              PACKAGES_DIRECTORY_NAME + SEPARATOR + getSenchaPackageName(SENCHA_APP_TEMPLATE_GROUP_ID, SENCHA_TEST_APP_TEMPLATE_ARTIFACT_ID) + "/**",
              "**/*-timestamp"
      });
      fileSet.setIncludingEmptyDirectories(false);
      addFileSetFollowingSymLinks(archiver, fileSet);
    }
    MavenArchiver mavenArchiver = new MavenArchiver();
    mavenArchiver.setArchiver(archiver);
    mavenArchiver.setOutputFile(jarFile);
    mavenArchiver.configureReproducible(project.getProperties().getProperty("project.build.outputTimestamp"));
    try {
      MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
      archive.setManifestFile(MavenPluginHelper.createDefaultManifest(project));
      if (manifestEntries != null) {
        archive.addManifestEntries(manifestEntries);
      }
      mavenArchiver.createArchive(session, project, archive);
    } catch (Exception e) { // NOSONAR
      throw new MojoExecutionException("Failed to create the javascript archive", e);
    }
    Artifact mainArtifact = project.getArtifact();
    mainArtifact.setFile(jarFile);
    // workaround for MNG-1682: force maven to install artifact using the "jar"/"zip" handler
    mainArtifact.setArtifactHandler(artifactHandlerManager.getArtifactHandler(zipMode ? Type.ZIP_EXTENSION : Type.JAR_EXTENSION));
  }

  public static void createAppsJar(MavenSession session,
                                   JarArchiver archiver,
                                   Map manifestEntries,
                                   ArtifactHandlerManager artifactHandlerManager,
                                   String senchaAppBuild,
                                   File appsDir,
                                   Map> appNamesToDirsOrJars,
                                   String rootAppName) throws MojoExecutionException {
    if (senchaAppBuild != null && !SenchaUtils.DEVELOPMENT_PROFILE.equals(senchaAppBuild)) {
      // really important?
      throw new MojoExecutionException("Apps jar is only supported for developer mode");
    }
    MavenProject project = session.getCurrentProject();
    appsDir = appsDir != null ? appsDir : new File(project.getBuild().getDirectory() + SenchaUtils.APPS_TARGET_DIRECTORY);

    File jarFile = new File(project.getBuild().getDirectory(), project.getBuild().getFinalName() + ".jar");

    // add the Jangaroo compiler resources to the resulting JAR first, no files will be overridden afterwards
    DefaultFileSet fileSet = fileSet(appsDir).prefixed(MavenPluginHelper.META_INF_RESOURCES);
    fileSet.setIncludingEmptyDirectories(false);
    addFileSetFollowingSymLinks(archiver, fileSet);

    appNamesToDirsOrJars.forEach((appName, appDirsOrJars) -> {
      appDirsOrJars.forEach(appDirOrJar -> {
        // add the Jangaroo compiler resources to the resulting JAR
        boolean isRootApp = appName.equals(rootAppName);
        String appPath = isRootApp ? "" : APPS_DIRECTORY_NAME + SEPARATOR + appName + SEPARATOR;
        boolean isDirectory = appDirOrJar.isDirectory();
        // while FileSet#setPrefix determines a prefix inside the generated archive, the
        // sourcePrefix is meant as a prefix for the input files
        String sourcePrefix = isDirectory ? "" : MavenPluginHelper.META_INF_RESOURCES;

        AbstractFileSet appFileSet = isDirectory ? fileSet(appDirOrJar) : archivedFileSet(appDirOrJar);
        appFileSet.prefixed(MavenPluginHelper.META_INF_RESOURCES + appPath);

        appFileSet.setExcludes(new String[]{
                "**/build/temp/**",
                "**/*-timestamp",
                sourcePrefix + EXT_DIRECTORY_NAME + SEPARATOR + "**",
                sourcePrefix + PACKAGES_DIRECTORY_NAME + SEPARATOR + "**",
        });
        appFileSet.setIncludingEmptyDirectories(false);
        if (isDirectory) {
          addFileSetFollowingSymLinks(archiver, (FileSet) appFileSet);
        } else {
          addArchivedFileSetRemovingPrefixFromSource(archiver, (ArchivedFileSet) appFileSet, MavenPluginHelper.META_INF_RESOURCES);
        }

        // add the Jangaroo compiler resources to the resulting JAR
        AbstractFileSet packagesFileSet = isDirectory ? fileSet(appDirOrJar) : archivedFileSet(appDirOrJar);
        packagesFileSet.prefixed(MavenPluginHelper.META_INF_RESOURCES);

        packagesFileSet.setIncludes(new String[]{
                sourcePrefix + EXT_DIRECTORY_NAME + SEPARATOR + "**",
                sourcePrefix + PACKAGES_DIRECTORY_NAME + SEPARATOR + "**",
        });
        packagesFileSet.setExcludes(new String[]{
                sourcePrefix + PACKAGES_DIRECTORY_NAME + SEPARATOR + getSenchaPackageName(SENCHA_APP_TEMPLATE_GROUP_ID, SENCHA_TEST_APP_TEMPLATE_ARTIFACT_ID) + "/**",
        });
        packagesFileSet.setIncludingEmptyDirectories(false);
        if (isDirectory) {
          addFileSetFollowingSymLinks(archiver, (FileSet) packagesFileSet);
        } else {
          addArchivedFileSetRemovingPrefixFromSource(archiver, (ArchivedFileSet) packagesFileSet, MavenPluginHelper.META_INF_RESOURCES);
        }
      });
    });

    MavenArchiver mavenArchiver = new MavenArchiver();
    mavenArchiver.setArchiver(archiver);
    mavenArchiver.configureReproducible(project.getProperties().getProperty("project.build.outputTimestamp"));
    mavenArchiver.setOutputFile(jarFile);
    try {
      MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
      archive.setManifestFile(MavenPluginHelper.createDefaultManifest(project));
      if (manifestEntries != null) {
        archive.addManifestEntries(manifestEntries);
      }
      mavenArchiver.createArchive(session, project, archive);
    } catch (Exception e) { // NOSONAR
      throw new MojoExecutionException("Failed to create the javascript archive", e);
    }
    Artifact mainArtifact = project.getArtifact();
    mainArtifact.setFile(jarFile);
    // workaround for MNG-1682: force maven to install artifact using the "jar" handler
    mainArtifact.setArtifactHandler(artifactHandlerManager.getArtifactHandler(Type.JAR_EXTENSION));
  }

  /**
   * Method copied + slightly adapted from AbstractArchiver because of symlink handling (see below).
   */
  private static void addFileSetFollowingSymLinks(@Nonnull AbstractArchiver archiver, @Nonnull final FileSet fileSet )
          throws ArchiverException
  {
    final File directory = fileSet.getDirectory();

    // The PlexusIoFileResourceCollection contains platform-specific File.separatorChar which
    // is an interesting cause of grief, see PLXCOMP-192
    final PlexusIoFileResourceCollection collection = new PlexusIoFileResourceCollection();

    // ALWAYS follow symlinks
    collection.setFollowingSymLinks(true);

    collection.setIncludes( fileSet.getIncludes() );
    collection.setExcludes( fileSet.getExcludes() );
    collection.setBaseDir( directory );
    collection.setFileSelectors( fileSet.getFileSelectors() );
    collection.setIncludingEmptyDirectories( fileSet.isIncludingEmptyDirectories() );
    collection.setPrefix( fileSet.getPrefix() );
    collection.setCaseSensitive( fileSet.isCaseSensitive() );
    collection.setUsingDefaultExcludes( fileSet.isUsingDefaultExcludes() );
    collection.setStreamTransformer( fileSet.getStreamTransformer() );

    if ( archiver.getOverrideDirectoryMode() > -1 || archiver.getOverrideFileMode() > -1 )
    {
      collection.setOverrideAttributes( -1, null, -1, null, archiver.getOverrideFileMode(), archiver.getOverrideDirectoryMode() );
    }

    if ( archiver.getDefaultDirectoryMode() > -1 || archiver.getDefaultFileMode() > -1 )
    {
      collection.setDefaultAttributes( -1, null, -1, null, archiver.getDefaultFileMode(), archiver.getDefaultDirectoryMode() );
    }

    archiver.addResources( collection );
  }

  private static void addArchivedFileSetRemovingPrefixFromSource(@Nonnull AbstractArchiver archiver, @Nonnull final ArchivedFileSet fileSet, String prefix )
          throws ArchiverException
  {
    final File archive = fileSet.getArchive();

    final PlexusArchiverZipFileResourceCollection collection = new PlexusArchiverZipFileResourceCollection();

    collection.setFile(archive);
    collection.setIncludes( fileSet.getIncludes() );
    collection.setExcludes( fileSet.getExcludes() );
    collection.setIncludingEmptyDirectories( fileSet.isIncludingEmptyDirectories() );
    collection.setPrefix( fileSet.getPrefix() );
    collection.setCaseSensitive( fileSet.isCaseSensitive() );
    collection.setUsingDefaultExcludes( fileSet.isUsingDefaultExcludes() );
    collection.setStreamTransformer( fileSet.getStreamTransformer() );

    FileSelector[] fileSelectors = fileSet.getFileSelectors();
    List fileSelectorsList = fileSelectors == null ? new ArrayList<>() : Arrays.asList(fileSelectors);
    fileSelectorsList.add(0,
            fileInfo -> !prefix.equals(fileInfo.getName()) && fileInfo.getName().startsWith(prefix)
    );
    // as soon as an own fileSelector is used the include/exclude list is no longer applied
    if (fileSet.getIncludes() != null || fileSet.getExcludes() != null) {
      IncludeExcludeFileSelector includeExcludeFileSelector = new IncludeExcludeFileSelector();
      includeExcludeFileSelector.setIncludes(fileSet.getIncludes());
      includeExcludeFileSelector.setExcludes(fileSet.getExcludes());
      fileSelectorsList.add(includeExcludeFileSelector);
    }
    collection.setFileSelectors(fileSelectorsList.toArray(new FileSelector[]{}));
    collection.setFileMappers(new FileMapper[]{
            s -> s.replaceFirst("^" + prefix, "")
    });

    archiver.addResources( collection );
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy