/*
* Copyright (c) 2012 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
* <http://www.gnu.org/licenses>.
*/
package com.redhat.rcm.version;
import static com.redhat.rcm.version.stats.VersionInfo.APP_BUILDER;
import static com.redhat.rcm.version.stats.VersionInfo.APP_COMMIT_ID;
import static com.redhat.rcm.version.stats.VersionInfo.APP_DESCRIPTION;
import static com.redhat.rcm.version.stats.VersionInfo.APP_NAME;
import static com.redhat.rcm.version.stats.VersionInfo.APP_TIMESTAMP;
import static com.redhat.rcm.version.stats.VersionInfo.APP_VERSION;
import static com.redhat.rcm.version.util.InputUtils.getClasspathResource;
import static com.redhat.rcm.version.util.InputUtils.getFile;
import static com.redhat.rcm.version.util.InputUtils.readClasspathProperties;
import static com.redhat.rcm.version.util.InputUtils.readListProperty;
import static com.redhat.rcm.version.util.InputUtils.readProperties;
import static com.redhat.rcm.version.util.InputUtils.readPropertiesList;
import static org.apache.commons.io.IOUtils.closeQuietly;
import static org.apache.commons.lang.StringUtils.join;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.BreakIterator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import org.apache.maven.mae.MAEException;
import org.apache.maven.mae.project.key.FullProjectKey;
import org.codehaus.plexus.util.StringUtils;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
import org.kohsuke.args4j.spi.MapOptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.redhat.rcm.version.mgr.VersionManager;
import com.redhat.rcm.version.mgr.mod.ProjectModder;
import com.redhat.rcm.version.mgr.session.SessionBuilder;
import com.redhat.rcm.version.mgr.session.VersionManagerSession;
import com.redhat.rcm.version.report.Report;
import com.redhat.rcm.version.util.InputUtils;
import com.redhat.rcm.version.util.VManFormatter;
import com.redhat.rcm.version.util.http.SSLUtils;
public class Cli
{
@Argument( index = 0, metaVar = "target", usage = "POM file (or directory containing POM files) to modify." )
private File target = new File( System.getProperty( "user.dir" ), "pom.xml" );
@Option( name = "-b", aliases = "--boms", usage = "File containing a list of BOM URLs to use for standardizing dependencies.\nProperty file equivalent: boms" )
private File bomList;
@Option( name = "-B", aliases = { "--bootstrap" }, usage = "Bootstrap properties to read for location of VMan configuration." )
private File bootstrapConfig;
@Option( name = "-C", aliases = "--config", usage = "Load default configuration for BOMs, toolchain, removedPluginsList, etc. from this file/url." )
private String configuration;
@Option( name = "-e", usage = "POM exclude path pattern (glob)\nProperty file equivalent: pom-file-excludes" )
private String pomExcludePattern;
@Option( name = "-E", usage = "POM exclude module list (groupId:artifactId,groupId:artifactId...)\nProperty file equivalent: pom-module-excludes" )
private String pomExcludeModules;
@Option( name = "-h", aliases = { "--help" }, usage = "Print this message and quit" )
private boolean help;
@Option( name = "-H", aliases = { "-hm", "--help-modifications" }, usage = "Print the list of available modifications and quit" )
private boolean helpModders;
@Option( name = "-hr", aliases = { "--help-reports" }, usage = "Print the list of available reports, plus their configuration options, and quit" )
private boolean helpReporters;
@Option( name = "--no-console", usage = "DON'T log information to console in addition to <workspace>/vman.log.\n" )
private boolean noConsole;
@Option( name = "--no-log-file", usage = "DON'T log information to <workspace>/vman.log.\n" )
private boolean noLogFile;
@Option( name = "-L", aliases = { "--local-repo", "--local-repository" }, usage = "Local repository directory.\nDefault: <workspace>/local-repository\nProperty file equivalent: local-repository" )
private File localRepository;
@Option( name = "-m", aliases = "--remote-repositories", usage = "Maven remote repositories from which load missing parent POMs.\nProperty file equivalent: remote-repositories." )
private String remoteRepositories;
@Option( name = "-M", aliases = { "--enable-modifications" }, usage = "List of modifications to enable for this execution (see --help-modifications for more information)." )
private String modifications;
@Option( name = "-O", aliases = { "--capture-output", "--capture-pom" }, usage = "Write captured (missing) definitions to this POM location.\nProperty file equivalent: capture-pom" )
private File capturePom;
@Option( name = "-p", usage = "POM path pattern (glob).\nDefault: **/*.pom,**/pom.xml" )
private String pomPattern = "**/*.pom,**/pom.xml";
@Option( name = "-P", aliases = { "--preserve" }, usage = "Write changed POMs back to original input files.\nDefault: false" )
private boolean preserveFiles = false;
@Option( name = "-r", aliases = { "--rm-plugins", "--removed-plugins" }, usage = "List of plugins (format: <groupId:artifactId>[,<groupId:artifactId>]) to REMOVE if found.\nProperty file equivalent: removed-plugins" )
private String removedPluginsList;
@Option( name = "--removed-tests", usage = "List of test modules (format: <groupId:artifactId>[,<groupId:artifactId>]) to remove (via maven.test.skip) if found.\nProperty file equivalent: removed-tests" )
private String removedTestsList;
@Option( name = "--extensions-whitelist", usage = "List of extensions (format: <groupId:artifactId>[,<groupId:artifactId>]) to preserve.\nProperty file equivalent: extensions-whitelist" )
private String extensionsWhitelistList;
@Option( name = "-R", aliases = { "--report-dir" }, usage = "Write reports here.\nDefault: <workspace>/reports" )
private File reports = new File( "vman-workspace/reports" );
@Option( name = "-s", aliases = "--version-suffix", usage = "A suffix to append to each POM's version.\nProperty file equivalent: version-suffix" )
private String versionSuffix;
@Option( name = "--version-modifier", usage = "Change each POM's version using pattern:replacement format.\nProperty file equivalent: version-modifier" )
private String versionModifier;
@Option( name = "--strict", usage = "Change ONLY the dependencies, plugins, and parents that are listed in BOMs and toolchain POM\nDefault: false\nProperty file equivalent: strict" )
private boolean strict = false;
@Option( name = "-S", aliases = { "--settings" }, usage = "Maven settings.xml file/URL.\nProperty file equivalent: settings" )
private String settings;
@Option( name = "-t", aliases = "--toolchain", usage = "Toolchain POM URL, containing standard plugin versions in the build/pluginManagement section, and plugin injections in the regular build/plugins section.\nProperty file equivalent: toolchain" )
private String toolchain;
@Option( name = "-T", aliases = "--test-config", usage = "Test-load the configuration given, and print diagnostic information" )
private boolean testConfig;
@Option( name = "-XR", handler = MapOptionHandler.class, aliases = "--user-prop", usage = "Add user-defined property of the form x=y that reports and other components can pick up on" )
private Map<String, String> reportProperties;
@Option( name = "-v", aliases = "--version", usage = "Show version information and quit." )
private boolean showVersion;
@Option( name = "-W", aliases = { "--workspace" }, usage = "Backup original files here up before modifying.\nDefault: vman-workspace" )
private File workspace = new File( "vman-workspace" );
@Option( name = "-Z", aliases = { "--no-system-exit" }, usage = "Don't call System.exit(..) with the return value (for embedding/testing)." )
private boolean noSystemExit;
@Option( name = "--trustpath", usage = "Directory containing .pem files with certificates of servers to trust. (Use 'classpath:' prefix for a directory embedded in the jar.)" )
private String truststorePath = DEFAULT_TRUSTSTORE_PATH;
@Option( name = "--use-effective-poms", usage = "Enable resolution of effective POMs for projects being modified." )
private boolean useEffectivePoms = false;
private static final String DEFAULT_TRUSTSTORE_PATH = "classpath:ssl/trust";
private static final File DEFAULT_CONFIG_FILE = new File( System.getProperty( "user.home" ), ".vman.properties" );
static final String BOOTSTRAP_PROPERTIES = "vman.boot.properties";
public static final String REMOTE_REPOSITORIES_PROPERTY = "remote-repositories";
@Deprecated
public static final String REMOTE_REPOSITORY_PROPERTY = "remote-repository";
public static final String REPORT_PROPERTY_PREFIX = "report.";
public static final String VERSION_SUFFIX_PROPERTY = "version-suffix";
public static final String VERSION_MODIFIER_PROPERTY = "version-modifier";
public static final String TOOLCHAIN_PROPERTY = "toolchain";
public static final String BOMS_LIST_PROPERTY = "boms";
public static final String REMOVED_PLUGINS_PROPERTY = "removed-plugins";
public static final String EXTENSIONS_WHITELIST_PROPERTY = "extensions-whitelist";
public static final String POM_EXCLUDE_MODULE_PROPERTY = "pom-module-excludes";
public static final String POM_EXCLUDE_FILE_PROPERTY = "pom-file-excludes";
public static final String REMOVED_TESTS_PROPERTY = "removed-tests";
public static final String LOCAL_REPOSITORY_PROPERTY = "local-repository";
public static final String SETTINGS_PROPERTY = "settings";
public static final String CAPTURE_POM_PROPERTY = "capture-pom";
public static final String STRICT_MODE_PROPERTY = "strict";
public static final String MODIFICATIONS = "modifications";
public static final String RELOCATIONS_PROPERTY = "relocated-coordinates";
public static final String PROPERTY_MAPPINGS_PROPERTY = "property-mappings";
public static final String BOOT_CONFIG_PROPERTY = "configuration";
public static final String TRUSTSTORE_PATH_PROPERTY = "truststore-path";
public static final String USE_EFFECTIVE_POMS_PROPERTY = "use-effective-poms";
private static final File DEFAULT_BOOTSTRAP_CONFIG = new File( System.getProperty( "user.home" ), ".vman.boot.properties" );
private static VersionManager vman;
private List<String> boms;
private List<String> removedPlugins;
private List<String> extensionsWhitelist;
private List<String> removedTests;
private List<String> modders;
private Map<String, String> relocatedCoords;
private Map<String, String> propertyMappings;
private String bootstrapLocation;
private String configLocation;
private boolean bootstrapRead;
private File logFile = new File( workspace, "vman.log" );
private static int exitValue = Integer.MIN_VALUE;
/**
* The global logger for vman. Set here to prevent it being gc'd.
*/
private static final java.util.logging.Logger root = java.util.logging.Logger.getLogger( "com.redhat.rcm.version" );
public static void main( final String[] args )
{
final Cli cli = new Cli();
final CmdLineParser parser = new CmdLineParser( cli );
try
{
parser.parseArgument( args );
final boolean useLog = !( cli.noLogFile || cli.testConfig || /*cli.help ||*/cli.helpModders || cli.showVersion || cli.helpReporters );
// System.out.printf( "--no-console: %s \n--no-log-file: %s \n--test-config: %s\n--help: %s\n--help-modifications: %s\n--version: %s\n\nUse logfile? %s\n\n",
// cli.noConsole, cli.noLogFile, cli.testConfig, cli.help, cli.helpModders,
// cli.showVersion, useLog );
configureLogging( !cli.noConsole, useLog, cli.logFile );
LoggerFactory.getLogger( Cli.class )
.info( "Testing log appenders..." );
vman = VersionManager.getInstance();
exitValue = 0;
if ( cli.help )
{
printUsage( parser, null );
}
else if ( cli.helpModders )
{
printModders();
}
else if ( cli.helpReporters )
{
printReporters();
}
else if ( cli.showVersion )
{
printVersionInfo();
}
else if ( cli.testConfig )
{
cli.testConfigAndPrintDiags();
}
else
{
exitValue = cli.run();
}
if ( !cli.noSystemExit )
{
System.exit( exitValue );
}
}
catch ( final CmdLineException error )
{
printUsage( parser, error );
}
catch ( final MAEException e )
{
printUsage( parser, e );
}
catch ( final MalformedURLException e )
{
printUsage( parser, e );
}
}
public Cli( final File target, final File bomList )
{
this.target = target;
this.bomList = bomList;
}
public Cli()
{
}
private void testConfigAndPrintDiags()
{
VersionManagerSession session = null;
final List<VManException> errors = new ArrayList<VManException>();
try
{
session = initSession();
}
catch ( final VManException e )
{
errors.add( e );
}
if ( session != null )
{
try
{
vman.configureSession( boms, toolchain, session );
}
catch ( final VManException e )
{
errors.add( e );
}
}
final FullProjectKey toolchainKey = session == null ? null : session.getToolchainKey();
final List<FullProjectKey> bomCoords = session == null ? null : session.getBomCoords();
final LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
map.put( "Bootstrap location:", bootstrapLocation );
map.put( "Bootstrap read?", bootstrapRead );
map.put( "Config location:", configLocation );
map.put( "", "" );
map.put( " ", "" );
map.put( "Toolchain location:", toolchain );
map.put( "Toolchain:", toolchainKey );
map.put( " ", "" );
map.put( "BOM locations:", boms );
map.put( "BOMs:", bomCoords );
map.put( " ", "" );
map.put( " ", "" );
map.put( "Settings.xml:", settings );
map.put( "Remote repo:", remoteRepositories );
System.out.println( "Version information:\n-------------------------------------------------\n\n" );
printVersionInfo();
System.out.printf( "Diagnostics:\n-------------------------------------------------\n\n" );
int max = 0;
for ( final String key : map.keySet() )
{
max = Math.max( max, key.length() );
}
final StringBuilder indent = new StringBuilder();
for ( int i = 0; i < max + 4; i++ )
{
indent.append( ' ' );
}
final int descMax = 75 - max;
final String fmt = "%-" + max + "s %-" + descMax + "s\n";
for ( final Map.Entry<String, Object> entry : map.entrySet() )
{
final Object value = entry.getValue();
String val = value == null ? "-NONE-" : String.valueOf( value );
if ( value instanceof Collection<?> )
{
final Collection<?> coll = ( (Collection<?>) value );
if ( coll.isEmpty() )
{
val = "-NONE-";
}
else
{
val = join( coll, "\n" + indent ) + "\n";
}
}
System.out.printf( fmt, entry.getKey(), val );
}
System.out.println();
System.out.printf( "Errors:\n-------------------------------------------------\n%s\n\n", errors.isEmpty() ? "-NONE" : join( errors, "\n\n" ) );
System.out.println();
}
private static void configureLogging( boolean useConsole, final boolean useLogFile, final File logFile )
{
System.out.println( "Log file is: " + logFile.getAbsolutePath() );
final List<Handler> handlers = new ArrayList<Handler>();
if ( !useConsole && !useLogFile )
{
if ( !useLogFile )
{
System.out.println( "\n\nNOTE: --no-console option has been OVERRIDDEN since --no-log-file option was also provided.\nOutputting to console ONLY.\n" );
useConsole = true;
}
}
if ( useConsole )
{
final Handler chandler = new ConsoleHandler();
chandler.setFormatter( new VManFormatter() );
chandler.setLevel( Level.ALL );
handlers.add( chandler );
}
if ( useLogFile )
{
try
{
final File dir = logFile.getParentFile();
if ( dir != null && !dir.isDirectory() && !dir.mkdirs() )
{
throw new RuntimeException( "Failed to create parent directory for logfile: " + dir.getAbsolutePath() );
}
final Handler fhandler = new FileHandler( logFile.getPath(), false );
fhandler.setFormatter( new VManFormatter() );
fhandler.setLevel( Level.ALL );
handlers.add( fhandler );
}
catch ( final IOException e )
{
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter( sw );
e.printStackTrace( pw );
System.out.printf( "ERROR: Failed to initialize log file: %s. Reason: %s\n\n%s\n\n", logFile, e.getMessage(), sw.toString() );
throw new RuntimeException( "Failed to initialize logfile." );
}
}
root.setUseParentHandlers( false );
final Handler[] currenthandlers = root.getHandlers();
for ( final Handler h : currenthandlers )
{
h.close();
root.removeHandler( h );
}
for ( final Handler h : handlers )
{
root.addHandler( h );
}
}
public int run()
throws MAEException, VManException, MalformedURLException
{
final Logger logger = LoggerFactory.getLogger( getClass() );
final VersionManagerSession session = initSession();
if ( session.getModderKeys()
.contains( "bom-realignment" ) && ( boms == null || boms.isEmpty() ) )
{
logger.error( "You must specify at least one BOM." );
return -2;
}
if ( session.getErrors()
.isEmpty() )
{
logger.info( "Modifying POM(s).\n\nTarget:\n\t" + target + "\n\nBOMs:\n\t" + StringUtils.join( boms.iterator(), "\n\t" )
+ "\n\nWorkspace:\n\t" + workspace + "\n\nReports:\n\t" + reports );
if ( target.isDirectory() )
{
vman.modifyVersions( target, pomPattern, pomExcludePattern, boms, toolchain, session );
}
else
{
vman.modifyVersions( target, boms, toolchain, session );
}
}
reports.mkdirs();
vman.generateReports( reports, session );
if ( capturePom != null && capturePom.exists() )
{
logger.warn( "\n\n\n\n\nMissing dependency/plugin information has been captured in:\n\n\t" + capturePom.getAbsolutePath() + "\n\n\n\n" );
}
final List<Throwable> errors = session.getErrors();
if ( errors != null && !errors.isEmpty() )
{
logger.error( errors.size() + " errors detected!\n\n" );
int i = 1;
for ( final Throwable error : errors )
{
logger.error( "\n\n" + i, error );
i++;
}
return -1;
}
return 0;
}
private VersionManagerSession initSession()
throws VManException
{
SSLUtils.initSSLContext( truststorePath );
loadConfiguration();
loadBomList();
loadPlugins();
loadAndNormalizeModifications();
final Logger logger = LoggerFactory.getLogger( getClass() );
logger.info( "modifications = " + join( modders, " " ) );
final SessionBuilder builder = new SessionBuilder( workspace, reports ).withVersionSuffix( versionSuffix )
.withVersionModifier( versionModifier )
.withRemovedPlugins( removedPlugins )
.withRemovedTests( removedTests )
.withExtensionsWhitelist( extensionsWhitelist )
.withModders( modders )
.withPreserveFiles( preserveFiles )
.withStrict( strict )
.withCoordinateRelocations( relocatedCoords )
.withPropertyMappings( propertyMappings )
.withExcludedModulePoms( pomExcludeModules )
.withUseEffectivePoms( useEffectivePoms )
.withUserProperties( reportProperties );
final VersionManagerSession session = builder.build();
if ( remoteRepositories != null )
{
try
{
session.setRemoteRepositories( remoteRepositories );
}
catch ( final MalformedURLException e )
{
throw new VManException( "Cannot initialize remote repositories: %s. Error: %s", e, remoteRepositories, e.getMessage() );
}
}
if ( settings != null )
{
session.setSettingsXml( settings );
}
if ( localRepository == null )
{
localRepository = new File( workspace, "local-repository" );
}
session.setLocalRepositoryDirectory( localRepository );
if ( capturePom != null )
{
session.setCapturePom( capturePom );
}
return session;
}
private static void printVersionInfo()
{
final StringBuilder sb = new StringBuilder();
sb.append( APP_NAME )
.append( "\n\n" )
.append( APP_DESCRIPTION )
.append( "\n\n" );
final LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
map.put( "Built By:", APP_BUILDER );
map.put( "Commit ID:", APP_COMMIT_ID );
map.put( "Built On:", APP_TIMESTAMP );
map.put( "Version:", APP_VERSION );
sb.append( formatHelpMap( map, "\n" ) );
sb.append( "\n\n" );
System.out.println( sb.toString() );
}
private static void printReporters()
{
final Map<String, Report> reports = vman.getReports();
final List<String> keys = new ArrayList<String>( reports.keySet() );
Collections.sort( keys );
final int max = maxKeyLength( reports.keySet() );
final int valMax = 75 - max;
final String headerFmt = "%-" + max + "s %-" + valMax + "s\n";
// final int propMax = valMax - 8;
// final String propFmt = "%-" + ( max + 8 ) + "s %-" + propMax + "s\n";
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter( sw );
pw.println( "The following project reports are available: \n\n" );
for ( final String key : keys )
{
final Report r = reports.get( key );
printKVLine( key, r.getDescription(), headerFmt, valMax, pw );
final Map<String, String> props = r.getPropertyDescriptions();
if ( props != null && !props.isEmpty() )
{
pw.println( "\n Properties:\n ---------------" );
pw.println();
for ( final Entry<String, String> entry : props.entrySet() )
{
final String pk = entry.getKey();
final String value = entry.getValue();
pw.println( " - " + REPORT_PROPERTY_PREFIX + key + "." + pk );
pw.println();
printTextLine( value, " ", 69, pw );
}
}
pw.println();
pw.println();
}
System.out.println( sw.toString() );
}
private static void printModders()
{
final Map<String, ProjectModder> modders = vman.getModders();
final List<String> keys = new ArrayList<String>( modders.keySet() );
Collections.sort( keys );
final LinkedHashMap<String, Object> props = new LinkedHashMap<String, Object>();
for ( final String key : keys )
{
props.put( key, modders.get( key )
.getDescription() );
}
final StringBuilder sb = new StringBuilder();
sb.append( "The following project modifications are available: " );
sb.append( formatHelpMap( props, "\n\n" ) );
sb.append( "\n\nNOTE: To ADD any of these modifiers to the standard list, use the notation '--modifications=+<modifier-id>' (prefixed with '+') or for the properties file use 'modifications=+...'.\n\nThe standard modifiers are: " );
for ( final String key : ProjectModder.STANDARD_MODIFICATIONS )
{
sb.append( String.format( "\n - %s", key ) );
}
sb.append( "\n\n" );
System.out.println( sb );
}
private static String formatHelpMap( final LinkedHashMap<String, Object> map, final String itemSeparator )
{
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter( sw );
final int max = maxKeyLength( map.keySet() );
final int descMax = 75 - max;
final String fmt = "%-" + max + "s %-" + descMax + "s" + itemSeparator;
for ( final Map.Entry<String, Object> entry : map.entrySet() )
{
final String key = entry.getKey();
final String description = entry.getValue() == null ? "-NONE-" : String.valueOf( entry.getValue() );
printKVLine( key, description, fmt, descMax, pw );
}
return sw.toString();
}
private static void printTextLine( final String line, final String indent, final int max, final PrintWriter pw )
{
final String fmt = "%s%-" + max + "s\n";
final List<String> lines = new ArrayList<String>();
final BreakIterator iter = BreakIterator.getLineInstance();
iter.setText( line );
int start = iter.first();
int end = BreakIterator.DONE;
final StringBuilder currentLine = new StringBuilder();
String seg;
while ( start != BreakIterator.DONE && ( end = iter.next() ) != BreakIterator.DONE )
{
seg = line.substring( start, end );
if ( currentLine.length() + seg.length() > max )
{
lines.add( currentLine.toString() );
currentLine.setLength( 0 );
}
currentLine.append( seg );
start = end;
}
if ( currentLine.length() > 0 )
{
lines.add( currentLine.toString() );
}
for ( final String ln : lines )
{
pw.printf( fmt, indent, ln );
}
}
private static void printKVLine( final String key, final String value, final String fmt, final int valMax, final PrintWriter pw )
{
final List<String> lines = new ArrayList<String>();
final BreakIterator iter = BreakIterator.getLineInstance();
iter.setText( value );
int start = iter.first();
int end = BreakIterator.DONE;
final StringBuilder currentLine = new StringBuilder();
String seg;
while ( start != BreakIterator.DONE && ( end = iter.next() ) != BreakIterator.DONE )
{
seg = value.substring( start, end );
if ( currentLine.length() + seg.length() > valMax )
{
lines.add( currentLine.toString() );
currentLine.setLength( 0 );
}
currentLine.append( seg );
start = end;
}
if ( currentLine.length() > 0 )
{
lines.add( currentLine.toString() );
}
pw.printf( fmt, key, lines.isEmpty() ? "" : lines.get( 0 ) );
if ( lines.size() > 1 )
{
for ( int i = 1; i < lines.size(); i++ )
{
// blank string to serve for indentation in format with two fields.
pw.printf( fmt, "", lines.get( i ) );
}
}
}
private static int maxKeyLength( final Set<String> keys )
{
int max = 0;
for ( final String key : keys )
{
max = Math.max( max, key.length() );
}
return max;
}
private void loadPlugins()
{
if ( extensionsWhitelistList == null && extensionsWhitelistList != null )
{
final String[] ls = extensionsWhitelistList.split( "\\s*,\\s*" );
extensionsWhitelist = Arrays.asList( ls );
}
if ( removedPlugins == null && removedPluginsList != null )
{
final String[] ls = removedPluginsList.split( "\\s*,\\s*" );
removedPlugins = Arrays.asList( ls );
}
if ( removedTests == null && removedTestsList != null )
{
final String[] ls = removedTestsList.split( "\\s*,\\s*" );
removedTests = Arrays.asList( ls );
}
}
private void loadAndNormalizeModifications()
{
if ( modifications != null )
{
final String[] ls = modifications.split( "\\s*,\\s*" );
modders = new ArrayList<String>();
for ( final String modder : ls )
{
if ( !modders.contains( modder ) )
{
modders.add( modder );
}
}
}
final List<String> mods = new ArrayList<String>();
boolean loadStandards = modders == null;
if ( modders != null )
{
if ( !modders.isEmpty() && modders.iterator()
.next()
.startsWith( "+" ) )
{
loadStandards = true;
}
for ( final String key : modders )
{
if ( ProjectModder.STANDARD_MODS_ALIAS.equals( key ) )
{
loadStandards = true;
}
else if ( key.startsWith( "+" ) )
{
if ( key.length() > 1 )
{
mods.add( key.substring( 1 )
.trim() );
}
}
else
{
mods.add( key );
}
}
}
if ( loadStandards )
{
mods.addAll( ProjectModder.STANDARD_MODIFICATIONS );
}
modders = mods;
}
private void loadConfiguration()
throws VManException
{
final Logger logger = LoggerFactory.getLogger( getClass() );
File config = null;
if ( configuration != null )
{
config = InputUtils.getFile( configuration, workspace );
}
if ( config == null )
{
config = loadBootstrapConfig();
}
if ( config == null )
{
configLocation = DEFAULT_CONFIG_FILE.getAbsolutePath();
config = DEFAULT_CONFIG_FILE;
}
if ( config != null && config.canRead() )
{
InputStream is = null;
try
{
is = new FileInputStream( config );
final Properties props = new Properties();
props.load( is );
final StringWriter sWriter = new StringWriter();
for ( final Enumeration<?> e = props.propertyNames(); e.hasMoreElements(); )
{
final String key = (String) e.nextElement();
sWriter.write( " " );
sWriter.write( key );
sWriter.write( " = " );
sWriter.write( props.getProperty( key ) );
sWriter.write( "\n" );
}
props.list( new PrintWriter( sWriter ) );
logger.info( "Loading configuration from: " + config + ":\n\n" + sWriter );
final File downloadsDir = VersionManagerSession.getDownloadsDir( workspace );
final List<String> relocations = readListProperty( props, RELOCATIONS_PROPERTY );
if ( relocations != null )
{
relocatedCoords = readPropertiesList( relocations, downloadsDir, true );
}
else
{
relocatedCoords = new HashMap<String, String>();
}
final List<String> mappingsLocations = readListProperty( props, PROPERTY_MAPPINGS_PROPERTY );
if ( mappingsLocations != null )
{
this.propertyMappings = readPropertiesList( mappingsLocations, downloadsDir, true );
}
else
{
this.propertyMappings = new HashMap<String, String>();
}
if ( removedPluginsList == null )
{
removedPlugins = readListProperty( props, REMOVED_PLUGINS_PROPERTY );
}
if ( removedTestsList == null )
{
removedTests = readListProperty( props, REMOVED_TESTS_PROPERTY );
}
if ( extensionsWhitelistList == null )
{
extensionsWhitelist = readListProperty( props, EXTENSIONS_WHITELIST_PROPERTY );
}
if ( pomExcludeModules == null )
{
pomExcludeModules = props.getProperty( POM_EXCLUDE_MODULE_PROPERTY );
}
if ( pomExcludePattern == null )
{
pomExcludePattern = props.getProperty( POM_EXCLUDE_FILE_PROPERTY );
}
if ( modifications == null )
{
final List<String> lst = readListProperty( props, MODIFICATIONS );
logger.info( "modifications from properties: '" + join( lst, " " ) + "'" );
if ( lst != null )
{
modders = modders == null ? new ArrayList<String>() : new ArrayList<String>( modders );
modders.addAll( lst );
}
}
if ( bomList == null )
{
if ( boms == null )
{
boms = new ArrayList<String>();
}
final List<String> pBoms = readListProperty( props, BOMS_LIST_PROPERTY );
if ( pBoms != null )
{
boms.addAll( pBoms );
}
}
if ( toolchain == null )
{
toolchain = props.getProperty( TOOLCHAIN_PROPERTY );
if ( toolchain != null )
{
toolchain = toolchain.trim();
}
}
if ( versionSuffix == null )
{
versionSuffix = props.getProperty( VERSION_SUFFIX_PROPERTY );
if ( versionSuffix != null )
{
versionSuffix = versionSuffix.trim();
}
}
if ( versionModifier == null )
{
versionModifier = props.getProperty( VERSION_MODIFIER_PROPERTY );
if ( versionModifier != null )
{
versionModifier = versionModifier.trim();
}
}
if ( remoteRepositories == null )
{
remoteRepositories = props.getProperty( REMOTE_REPOSITORIES_PROPERTY );
if ( remoteRepositories != null )
{
remoteRepositories = remoteRepositories.trim();
}
else
{
remoteRepositories = props.getProperty( REMOTE_REPOSITORY_PROPERTY );
if ( remoteRepositories != null )
{
logger.warn( "Using deprecated " + REMOTE_REPOSITORY_PROPERTY );
remoteRepositories = remoteRepositories.trim();
}
}
}
if ( settings == null )
{
final String s = props.getProperty( SETTINGS_PROPERTY );
if ( s != null )
{
settings = s;
}
}
if ( localRepository == null )
{
final String l = props.getProperty( LOCAL_REPOSITORY_PROPERTY );
if ( l != null )
{
localRepository = new File( l );
}
}
if ( capturePom == null )
{
final String p = props.getProperty( CAPTURE_POM_PROPERTY );
if ( p != null )
{
capturePom = new File( p );
}
}
if ( !strict )
{
strict = Boolean.valueOf( props.getProperty( STRICT_MODE_PROPERTY, Boolean.toString( Boolean.FALSE ) ) );
}
if ( !useEffectivePoms )
{
useEffectivePoms = Boolean.valueOf( props.getProperty( USE_EFFECTIVE_POMS_PROPERTY, Boolean.toString( Boolean.FALSE ) ) );
}
if ( truststorePath == null )
{
truststorePath = props.getProperty( TRUSTSTORE_PATH_PROPERTY );
}
final Map<String, String> userProps = new HashMap<String, String>();
for ( final Enumeration<?> keys = props.keys(); keys.hasMoreElements(); )
{
final String key = (String) keys.nextElement();
if ( key.startsWith( REPORT_PROPERTY_PREFIX ) )
{
userProps.put( key.substring( REPORT_PROPERTY_PREFIX.length() ), props.getProperty( key ) );
}
}
if ( !userProps.isEmpty() )
{
if ( reportProperties == null )
{
reportProperties = userProps;
}
else
{
userProps.putAll( reportProperties );
reportProperties = userProps;
}
}
}
catch ( final IOException e )
{
throw new VManException( "Failed to load configuration from: " + config, e );
}
finally
{
closeQuietly( is );
}
}
else
{
configLocation = "command-line";
}
}
/**
* Try to load bootstrap configuration using the following order or preference:
* 1. configured file (using -B option)
* 2. embedded resource (classpath:bootstrap.properties)
* 3. default file ($HOME/.vman.boot.properties)
*
* @return The configuration file referenced by the bootstrap properties, or null if no bootstrap properties is
* found.
*
* @throws VManException In cases where the specified bootstrap properties file is unreadable.
*/
private File loadBootstrapConfig()
throws VManException
{
final Logger logger = LoggerFactory.getLogger( getClass() );
Map<String, String> bootProps = null;
if ( bootstrapConfig == null )
{
logger.info( "Reading bootstrap info from classpath resource: " + BOOTSTRAP_PROPERTIES );
final URL resource = getClasspathResource( BOOTSTRAP_PROPERTIES );
if ( resource != null )
{
bootstrapLocation = "classpath:" + resource;
bootProps = readClasspathProperties( BOOTSTRAP_PROPERTIES );
}
else if ( DEFAULT_BOOTSTRAP_CONFIG.exists() && DEFAULT_BOOTSTRAP_CONFIG.canRead() )
{
logger.info( "Reading bootstrap info from: " + DEFAULT_BOOTSTRAP_CONFIG );
bootstrapLocation = "file:" + DEFAULT_BOOTSTRAP_CONFIG.getAbsolutePath();
bootProps = readProperties( DEFAULT_BOOTSTRAP_CONFIG );
}
}
else
{
if ( !bootstrapConfig.exists() || !bootstrapConfig.canRead() )
{
throw new VManException( "Cannot read bootstrap from: " + bootstrapConfig );
}
else
{
logger.info( "Reading bootstrap info from: " + bootstrapConfig );
bootstrapLocation = "file:" + bootstrapConfig.getAbsolutePath();
bootProps = readProperties( bootstrapConfig );
}
}
bootstrapRead = bootProps != null;
if ( bootProps != null )
{
configLocation = bootProps.get( BOOT_CONFIG_PROPERTY );
if ( configLocation != null )
{
logger.info( "Reading configuration from: " + configLocation );
try
{
final File file = getFile( configLocation, new File( System.getProperty( "java.io.tmpdir" ) ), true );
logger.info( "...downloaded to file: " + file );
return file;
}
catch ( final VManException e )
{
logger.error( "Failed to download configuration from: " + configLocation + ". Reason: " + e.getMessage(), e );
throw e;
}
}
}
return null;
}
private void loadBomList()
throws VManException
{
if ( boms == null )
{
boms = new ArrayList<String>();
}
if ( bomList != null && bomList.canRead() )
{
BufferedReader reader = null;
try
{
reader = new BufferedReader( new FileReader( bomList ) );
String line = null;
while ( ( line = reader.readLine() ) != null )
{
boms.add( line.trim() );
}
}
catch ( final IOException e )
{
throw new VManException( "Failed to read bom list from: " + bomList, e );
}
finally
{
closeQuietly( reader );
}
}
}
private static void printUsage( final CmdLineParser parser, final Exception error )
{
if ( error != null )
{
System.err.println( "Invalid option(s): " + error.getMessage() );
System.err.println();
}
System.err.println( "Usage: $0 [OPTIONS] [<target-path>]" );
System.err.println();
System.err.println();
// If we are running under a Linux shell COLUMNS might be available for the width
// of the terminal.
parser.setUsageWidth( ( System.getenv( "COLUMNS" ) == null ? 100 : Integer.valueOf( System.getenv( "COLUMNS" ) ) ) );
parser.printUsage( System.err );
System.err.println();
}
public File getTarget()
{
return target;
}
public File getBomList()
{
return bomList;
}
public File getBootstrapConfig()
{
return bootstrapConfig;
}
public String getConfiguration()
{
return configuration;
}
public String getPomExcludePattern()
{
return pomExcludePattern;
}
public String getPomExcludeModules()
{
return pomExcludeModules;
}
public boolean isHelp()
{
return help;
}
public boolean isHelpModders()
{
return helpModders;
}
public boolean isNoConsole()
{
return noConsole;
}
public boolean isNoLogFile()
{
return noLogFile;
}
public File getLocalRepository()
{
return localRepository;
}
public String getRemoteRepositories()
{
return remoteRepositories;
}
public String getModifications()
{
return modifications;
}
public File getCapturePom()
{
return capturePom;
}
public String getPomPattern()
{
return pomPattern;
}
public boolean isPreserveFiles()
{
return preserveFiles;
}
public String getRemovedPluginsList()
{
return removedPluginsList;
}
public String getRemovedTestsList()
{
return removedTestsList;
}
public String getExtensionsWhitelistList()
{
return extensionsWhitelistList;
}
public File getReports()
{
return reports;
}
public String getVersionSuffix()
{
return versionSuffix;
}
public String getVersionModifier()
{
return versionModifier;
}
public boolean isStrict()
{
return strict;
}
public String getSettings()
{
return settings;
}
public String getToolchain()
{
return toolchain;
}
public boolean isTestConfig()
{
return testConfig;
}
public Map<String, String> getUserProperties()
{
return reportProperties;
}
public boolean isShowVersion()
{
return showVersion;
}
public File getWorkspace()
{
return workspace;
}
public boolean isNoSystemExit()
{
return noSystemExit;
}
public String getTruststorePath()
{
return truststorePath;
}
public boolean isUseEffectivePoms()
{
return useEffectivePoms;
}
public List<String> getBoms()
{
return boms;
}
public List<String> getRemovedPlugins()
{
return removedPlugins;
}
public List<String> getExtensionsWhitelist()
{
return extensionsWhitelist;
}
public List<String> getRemovedTests()
{
return removedTests;
}
public List<String> getModders()
{
return modders;
}
public Map<String, String> getRelocatedCoords()
{
return relocatedCoords;
}
public Map<String, String> getPropertyMappings()
{
return propertyMappings;
}
public String getBootstrapLocation()
{
return bootstrapLocation;
}
public String getConfigLocation()
{
return configLocation;
}
public boolean isBootstrapRead()
{
return bootstrapRead;
}
public File getLogFile()
{
return logFile;
}
public void setTarget( final File target )
{
this.target = target;
}
public void setBomList( final File bomList )
{
this.bomList = bomList;
}
public void setBootstrapConfig( final File bootstrapConfig )
{
this.bootstrapConfig = bootstrapConfig;
}
public void setConfiguration( final String configuration )
{
this.configuration = configuration;
}
public void setPomExcludePattern( final String pomExcludePattern )
{
this.pomExcludePattern = pomExcludePattern;
}
public void setPomExcludeModules( final String pomExcludeModules )
{
this.pomExcludeModules = pomExcludeModules;
}
public void setHelp( final boolean help )
{
this.help = help;
}
public void setHelpModders( final boolean helpModders )
{
this.helpModders = helpModders;
}
public void setNoConsole( final boolean noConsole )
{
this.noConsole = noConsole;
}
public void setNoLogFile( final boolean noLogFile )
{
this.noLogFile = noLogFile;
}
public void setLocalRepository( final File localRepository )
{
this.localRepository = localRepository;
}
public void setRemoteRepositories( final String remoteRepositories )
{
this.remoteRepositories = remoteRepositories;
}
public void setModifications( final String modifications )
{
this.modifications = modifications;
}
public void setCapturePom( final File capturePom )
{
this.capturePom = capturePom;
}
public void setPomPattern( final String pomPattern )
{
this.pomPattern = pomPattern;
}
public void setPreserveFiles( final boolean preserveFiles )
{
this.preserveFiles = preserveFiles;
}
public void setRemovedPluginsList( final String removedPluginsList )
{
this.removedPluginsList = removedPluginsList;
}
public void setRemovedTestsList( final String removedTestsList )
{
this.removedTestsList = removedTestsList;
}
public void setExtensionsWhitelistList( final String extensionsWhitelistList )
{
this.extensionsWhitelistList = extensionsWhitelistList;
}
public void setReports( final File reports )
{
this.reports = reports;
}
public void setVersionSuffix( final String versionSuffix )
{
this.versionSuffix = versionSuffix;
}
public void setVersionModifier( final String versionModifier )
{
this.versionModifier = versionModifier;
}
public void setStrict( final boolean strict )
{
this.strict = strict;
}
public void setSettings( final String settings )
{
this.settings = settings;
}
public void setToolchain( final String toolchain )
{
this.toolchain = toolchain;
}
public void setTestConfig( final boolean testConfig )
{
this.testConfig = testConfig;
}
public void setUserProperties( final Map<String, String> userProperties )
{
this.reportProperties = userProperties;
}
public void setShowVersion( final boolean showVersion )
{
this.showVersion = showVersion;
}
public void setWorkspace( final File workspace )
{
this.workspace = workspace;
}
public void setNoSystemExit( final boolean noSystemExit )
{
this.noSystemExit = noSystemExit;
}
public void setTruststorePath( final String truststorePath )
{
this.truststorePath = truststorePath;
}
public void setUseEffectivePoms( final boolean useEffectivePoms )
{
this.useEffectivePoms = useEffectivePoms;
}
public void setBoms( final List<String> boms )
{
this.boms = boms;
}
public void setRemovedPlugins( final List<String> removedPlugins )
{
this.removedPlugins = removedPlugins;
}
public void setExtensionsWhitelist( final List<String> extensionsWhitelist )
{
this.extensionsWhitelist = extensionsWhitelist;
}
public void setRemovedTests( final List<String> removedTests )
{
this.removedTests = removedTests;
}
public void setModders( final List<String> modders )
{
this.modders = modders;
}
public void setRelocatedCoords( final Map<String, String> relocatedCoords )
{
this.relocatedCoords = relocatedCoords;
}
public void setPropertyMappings( final Map<String, String> propertyMappings )
{
this.propertyMappings = propertyMappings;
}
public void setBootstrapLocation( final String bootstrapLocation )
{
this.bootstrapLocation = bootstrapLocation;
}
public void setConfigLocation( final String configLocation )
{
this.configLocation = configLocation;
}
public void setBootstrapRead( final boolean bootstrapRead )
{
this.bootstrapRead = bootstrapRead;
}
public void setLogFile( final File logFile )
{
this.logFile = logFile;
}
}