/** * Copyright 2015-2016 Red Hat, Inc, and individual contributors. * * 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 org.wildfly.swarm.swarmtool; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Paths; import java.util.List; import java.util.Properties; import joptsimple.OptionException; import joptsimple.OptionParser; import joptsimple.OptionSet; import joptsimple.OptionSpec; import org.jboss.shrinkwrap.resolver.api.maven.ConfigurableMavenResolverSystem; import org.jboss.shrinkwrap.resolver.api.maven.Maven; import org.jboss.shrinkwrap.resolver.api.maven.repository.MavenChecksumPolicy; import org.jboss.shrinkwrap.resolver.api.maven.repository.MavenRemoteRepositories; import org.jboss.shrinkwrap.resolver.api.maven.repository.MavenUpdatePolicy; import org.wildfly.swarm.arquillian.resolver.ShrinkwrapArtifactResolvingHelper; import org.wildfly.swarm.fractions.FractionDescriptor; import org.wildfly.swarm.fractions.FractionList; import org.wildfly.swarm.fractions.PropertiesUtil; import org.wildfly.swarm.tools.ArtifactResolvingHelper; import org.wildfly.swarm.tools.ArtifactSpec; import org.wildfly.swarm.tools.BuildTool; import org.wildfly.swarm.tools.DeclaredDependencies; import static java.util.Arrays.asList; public class Main { protected Main() { } private static final OptionParser OPT_PARSER = new OptionParser(); private static final OptionSpec<Void> HOLLOW_OPT = OPT_PARSER.accepts("hollow", "creates a swarm jar containing only the fractions"); private static final OptionSpec<Void> HELP_OPT = OPT_PARSER.acceptsAll(asList("h", "help"), "print help and exit") .forHelp(); private static final OptionSpec<Void> VERSION_OPT = OPT_PARSER.acceptsAll(asList("v", "version"), "print version and exit") .forHelp(); private static final OptionSpec<Void> DISABLE_AUTO_DETECT_OPT = OPT_PARSER.accepts("no-fraction-detect", "disable auto fraction detection"); private static final OptionSpec<Void> DISABLE_BUNDLE_DEPS_OPT = OPT_PARSER.accepts("no-bundle-deps", "disable bundling of dependencies"); private static final OptionSpec<Void> EXECUTABLE_OPT = OPT_PARSER.accepts("executable", "make the swarm jar executable"); private static final OptionSpec<Void> DEBUG_LOGGING = OPT_PARSER.accepts("debug", "enable debug logging"); private static final OptionSpec<String> FRACTIONS_OPT = OPT_PARSER.acceptsAll(asList("f", "fractions"), "swarm fractions to include") .withRequiredArg() .ofType(String.class) .withValuesSeparatedBy(',') .describedAs("undertow,jaxrs,..."); private static final OptionSpec<String> DEPENDENCIES_OPT = OPT_PARSER.acceptsAll(asList("d", "dependencies"), "Maven coordinates (groupId:artifactId:version) of dependencies to include") .withRequiredArg() .ofType(String.class) .withValuesSeparatedBy(",") .describedAs("gav1,gav2,..."); private static final OptionSpec<String> REPOS_OPT = OPT_PARSER.accepts("repos", "additional maven repos to resolve against") .withRequiredArg() .ofType(String.class) .withValuesSeparatedBy(',') .describedAs("url1,url2,..."); private static final OptionSpec<String> OUTPUT_DIR_OPT = OPT_PARSER.acceptsAll(asList("o", "output-dir"), "directory where the final jar will be written") .withRequiredArg() .ofType(String.class) .defaultsTo(".") .describedAs("path"); private static final OptionSpec<String> MAIN_OPT = OPT_PARSER.accepts("main", "The name of the custom main class") .withRequiredArg() .ofType(String.class) .describedAs("main-class"); private static final OptionSpec<String> MODULES_OPT = OPT_PARSER.accepts("modules", "A list of JBoss Modules module dirs to include") .withRequiredArg() .ofType(String.class) .withValuesSeparatedBy(',') .describedAs("module-dir1,module-dir2,..."); private static final OptionSpec<String> NAME_OPT = OPT_PARSER.acceptsAll(asList("n", "name"), "The name of the final jar sans the -swarm.jar suffix (default: <source name>)") .withRequiredArg() .ofType(String.class) .describedAs("jar-name"); private static final OptionSpec<File> SOURCE_OPT = OPT_PARSER.nonOptions("The source artifact") .ofType(File.class); private static final OptionSpec<String> SYSPROPS_OPT = OPT_PARSER.accepts("D", "system property (overrides entry in --property-file)") .withRequiredArg() .ofType(String.class) .describedAs("key=value"); private static final OptionSpec<File> SYSPROPS_FILE_OPT = OPT_PARSER.accepts("property-file", "system properties") .withRequiredArg() .ofType(File.class) .describedAs("system properties file"); protected static final String VERSION; static { try { VERSION = PropertiesUtil .loadProperties(Main.class.getClassLoader() .getResourceAsStream("org/wildfly/swarm/swarmtool/version.properties")) .getProperty("version"); } catch (Exception e) { throw new RuntimeException("Failed to load version.properties", e); } } public static void main(final String[] args) throws Exception { try { generateSwarmJar(args); } catch (ExitException e) { final String msg = e.getMessage(); if (msg != null) { System.err.println(msg); } if (e.printHelp) { if (msg != null) { System.err.println(); } System.err.println(String.format("Usage: %s <options> artifact-path\n", executableName())); try { OPT_PARSER.printHelpOn(System.err); } catch (IOException ignored) { } } System.exit(e.status); } } protected static String executableName() { String name = System.getenv("SWARMTOOL_NAME"); if (name == null) { name = "java -jar swarmtool-standalone.jar"; } return name; } protected static File generateSwarmJar(final String[] args) throws Exception { OptionSet foundOptions = null; try { foundOptions = OPT_PARSER.parse(args); } catch (OptionException e) { exit(e.getMessage(), true); } if (foundOptions.has(HELP_OPT)) { exit(null, 0, true); } if (foundOptions.has(VERSION_OPT)) { exit("swarmtool v" + VERSION, 0); } final List<File> nonOptArgs = foundOptions.valuesOf(SOURCE_OPT); if (nonOptArgs.isEmpty()) { exit("No source artifact specified.", true); } if (nonOptArgs.size() > 1) { exit("Too many source artifacts provided (" + nonOptArgs + ")", true); } final File source = nonOptArgs.get(0); if (!source.exists()) { exit("File " + source.getAbsolutePath() + " does not exist."); } final Properties properties = new Properties(); if (foundOptions.has(SYSPROPS_FILE_OPT)) { try (InputStream in = new FileInputStream(foundOptions.valueOf(SYSPROPS_FILE_OPT))) { properties.load(in); } } foundOptions.valuesOf(SYSPROPS_OPT) .forEach(prop -> { final String[] parts = prop.split("="); properties.put(parts[0], parts[1]); }); final DeclaredDependencies dependencies = new DeclaredDependencies(); foundOptions.valuesOf(DEPENDENCIES_OPT).stream() .map(DeclaredDependencies::createSpec) .forEach(dependencies::add); final String[] parts = source.getName().split("\\.(?=[^\\.]+$)"); final String baseName = parts[0]; final String type = parts[1] == null ? "jar" : parts[1]; final String jarName = foundOptions.has(NAME_OPT) ? foundOptions.valueOf(NAME_OPT) : baseName; final String outDir = new File(foundOptions.valueOf(OUTPUT_DIR_OPT)).getCanonicalPath(); final String suffix = foundOptions.has(HOLLOW_OPT) ? "-hollow-swarm" : "-swarm"; final BuildTool tool = new BuildTool(getResolvingHelper(foundOptions.valuesOf(REPOS_OPT))) .projectArtifact("", baseName, "", type, source) .declaredDependencies(dependencies) .fractionDetectionMode(foundOptions.has(DISABLE_AUTO_DETECT_OPT) ? BuildTool.FractionDetectionMode.never : BuildTool.FractionDetectionMode.force) .bundleDependencies(!foundOptions.has(DISABLE_BUNDLE_DEPS_OPT)) .executable(foundOptions.has(EXECUTABLE_OPT)) .properties(properties) .hollow(foundOptions.has(HOLLOW_OPT)); if (foundOptions.has(MAIN_OPT)) { tool.mainClass(foundOptions.valueOf(MAIN_OPT)); } if (foundOptions.has(MODULES_OPT)) { tool.additionalModules(foundOptions.valuesOf(MODULES_OPT)); } if (foundOptions.has(DEBUG_LOGGING)) { tool.logger(BuildTool.STD_LOGGER_WITH_DEBUG); } addSwarmFractions(tool, foundOptions.valuesOf(FRACTIONS_OPT)); System.err.println(String.format("Building %s/%s%s.jar", outDir, jarName, suffix)); return tool.build(jarName, Paths.get(outDir)); } private static void exit(String message) { exit(message, 1); } private static void exit(String message, boolean printHelp) { exit(message, 1, printHelp); } private static void exit(String message, int code) { exit(message, code, false); } private static void exit(String message, int code, boolean printHelp) { throw new ExitException(code, printHelp, message); } private static ArtifactResolvingHelper getResolvingHelper(final List<String> repos) { final ConfigurableMavenResolverSystem resolver = Maven.configureResolver() .withMavenCentralRepo(true) .withRemoteRepo(MavenRemoteRepositories.createRemoteRepository("jboss-public-repository-group", "https://repository.jboss.org/nexus/content/groups/public/", "default") .setChecksumPolicy(MavenChecksumPolicy.CHECKSUM_POLICY_IGNORE) .setUpdatePolicy(MavenUpdatePolicy.UPDATE_POLICY_NEVER)); repos.forEach(r -> resolver.withRemoteRepo(MavenRemoteRepositories.createRemoteRepository(r, r, "default") .setChecksumPolicy(MavenChecksumPolicy.CHECKSUM_POLICY_IGNORE) .setUpdatePolicy(MavenUpdatePolicy.UPDATE_POLICY_NEVER))); return new ShrinkwrapArtifactResolvingHelper(resolver); } private static void addSwarmFractions(BuildTool tool, final List<String> deps) { deps.stream().map(f -> f.split(":")) .map(parts -> { switch (parts.length) { case 1: final FractionDescriptor desc = FractionList.get() .getFractionDescriptor("org.wildfly.swarm", parts[0]); if (desc != null) { return desc; } else { System.err.println("Warning: Unknown fraction: " + parts[0]); return null; } case 2: return new FractionDescriptor("org.wildfly.swarm", parts[0], parts[1]); case 3: return new FractionDescriptor(parts[0], parts[1], parts[2]); default: System.err.println("Warning: Invalid fraction specifier: " + String.join(":", parts)); return null; } }) .filter(f -> f != null) .forEach(f -> tool.fraction(ArtifactSpec.fromFractionDescriptor(f))); } static class ExitException extends RuntimeException { public int status; public boolean printHelp; ExitException(final int status, final boolean printHelp, final String message) { super(message); this.printHelp = printHelp; this.status = status; } } }