diff --git a/maven-mboot2/.cvsignore b/maven-mboot2/.cvsignore new file mode 100644 index 0000000000..d9cded9200 --- /dev/null +++ b/maven-mboot2/.cvsignore @@ -0,0 +1,11 @@ +*~ +*.log +target +dist +*.ipr +*.iws +*.iml +dist +target +.classpath +.project diff --git a/maven-mboot2/build b/maven-mboot2/build new file mode 100755 index 0000000000..e3465b29dd --- /dev/null +++ b/maven-mboot2/build @@ -0,0 +1,13 @@ +#!/bin/sh + +buildDir=target + +rm -rf ${buildDir} > /dev/null 2>&1 + +mkdir ${buildDir} + +javac -d ${buildDir} @sources.txt + +cp src/main/resources/mboot.deps ${buildDir}/mboot.deps + +( cd ${buildDir} ; jar -cf ../mboot.jar -m ../manifest.txt * ) diff --git a/maven-mboot2/manifest.txt b/maven-mboot2/manifest.txt new file mode 100644 index 0000000000..3af02e21b9 --- /dev/null +++ b/maven-mboot2/manifest.txt @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Created-By: 0.92-gcc +Main-Class: MBoot diff --git a/maven-mboot2/mboot.jar b/maven-mboot2/mboot.jar new file mode 100644 index 0000000000..11a0169a7f Binary files /dev/null and b/maven-mboot2/mboot.jar differ diff --git a/maven-mboot2/sources.txt b/maven-mboot2/sources.txt new file mode 100644 index 0000000000..e6d3acf359 --- /dev/null +++ b/maven-mboot2/sources.txt @@ -0,0 +1,19 @@ +src/main/java/ArtifactDownloader.java +src/main/java/Base64.java +src/main/java/FileUtils.java +src/main/java/HttpUtils.java +src/main/java/IsolatedClassLoader.java +src/main/java/JavacCompiler.java +src/main/java/AbstractCompiler.java +src/main/java/CompilerError.java +src/main/java/StreamPumper.java +src/main/java/DirectoryScanner.java +src/main/java/SelectorUtils.java +src/main/java/MBoot.java +src/main/java/SurefirePlugin.java +src/main/java/JarMojo.java +src/main/java/StringUtils.java +src/main/java/IOUtil.java +src/main/java/Commandline.java +src/main/java/SurefireBooter.java +src/main/java/Os.java diff --git a/maven-mboot2/src/main/java/AbstractCompiler.java b/maven-mboot2/src/main/java/AbstractCompiler.java new file mode 100644 index 0000000000..df222b9579 --- /dev/null +++ b/maven-mboot2/src/main/java/AbstractCompiler.java @@ -0,0 +1,124 @@ + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Jason van Zyl + * @author Michal Maczka + * @version $Id$ + */ +public abstract class AbstractCompiler +{ + private static String PS = System.getProperty( "path.separator" ); + + public String getClasspathString( String[] classpathElements ) + throws Exception + { + StringBuffer sb = new StringBuffer(); + + for ( int i = 0; i < classpathElements.length; i++ ) + { + sb.append( classpathElements[i] ).append( PS ); + } + + return sb.toString(); + } + + protected String[] getSourceFiles( String[] sourceDirectories ) + { + List sources = new ArrayList(); + + for ( int i = 0; i < sourceDirectories.length; i++ ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + scanner.setBasedir( sourceDirectories[i] ); + + scanner.setIncludes( new String[]{"**/*.java"} ); + + scanner.scan(); + + String[] sourceDirectorySources = scanner.getIncludedFiles(); + + for ( int j = 0; j < sourceDirectorySources.length; j++ ) + { + File f = new File( sourceDirectories[i], sourceDirectorySources[j] ); + + sources.add( f.getPath() ); + } + } + + String[] sourceArray = new String[sources.size()]; + + return (String[]) sources.toArray( sourceArray ); + } + + protected String makeClassName( String fileName, String sourceDir ) + throws IOException + { + File origFile = new File( fileName ); + String canonical = null; + + if ( origFile.exists() ) + { + canonical = origFile.getCanonicalPath().replace( '\\', '/' ); + } + + String str = fileName; + str = str.replace( '\\', '/' ); + + if ( sourceDir != null ) + { + String prefix = + new File( sourceDir ).getCanonicalPath().replace( '\\', '/' ); + + if ( canonical != null ) + { + if ( canonical.startsWith( prefix ) ) + { + String result = canonical.substring( prefix.length() + 1, canonical.length() - 5 ); + + result = result.replace( '/', '.' ); + + return result; + } + } + else + { + File t = new File( sourceDir, fileName ); + + if ( t.exists() ) + { + str = t.getCanonicalPath().replace( '\\', '/' ); + + String result = str.substring( prefix.length() + 1, str.length() - 5 ).replace( '/', '.' ); + + return result; + } + } + } + + if ( fileName.endsWith( ".java" ) ) + { + fileName = fileName.substring( 0, fileName.length() - 5 ); + } + + fileName = fileName.replace( '\\', '.' ); + + return fileName.replace( '/', '.' ); + } + + protected String[] toStringArray( List arguments ) + { + String[] args = new String[arguments.size()]; + + for ( int i = 0; i < arguments.size(); i++ ) + { + args[i] = (String) arguments.get( i ); + } + + return args; + } +} diff --git a/maven-mboot2/src/main/java/ArtifactDownloader.java b/maven-mboot2/src/main/java/ArtifactDownloader.java new file mode 100644 index 0000000000..808932bb1c --- /dev/null +++ b/maven-mboot2/src/main/java/ArtifactDownloader.java @@ -0,0 +1,235 @@ + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; +import java.util.StringTokenizer; + +public class ArtifactDownloader +{ + public static final String SNAPSHOT_SIGNATURE = "-SNAPSHOT"; + + private File mavenRepoLocal; + + private List remoteRepos; + + private boolean useTimestamp = true; + + private boolean ignoreErrors = true; + + private String proxyHost; + + private String proxyPort; + + private String proxyUserName; + + private String proxyPassword; + + public ArtifactDownloader( Properties properties ) throws Exception + { + setRemoteRepo( properties.getProperty( "maven.repo.remote" ) ); + + String mavenRepoLocalProperty = properties.getProperty( "maven.repo.local" ); + + if ( mavenRepoLocalProperty == null ) + { + mavenRepoLocalProperty = System.getProperty( "user.home" ) + "/.maven/repository"; + } + + mavenRepoLocal = new File( mavenRepoLocalProperty ); + + if ( !mavenRepoLocal.exists() ) + { + if ( !mavenRepoLocal.mkdirs() ) + { + System.err.println( "Cannot create the specified maven.repo.local: " + mavenRepoLocal ); + + System.exit( 1 ); + } + } + + if ( !mavenRepoLocal.canWrite() ) + { + System.err.println( "Can't write to " + mavenRepoLocal.getAbsolutePath() ); + + System.exit( 1 ); + } + + writeFile( "bootstrap.repo", mavenRepoLocal.getPath() ); + + System.out.println( "Using the following for your maven.repo.local: " + mavenRepoLocal ); + } + + private void writeFile( String name, String contents ) + throws Exception + { + Writer writer = new FileWriter( name ); + + writer.write( contents ); + + writer.close(); + } + + public File getMavenRepoLocal() + { + return mavenRepoLocal; + } + + public void downloadDependencies( List files ) + throws Exception + { + for ( Iterator j = files.iterator(); j.hasNext(); ) + { + try + { + String file = (String) j.next(); + + File destinationFile = new File( mavenRepoLocal, file ); + + // The directory structure for this project may + // not exists so create it if missing. + File directory = destinationFile.getParentFile(); + + if ( directory.exists() == false ) + { + directory.mkdirs(); + } + + if ( destinationFile.exists() && file.indexOf( SNAPSHOT_SIGNATURE ) < 0 ) + { + continue; + } + + //log( "Downloading dependency: " + file ); + + getRemoteArtifact( file, destinationFile ); + + if ( !destinationFile.exists() ) + { + throw new Exception( "Failed to download " + file ); + } + } + catch ( Exception e ) + { + throw new Exception( e ); + } + } + } + + private void setRemoteRepo( String repos ) + { + remoteRepos = new ArrayList(); + + if ( repos == null ) + { + remoteRepos.add( "http://www.ibiblio.org/maven/" ); + return; + } + + StringTokenizer st = new StringTokenizer( repos, "," ); + while ( st.hasMoreTokens() ) + { + remoteRepos.add( st.nextToken().trim() ); + } + } + + private List getRemoteRepo() + { + return remoteRepos; + } + + private boolean getRemoteArtifact( String file, File destinationFile ) + { + boolean fileFound = false; + + for ( Iterator i = getRemoteRepo().iterator(); i.hasNext(); ) + { + String remoteRepo = (String) i.next(); + + // The username and password parameters are not being + // used here. Those are the "" parameters you see below. + String url = remoteRepo + "/" + file; + + if ( !url.startsWith( "file" ) ) + { + url = replace( url, "//", "/" ); + if ( url.startsWith( "https" ) ) + { + url = replace( url, "https:/", "https://" ); + } + else + { + url = replace( url, "http:/", "http://" ); + } + } + + // Attempt to retrieve the artifact and set the checksum if retrieval + // of the checksum file was successful. + try + { + HttpUtils.getFile( url, + destinationFile, + ignoreErrors, + useTimestamp, + proxyHost, + proxyPort, + proxyUserName, + proxyPassword, + true ); + + // Artifact was found, continue checking additional remote repos (if any) + // in case there is a newer version (i.e. snapshots) in another repo + fileFound = true; + } + catch ( FileNotFoundException e ) + { + // Ignore + } + catch ( Exception e ) + { + // If there are additional remote repos, then ignore exception + // as artifact may be found in another remote repo. If there + // are no more remote repos to check and the artifact wasn't found in + // a previous remote repo, then artifactFound is false indicating + // that the artifact could not be found in any of the remote repos + // + // arguably, we need to give the user better control (another command- + // line switch perhaps) of what to do in this case? Maven already has + // a command-line switch to work in offline mode, but what about when + // one of two or more remote repos is unavailable? There may be multiple + // remote repos for redundancy, in which case you probably want the build + // to continue. There may however be multiple remote repos because some + // artifacts are on one, and some are on another. In this case, you may + // want the build to break. + // + // print a warning, in any case, so user catches on to mistyped + // hostnames, or other snafus + log( "Error retrieving artifact from [" + url + "]: " ); + } + } + + return fileFound; + } + + private String replace( String text, String repl, String with ) + { + StringBuffer buf = new StringBuffer( text.length() ); + int start = 0, end = 0; + while ( ( end = text.indexOf( repl, start ) ) != -1 ) + { + buf.append( text.substring( start, end ) ).append( with ); + start = end + repl.length(); + } + buf.append( text.substring( start ) ); + return buf.toString(); + } + + private void log( String message ) + { + System.out.println( message ); + } +} \ No newline at end of file diff --git a/maven-mboot2/src/main/java/Base64.java b/maven-mboot2/src/main/java/Base64.java new file mode 100644 index 0000000000..b205480013 --- /dev/null +++ b/maven-mboot2/src/main/java/Base64.java @@ -0,0 +1,386 @@ +/* + * ==================================================================== + * Copyright 2001-2004 The Apache Software Foundation. + * + * 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. + * ==================================================================== + */ + +import java.io.ByteArrayOutputStream; + +// import org.apache.commons.logging.Log; +// import org.apache.commons.logging.LogFactory; + +/** + * Encode/Decode Base-64. + * + * @author John Casey + */ +public final class Base64 +{ + + // private static final Log LOG = LogFactory.getLog( Base64.class ); + + private static final String CRLF = System.getProperty( "line.separator" ); + + private static final int LINE_END = 64; + + public static String encode( byte[] data ) + { + return Base64.encode( data, true ); + } + + public static String encode( byte[] data, boolean useLineDelimiter ) + { + if ( data == null ) + { + return null; + } + else if ( data.length == 0 ) + { + return ""; + } + + int padding = 3 - ( data.length % 3 ); + + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "padding = " + padding + "characters." ); + // } + + StringBuffer buffer = new StringBuffer(); + + for ( int i = 0; i < data.length; i += 3 ) + { + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "iteration base offset = " + i ); + // } + + int neutral = ( data[i] < 0 ? data[i] + 256 : data[i] ); + + int block = ( neutral & 0xff ); + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "after first byte, block = " + Integer.toBinaryString( block ) ); + // } + + boolean inLastSegment = false; + + block <<= 8; + if ( i + 1 < data.length ) + { + neutral = ( data[i + 1] < 0 ? data[i + 1] + 256 : data[i + 1] ); + block |= ( neutral & 0xff ); + } + else + { + inLastSegment = true; + } + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "after second byte, block = " + Integer.toBinaryString( block ) + "; inLastSegment = " + // + inLastSegment ); + // } + + block <<= 8; + if ( i + 2 < data.length ) + { + neutral = ( data[i + 2] < 0 ? data[i + 2] + 256 : data[i + 2] ); + block |= ( neutral & 0xff ); + } + else + { + inLastSegment = true; + } + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "after third byte, block = " + Integer.toBinaryString( block ) + "; inLastSegment = " + // + inLastSegment ); + // } + + char[] encoded = new char[4]; + int encIdx = 0; + encoded[0] = toBase64Char( ( block >>> 18 ) & 0x3f ); + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "first character = " + encoded[0] ); + // } + + encoded[1] = toBase64Char( ( block >>> 12 ) & 0x3f ); + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "second character = " + encoded[1] ); + // } + + if ( inLastSegment && padding > 1 ) + { + encoded[2] = '='; + } + else + { + encoded[2] = toBase64Char( ( block >>> 6 ) & 0x3f ); + } + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "third character = " + encoded[2] ); + // } + + if ( inLastSegment && padding > 0 ) + { + encoded[3] = '='; + } + else + { + encoded[3] = toBase64Char( block & 0x3f ); + } + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "fourth character = " + encoded[3] ); + // } + + buffer.append( encoded ); + } + + if ( useLineDelimiter ) + { + return canonicalize( buffer.toString() ); + } + else + { + return buffer.toString(); + } + } + + public static byte[] decode( String src ) + { + return Base64.decode( src, true ); + } + + public static byte[] decode( String src, boolean useLineDelimiter ) + { + if ( src == null ) + { + return null; + } + else if ( src.length() < 1 ) + { + return new byte[0]; + } + + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "pre-canonicalization = \n" + src ); + // } + String data = src; + + if ( useLineDelimiter ) + { + data = deCanonicalize( src ); + } + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "post-canonicalization = \n" + data ); + // } + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + char[] input = data.toCharArray(); + + int index = 0; + for ( int i = 0; i < input.length; i += 4 ) + { + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "iteration base offset = " + i ); + // } + + int block = ( toBase64Int( input[i] ) & 0x3f ); + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "block after first char [" + input[i] + "] = " + Integer.toBinaryString( block ) ); + // } + + block <<= 6; + block |= ( toBase64Int( input[i + 1] ) & 0x3f ); + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "block after second char [" + input[i + 1] + "] = " + Integer.toBinaryString( block ) ); + // } + + boolean inPadding = false; + boolean twoCharPadding = false; + block <<= 6; + if ( input[i + 2] != '=' ) + { + block |= ( toBase64Int( input[i + 2] ) & 0x3f ); + } + else + { + twoCharPadding = true; + inPadding = true; + } + + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "block after third char [" + input[i + 2] + "] = " + Integer.toBinaryString( block ) ); + // } + + block <<= 6; + if ( input[i + 3] != '=' ) + { + block |= ( toBase64Int( input[i + 3] ) & 0x3f ); + } + else + { + inPadding = true; + } + + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "block after fourth char [" + input[i + 3] + "] = " + Integer.toBinaryString( block ) ); + // } + + baos.write( ( block >>> 16 ) & 0xff ); + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "byte[" + ( index++ ) + "] = " + ( ( block >>> 16 ) & 0xff ) ); + // } + + if ( !inPadding || !twoCharPadding ) + { + baos.write( ( block >>> 8 ) & 0xff ); + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "byte[" + ( index++ ) + "] = " + ( ( block >>> 8 ) & 0xff ) ); + // } + } + + if ( !inPadding ) + { + baos.write( block & 0xff ); + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "byte[" + ( index++ ) + "] = " + ( block & 0xff ) ); + // } + } + } + + byte[] result = baos.toByteArray(); + // if ( LOG.isDebugEnabled() ) + // { + // LOG.debug( "byte array is " + result.length + " bytes long." ); + // } + + return result; + } + + private static char toBase64Char( int input ) + { + if ( input > -1 && input < 26 ) + { + return (char) ( 'A' + input ); + } + else if ( input > 25 && input < 52 ) + { + return (char) ( 'a' + input - 26 ); + } + else if ( input > 51 && input < 62 ) + { + return (char) ( '0' + input - 52 ); + } + else if ( input == 62 ) + { + return '+'; + } + else if ( input == 63 ) + { + return '/'; + } + else + { + return '?'; + } + } + + private static int toBase64Int( char input ) + { + if ( input >= 'A' && input <= 'Z' ) + { + return input - 'A'; + } + else if ( input >= 'a' && input <= 'z' ) + { + return input + 26 - 'a'; + } + else if ( input >= '0' && input <= '9' ) + { + return input + 52 - '0'; + } + else if ( input == '+' ) + { + return 62; + } + else if ( input == '/' ) + { + return 63; + } + else + { + return 0; + } + } + + private static String deCanonicalize( String data ) + { + if ( data == null ) + { + return null; + } + + StringBuffer buffer = new StringBuffer( data.length() ); + for ( int i = 0; i < data.length(); i++ ) + { + char c = data.charAt( i ); + if ( c != '\r' && c != '\n' ) + { + buffer.append( c ); + } + } + + return buffer.toString(); + } + + private static String canonicalize( String data ) + { + StringBuffer buffer = new StringBuffer( (int) ( data.length() * 1.1 ) ); + + int col = 0; + for ( int i = 0; i < data.length(); i++ ) + { + if ( col == LINE_END ) + { + buffer.append( CRLF ); + col = 0; + } + + buffer.append( data.charAt( i ) ); + col++; + } + + buffer.append( CRLF ); + + return buffer.toString(); + } + +} diff --git a/maven-mboot2/src/main/java/Commandline.java b/maven-mboot2/src/main/java/Commandline.java new file mode 100644 index 0000000000..bd21e11461 --- /dev/null +++ b/maven-mboot2/src/main/java/Commandline.java @@ -0,0 +1,606 @@ + +import java.io.File; +import java.io.IOException; +import java.util.Enumeration; +import java.util.StringTokenizer; +import java.util.Vector; + +/** + * Commandline objects help handling command lines specifying processes to + * execute. + * + * The class can be used to define a command line as nested elements or as a + * helper to define a command line by an application. + *
+ *
+ * <someelement>
+ * The element
+ * <acommandline executable="/executable/to/run">
+ * <argument value="argument 1" />
+ * <argument line="argument_1 argument_2 argument_3" />
+ * <argument value="argument 4" />
+ * </acommandline>
+ * </someelement>
+ * someelement
must provide a method
+ * createAcommandline
which returns an instance of this class.
+ *
+ * @author thomas.haas@softwired-inc.com
+ * @author Stefan Bodewig
+ */
+public class Commandline implements Cloneable
+{
+
+ protected static final String OS_NAME = "os.name";
+ protected static final String WINDOWS = "Windows";
+
+ private String shell = null;
+ private Vector shellArgs = new Vector();
+ private String executable = null;
+ private Vector arguments = new Vector();
+ private File workingDir = null;
+
+ public Commandline( String toProcess )
+ {
+ super();
+ setDefaultShell();
+ String[] tmp = new String[0];
+ try
+ {
+ tmp = translateCommandline( toProcess );
+ }
+ catch ( Exception e )
+ {
+ System.err.println( "Error translating Commandline." );
+ }
+ if ( tmp != null && tmp.length > 0 )
+ {
+ setExecutable( tmp[0] );
+ for ( int i = 1; i < tmp.length; i++ )
+ {
+ createArgument().setValue( tmp[i] );
+ }
+ }
+ }
+
+ public Commandline()
+ {
+ super();
+ setDefaultShell();
+ }
+
+ /**
+ * Used for nested xml command line definitions.
+ */
+ public static class Argument
+ {
+
+ private String[] parts;
+
+ /**
+ * Sets a single commandline argument.
+ *
+ * @param value a single commandline argument.
+ */
+ public void setValue( String value )
+ {
+ parts = new String[]{value};
+ }
+
+ /**
+ * Line to split into several commandline arguments.
+ *
+ * @param line line to split into several commandline arguments
+ */
+ public void setLine( String line )
+ {
+ if ( line == null )
+ {
+ return;
+ }
+ try
+ {
+ parts = translateCommandline( line );
+ }
+ catch ( Exception e )
+ {
+ System.err.println( "Error translating Commandline." );
+ }
+ }
+
+ /**
+ * Sets a single commandline argument to the absolute filename
+ * of the given file.
+ *
+ * @param value a single commandline argument.
+ */
+ public void setFile( File value )
+ {
+ parts = new String[]{value.getAbsolutePath()};
+ }
+
+ /**
+ * Returns the parts this Argument consists of.
+ */
+ public String[] getParts()
+ {
+ return parts;
+ }
+ }
+
+ /**
+ * Class to keep track of the position of an Argument.
+ */
+ //
This class is there to support the srcfile and targetfile + // elements of <execon> and <transform> - don't know + // whether there might be additional use cases.
--SB + public class Marker + { + + private int position; + private int realPos = -1; + + Marker( int position ) + { + this.position = position; + } + + /** + * Return the number of arguments that preceeded this marker. + * + *The name of the executable - if set - is counted as the + * very first argument.
+ */ + public int getPosition() + { + if ( realPos == -1 ) + { + realPos = ( executable == null ? 0 : 1 ); + for ( int i = 0; i < position; i++ ) + { + Argument arg = (Argument) arguments.elementAt( i ); + realPos += arg.getParts().length; + } + } + return realPos; + } + } + + + /** + *Sets the shell or command-line interpretor for the detected operating system, + * and the shell arguments.
+ */ + private void setDefaultShell() { + String os = System.getProperty(OS_NAME); + + //If this is windows set the shell to command.com or cmd.exe with correct arguments. + if ( os.indexOf(WINDOWS) != -1 ) + { + if (os.indexOf("95") != -1 || os.indexOf("98") != -1 || os.indexOf("Me") != -1) + { + shell = "COMMAND.COM"; + shellArgs.add("/C"); + } + else + { + shell = "CMD.EXE"; + shellArgs.add("/X"); + shellArgs.add("/C"); + } + } + } + + /** + *Gets the shell or command-line interpretor for the detected operating system, + * and the shell arguments.
+ */ + private String getDefaultShell() + { + if ( shell != null ) + { + String args = ""; + for (Enumeration enums = shellArgs.elements(); enums.hasMoreElements(); ) + { + args += (String)enums.nextElement(); + if (enums.hasMoreElements()) + { + args += " "; + } + } + + if (args.length() > 0) + { + return shell + " " + args; + } + else + { + return shell; + } + } + else + { + return ""; + } + } + + /** + * Creates an argument object. + * + *Each commandline object has at most one instance of the
+ * argument class. This method calls
+ * this.createArgument(false)
.
Each commandline object has at most one instance of the + * argument class.
+ * + * @param insertAtStart if true, the argument is inserted at the + * beginning of the list of args, otherwise it is appended. + */ + public Argument createArgument( boolean insertAtStart ) + { + Argument argument = new Argument(); + if ( insertAtStart ) + { + arguments.insertElementAt( argument, 0 ); + } + else + { + arguments.addElement( argument ); + } + return argument; + } + + /** + * Sets the executable to run. + */ + public void setExecutable( String executable ) + { + if ( executable == null || executable.length() == 0 ) + { + return; + } + this.executable = + executable.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); + } + + public String getExecutable() + { + return executable; + } + + public void addArguments( String[] line ) + { + for ( int i = 0; i < line.length; i++ ) + { + createArgument().setValue( line[i] ); + } + } + + /** + * Returns the executable and all defined arguments. + */ + public String[] getCommandline() + { + final String[] args = getArguments(); + if ( executable == null ) + { + return args; + } + final String[] result = new String[args.length + 1]; + result[0] = executable; + System.arraycopy( args, 0, result, 1, args.length ); + return result; + } + + /** + * Returns the shell, executable and all defined arguments. + */ + public String[] getShellCommandline() + { + int shellCount = 0; + int arrayPos = 0; + if ( shell != null ) + { + shellCount = 1; + } + shellCount += shellArgs.size(); + final String[] args = getArguments(); + + String[] result = new String[shellCount + args.length + (( executable == null )? 0:1)]; + //Build shell and arguments into result + if ( shell != null ) + { + result[0] = shell; + arrayPos++; + } + System.arraycopy( shellArgs.toArray(), 0, result, arrayPos, shellArgs.size() ); + arrayPos += shellArgs.size(); + //Build excutable and arguments into result + if ( executable != null ) + { + result[arrayPos] = executable; + arrayPos++; + } + System.arraycopy( args, 0, result, arrayPos, args.length ); + return result; + } + + /** + * Returns all arguments defined byaddLine
,
+ * addValue
or the argument object.
+ */
+ public String[] getArguments()
+ {
+ Vector result = new Vector( arguments.size() * 2 );
+ for ( int i = 0; i < arguments.size(); i++ )
+ {
+ Argument arg = (Argument) arguments.elementAt( i );
+ String[] s = arg.getParts();
+ if ( s != null )
+ {
+ for ( int j = 0; j < s.length; j++ )
+ {
+ result.addElement( s[j] );
+ }
+ }
+ }
+
+ String[] res = new String[result.size()];
+ result.copyInto( res );
+ return res;
+ }
+
+ public String toString()
+ {
+ return toString( getCommandline() );
+ }
+
+ /**
+ * Put quotes around the given String if necessary.
+ *
+ * If the argument doesn't include spaces or quotes, return it + * as is. If it contains double quotes, use single quotes - else + * surround the argument by double quotes.
+ * + * @exception Exception if the argument contains both, single + * and double quotes. + */ + public static String quoteArgument( String argument ) throws Exception + { + if ( argument.indexOf( "\"" ) > -1 ) + { + if ( argument.indexOf( "\'" ) > -1 ) + { + throw new Exception( "Can't handle single and double quotes in same argument" ); + } + else + { + return '\'' + argument + '\''; + } + } + else if ( argument.indexOf( "\'" ) > -1 || argument.indexOf( " " ) > -1 ) + { + return '\"' + argument + '\"'; + } + else + { + return argument; + } + } + + public static String toString( String[] line ) + { + // empty path return empty string + if ( line == null || line.length == 0 ) + { + return ""; + } + + // path containing one or more elements + final StringBuffer result = new StringBuffer(); + for ( int i = 0; i < line.length; i++ ) + { + if ( i > 0 ) + { + result.append( ' ' ); + } + try + { + result.append( quoteArgument( line[i] ) ); + } + catch ( Exception e ) + { + System.err.println( "Error quoting argument." ); + } + } + return result.toString(); + } + + public static String[] translateCommandline( String toProcess ) throws Exception + { + if ( toProcess == null || toProcess.length() == 0 ) + { + return new String[0]; + } + + // parse with a simple finite state machine + + final int normal = 0; + final int inQuote = 1; + final int inDoubleQuote = 2; + int state = normal; + StringTokenizer tok = new StringTokenizer( toProcess, "\"\' ", true ); + Vector v = new Vector(); + StringBuffer current = new StringBuffer(); + + while ( tok.hasMoreTokens() ) + { + String nextTok = tok.nextToken(); + switch ( state ) + { + case inQuote: + if ( "\'".equals( nextTok ) ) + { + state = normal; + } + else + { + current.append( nextTok ); + } + break; + case inDoubleQuote: + if ( "\"".equals( nextTok ) ) + { + state = normal; + } + else + { + current.append( nextTok ); + } + break; + default : + if ( "\'".equals( nextTok ) ) + { + state = inQuote; + } + else if ( "\"".equals( nextTok ) ) + { + state = inDoubleQuote; + } + else if ( " ".equals( nextTok ) ) + { + if ( current.length() != 0 ) + { + v.addElement( current.toString() ); + current.setLength( 0 ); + } + } + else + { + current.append( nextTok ); + } + break; + } + } + + if ( current.length() != 0 ) + { + v.addElement( current.toString() ); + } + + if ( state == inQuote || state == inDoubleQuote ) + { + throw new Exception( "unbalanced quotes in " + toProcess ); + } + + String[] args = new String[v.size()]; + v.copyInto( args ); + return args; + } + + public int size() + { + return getCommandline().length; + } + + public Object clone() + { + Commandline c = new Commandline(); + c.setExecutable( executable ); + c.addArguments( getArguments() ); + return c; + } + + /** + * Clear out the whole command line. */ + public void clear() + { + executable = null; + arguments.removeAllElements(); + } + + /** + * Clear out the arguments but leave the executable in place for another operation. + */ + public void clearArgs() + { + arguments.removeAllElements(); + } + + /** + * Return a marker. + * + *This marker can be used to locate a position on the + * commandline - to insert something for example - when all + * parameters have been set.
+ */ + public Marker createMarker() + { + return new Marker( arguments.size() ); + } + + /** + * Sets execution directory. + */ + public void setWorkingDirectory( String path ) + { + if ( path != null ) + { + workingDir = new File( path ); + } + } + + public File getWorkingDirectory() + { + return workingDir; + } + + /** + * Executes the command. + */ + public Process execute() + throws IOException + { + Process process = null; + + if ( workingDir == null ) + { + System.err.println( "Executing \"" + this + "\"" ); + process = Runtime.getRuntime().exec( getShellCommandline() ); + } + else + { + if ( !workingDir.exists() ) + { + throw new IOException( + "Working directory \"" + workingDir.getPath() + "\" does not exist!" ); + } + else if ( !workingDir.isDirectory() ) + { + throw new IOException( + "Path \"" + workingDir.getPath() + "\" does not specify a directory." ); + } + + System.err.println( + "Executing \"" + + this + + "\" in directory " + + ( workingDir != null ? workingDir.getAbsolutePath() : null ) ); + process = Runtime.getRuntime().exec( getShellCommandline(), null, workingDir ); + } + + return process; + } + +} \ No newline at end of file diff --git a/maven-mboot2/src/main/java/CompilerError.java b/maven-mboot2/src/main/java/CompilerError.java new file mode 100644 index 0000000000..3d534e2a41 --- /dev/null +++ b/maven-mboot2/src/main/java/CompilerError.java @@ -0,0 +1,158 @@ + +/** + * This class encapsulates an error message produced by a programming language + * processor (whether interpreted or compiled) + * + * @author Stefano Mazzocchi + * @version CVS $Id$ + * @since 2.0 + */ + +public class CompilerError +{ + /** + * Is this a severe error or a warning? + */ + private boolean error; + /** + * The start line number of the offending program text + */ + private int startline; + /** + * The start column number of the offending program text + */ + private int startcolumn; + /** + * The end line number of the offending program text + */ + private int endline; + /** + * The end column number of the offending program text + */ + private int endcolumn; + /** + * The name of the file containing the offending program text + */ + private String file; + /** + * The actual error text produced by the language processor + */ + private String message; + + /** + * The error message constructor. + * + * @param file The name of the file containing the offending program text + * @param error The actual error text produced by the language processor + * @param startline The start line number of the offending program text + * @param startcolumn The start column number of the offending program text + * @param endline The end line number of the offending program text + * @param endcolumn The end column number of the offending program text + * @param message The actual error text produced by the language processor + */ + public CompilerError( String file, + boolean error, + int startline, + int startcolumn, + int endline, + int endcolumn, + String message ) + { + this.file = file; + this.error = error; + this.startline = startline; + this.startcolumn = startcolumn; + this.endline = endline; + this.endcolumn = endcolumn; + this.message = message; + } + + /** + * The error message constructor. + * + * @param message The actual error text produced by the language processor + */ + public CompilerError( String message ) + { + this.message = message; + } + + /** + * Return the filename associated with this compiler error. + * + * @return The filename associated with this compiler error + */ + public String getFile() + { + return file; + } + + /** + * Assert whether this is a severe error or a warning + * + * @return Whether the error is severe + */ + public boolean isError() + { + return error; + } + + /** + * Return the starting line number of the program text originating this error + * + * @return The starting line number of the program text originating this error + */ + public int getStartLine() + { + return startline; + } + + /** + * Return the starting column number of the program text originating this + * error + * + * @return The starting column number of the program text originating this + * error + */ + public int getStartColumn() + { + return startcolumn; + } + + /** + * Return the ending line number of the program text originating this error + * + * @return The ending line number of the program text originating this error + */ + public int getEndLine() + { + return endline; + } + + /** + * Return the ending column number of the program text originating this + * error + * + * @return The ending column number of the program text originating this + * error + */ + public int getEndColumn() + { + return endcolumn; + } + + /** + * Return the message produced by the language processor + * + * @return The message produced by the language processor + */ + public String getMessage() + { + return message; + } + + public String toString() + { + return file + ":" + "[" + startline + "," + startcolumn + "] " + message; + } +} diff --git a/maven-mboot2/src/main/java/DirectoryScanner.java b/maven-mboot2/src/main/java/DirectoryScanner.java new file mode 100644 index 0000000000..4c2c899b8c --- /dev/null +++ b/maven-mboot2/src/main/java/DirectoryScanner.java @@ -0,0 +1,993 @@ + +import java.io.File; +import java.io.IOException; +import java.util.Vector; + +/** + * Class for scanning a directory for files/directories which match certain + * criteria. + * + * These criteria consist of selectors and patterns which have been specified. + * With the selectors you can select which files you want to have included. + * Files which are not selected are excluded. With patterns you can include + * or exclude files based on their filename. + * + * The idea is simple. A given directory is recursively scanned for all files + * and directories. Each file/directory is matched against a set of selectors, + * including special support for matching against filenames with include and + * and exclude patterns. Only files/directories which match at least one + * pattern of the include pattern list or other file selector, and don't match + * any pattern of the exclude pattern list or fail to match against a required + * selector will be placed in the list of files/directories found. + * + * When no list of include patterns is supplied, "**" will be used, which + * means that everything will be matched. When no list of exclude patterns is + * supplied, an empty list is used, such that nothing will be excluded. When + * no selectors are supplied, none are applied. + * + * The filename pattern matching is done as follows: + * The name to be matched is split up in path segments. A path segment is the + * name of a directory or file, which is bounded by + *File.separator
('/' under UNIX, '\' under Windows).
+ * For example, "abc/def/ghi/xyz.java" is split up in the segments "abc",
+ * "def","ghi" and "xyz.java".
+ * The same is done for the pattern against which should be matched.
+ *
+ * The segments of the name and the pattern are then matched against each
+ * other. When '**' is used for a path segment in the pattern, it matches
+ * zero or more path segments of the name.
+ *
+ * There is a special case regarding the use of File.separator
s
+ * at the beginning of the pattern and the string to match:File.separator
, the string
+ * to match must also start with a File.separator
.
+ * When a pattern does not start with a File.separator
, the
+ * string to match may not start with a File.separator
.
+ * When one of these rules is not obeyed, the string will not
+ * match.
+ *
+ * When a name path segment is matched against a pattern path segment, the
+ * following special characters can be used:+ * String[] includes = {"**\\*.class"}; + * String[] excludes = {"modules\\*\\**"}; + * ds.setIncludes(includes); + * ds.setExcludes(excludes); + * ds.setBasedir(new File("test")); + * ds.setCaseSensitive(true); + * ds.scan(); + * + * System.out.println("FILES:"); + * String[] files = ds.getIncludedFiles(); + * for (int i = 0; i < files.length; i++) { + * System.out.println(files[i]); + * } + *+ * This will scan a directory called test for .class files, but excludes all + * files in all proper subdirectories of a directory called "modules" + * + * @author Arnout J. Kuiper + * ajkuiper@wxs.nl + * @author Magesh Umasankar + * @author Bruce Atherton + * @author Antoine Levy-Lambert + */ +public class DirectoryScanner +{ + /** + * Patterns which should be excluded by default. + * + * @see #addDefaultExcludes() + */ + protected static final String[] DEFAULTEXCLUDES = { + // Miscellaneous typical temporary files + "**/*~", + "**/#*#", + "**/.#*", + "**/%*%", + "**/._*", + + // CVS + "**/CVS", + "**/CVS/**", + "**/.cvsignore", + + // SCCS + "**/SCCS", + "**/SCCS/**", + + // Visual SourceSafe + "**/vssver.scc", + + // Subversion + "**/.svn", + "**/.svn/**", + + // Mac + "**/.DS_Store" + }; + + /** + * The base directory to be scanned. + */ + protected File basedir; + + /** + * The patterns for the files to be included. + */ + protected String[] includes; + + /** + * The patterns for the files to be excluded. + */ + protected String[] excludes; + + /** + * The files which matched at least one include and no excludes + * and were selected. + */ + protected Vector filesIncluded; + + /** + * The files which did not match any includes or selectors. + */ + protected Vector filesNotIncluded; + + /** + * The files which matched at least one include and at least + * one exclude. + */ + protected Vector filesExcluded; + + /** + * The directories which matched at least one include and no excludes + * and were selected. + */ + protected Vector dirsIncluded; + + /** + * The directories which were found and did not match any includes. + */ + protected Vector dirsNotIncluded; + + /** + * The directories which matched at least one include and at least one + * exclude. + */ + protected Vector dirsExcluded; + + /** + * The files which matched at least one include and no excludes and + * which a selector discarded. + */ + protected Vector filesDeselected; + + /** + * The directories which matched at least one include and no excludes + * but which a selector discarded. + */ + protected Vector dirsDeselected; + + /** + * Whether or not our results were built by a slow scan. + */ + protected boolean haveSlowResults = false; + + /** + * Whether or not the file system should be treated as a case sensitive + * one. + */ + protected boolean isCaseSensitive = true; + + /** + * Whether or not symbolic links should be followed. + * + * @since Ant 1.5 + */ + private boolean followSymlinks = true; + + /** + * Whether or not everything tested so far has been included. + */ + protected boolean everythingIncluded = true; + + /** + * Sole constructor. + */ + public DirectoryScanner() + { + } + + /** + * Tests whether or not a given path matches the start of a given + * pattern up to the first "**". + * + * This is not a general purpose test and should only be used if you + * can live with false positives. For example,
pattern=**\a
+ * and str=b
will yield true
.
+ *
+ * @param pattern The pattern to match against. Must not be
+ * null
.
+ * @param str The path to match, as a String. Must not be
+ * null
.
+ * @return whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ */
+ protected static boolean matchPatternStart( String pattern, String str )
+ {
+ return SelectorUtils.matchPatternStart( pattern, str );
+ }
+
+ /**
+ * Tests whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ *
+ * This is not a general purpose test and should only be used if you
+ * can live with false positives. For example, pattern=**\a
+ * and str=b
will yield true
.
+ *
+ * @param pattern The pattern to match against. Must not be
+ * null
.
+ * @param str The path to match, as a String. Must not be
+ * null
.
+ * @param isCaseSensitive Whether or not matching should be performed
+ * case sensitively.
+ * @return whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ */
+ protected static boolean matchPatternStart( String pattern, String str,
+ boolean isCaseSensitive )
+ {
+ return SelectorUtils.matchPatternStart( pattern, str, isCaseSensitive );
+ }
+
+ /**
+ * Tests whether or not a given path matches a given pattern.
+ *
+ * @param pattern The pattern to match against. Must not be
+ * null
.
+ * @param str The path to match, as a String. Must not be
+ * null
.
+ * @return true
if the pattern matches against the string,
+ * or false
otherwise.
+ */
+ protected static boolean matchPath( String pattern, String str )
+ {
+ return SelectorUtils.matchPath( pattern, str );
+ }
+
+ /**
+ * Tests whether or not a given path matches a given pattern.
+ *
+ * @param pattern The pattern to match against. Must not be
+ * null
.
+ * @param str The path to match, as a String. Must not be
+ * null
.
+ * @param isCaseSensitive Whether or not matching should be performed
+ * case sensitively.
+ * @return true
if the pattern matches against the string,
+ * or false
otherwise.
+ */
+ protected static boolean matchPath( String pattern, String str,
+ boolean isCaseSensitive )
+ {
+ return SelectorUtils.matchPath( pattern, str, isCaseSensitive );
+ }
+
+ /**
+ * Tests whether or not a string matches against a pattern.
+ * The pattern may contain two special characters:null
.
+ * @param str The string which must be matched against the pattern.
+ * Must not be null
.
+ * @return true
if the string matches against the pattern,
+ * or false
otherwise.
+ */
+ public static boolean match( String pattern, String str )
+ {
+ return SelectorUtils.match( pattern, str );
+ }
+
+ /**
+ * Tests whether or not a string matches against a pattern.
+ * The pattern may contain two special characters:null
.
+ * @param str The string which must be matched against the pattern.
+ * Must not be null
.
+ * @param isCaseSensitive Whether or not matching should be performed
+ * case sensitively.
+ * @return true
if the string matches against the pattern,
+ * or false
otherwise.
+ */
+ protected static boolean match( String pattern, String str,
+ boolean isCaseSensitive )
+ {
+ return SelectorUtils.match( pattern, str, isCaseSensitive );
+ }
+
+ /**
+ * Sets the base directory to be scanned. This is the directory which is
+ * scanned recursively. All '/' and '\' characters are replaced by
+ * File.separatorChar
, so the separator used need not match
+ * File.separatorChar
.
+ *
+ * @param basedir The base directory to scan.
+ * Must not be null
.
+ */
+ public void setBasedir( String basedir )
+ {
+ setBasedir( new File( basedir.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ) ) );
+ }
+
+ /**
+ * Sets the base directory to be scanned. This is the directory which is
+ * scanned recursively.
+ *
+ * @param basedir The base directory for scanning.
+ * Should not be null
.
+ */
+ public void setBasedir( File basedir )
+ {
+ this.basedir = basedir;
+ }
+
+ /**
+ * Returns the base directory to be scanned.
+ * This is the directory which is scanned recursively.
+ *
+ * @return the base directory to be scanned
+ */
+ public File getBasedir()
+ {
+ return basedir;
+ }
+
+ /**
+ * Sets whether or not the file system should be regarded as case sensitive.
+ *
+ * @param isCaseSensitive whether or not the file system should be
+ * regarded as a case sensitive one
+ */
+ public void setCaseSensitive( boolean isCaseSensitive )
+ {
+ this.isCaseSensitive = isCaseSensitive;
+ }
+
+ /**
+ * Sets whether or not symbolic links should be followed.
+ *
+ * @param followSymlinks whether or not symbolic links should be followed
+ */
+ public void setFollowSymlinks( boolean followSymlinks )
+ {
+ this.followSymlinks = followSymlinks;
+ }
+
+ /**
+ * Sets the list of include patterns to use. All '/' and '\' characters
+ * are replaced by File.separatorChar
, so the separator used
+ * need not match File.separatorChar
.
+ *
+ * When a pattern ends with a '/' or '\', "**" is appended.
+ *
+ * @param includes A list of include patterns.
+ * May be null
, indicating that all files
+ * should be included. If a non-null
+ * list is given, all elements must be
+ * non-null
.
+ */
+ public void setIncludes( String[] includes )
+ {
+ if ( includes == null )
+ {
+ this.includes = null;
+ }
+ else
+ {
+ this.includes = new String[includes.length];
+ for ( int i = 0; i < includes.length; i++ )
+ {
+ String pattern;
+ pattern = includes[i].replace( '/', File.separatorChar ).replace( '\\', File.separatorChar );
+ if ( pattern.endsWith( File.separator ) )
+ {
+ pattern += "**";
+ }
+ this.includes[i] = pattern;
+ }
+ }
+ }
+
+
+ /**
+ * Sets the list of exclude patterns to use. All '/' and '\' characters
+ * are replaced by File.separatorChar
, so the separator used
+ * need not match File.separatorChar
.
+ *
+ * When a pattern ends with a '/' or '\', "**" is appended.
+ *
+ * @param excludes A list of exclude patterns.
+ * May be null
, indicating that no files
+ * should be excluded. If a non-null
list is
+ * given, all elements must be non-null
.
+ */
+ public void setExcludes( String[] excludes )
+ {
+ if ( excludes == null )
+ {
+ this.excludes = null;
+ }
+ else
+ {
+ this.excludes = new String[excludes.length];
+ for ( int i = 0; i < excludes.length; i++ )
+ {
+ String pattern;
+ pattern = excludes[i].replace( '/', File.separatorChar ).replace( '\\', File.separatorChar );
+ if ( pattern.endsWith( File.separator ) )
+ {
+ pattern += "**";
+ }
+ this.excludes[i] = pattern;
+ }
+ }
+ }
+
+ /**
+ * Returns whether or not the scanner has included all the files or
+ * directories it has come across so far.
+ *
+ * @return true
if all files and directories which have
+ * been found so far have been included.
+ */
+ public boolean isEverythingIncluded()
+ {
+ return everythingIncluded;
+ }
+
+ /**
+ * Scans the base directory for files which match at least one include
+ * pattern and don't match any exclude patterns. If there are selectors
+ * then the files must pass muster there, as well.
+ *
+ * @throws IllegalStateException if the base directory was set
+ * incorrectly (i.e. if it is null
, doesn't exist,
+ * or isn't a directory).
+ */
+ public void scan() throws IllegalStateException
+ {
+ if ( basedir == null )
+ {
+ throw new IllegalStateException( "No basedir set" );
+ }
+ if ( !basedir.exists() )
+ {
+ throw new IllegalStateException( "basedir " + basedir
+ + " does not exist" );
+ }
+ if ( !basedir.isDirectory() )
+ {
+ throw new IllegalStateException( "basedir " + basedir
+ + " is not a directory" );
+ }
+
+ if ( includes == null )
+ {
+ // No includes supplied, so set it to 'matches all'
+ includes = new String[1];
+ includes[0] = "**";
+ }
+ if ( excludes == null )
+ {
+ excludes = new String[0];
+ }
+
+ filesIncluded = new Vector();
+ filesNotIncluded = new Vector();
+ filesExcluded = new Vector();
+ filesDeselected = new Vector();
+ dirsIncluded = new Vector();
+ dirsNotIncluded = new Vector();
+ dirsExcluded = new Vector();
+ dirsDeselected = new Vector();
+
+ if ( isIncluded( "" ) )
+ {
+ if ( !isExcluded( "" ) )
+ {
+ if ( isSelected( "", basedir ) )
+ {
+ dirsIncluded.addElement( "" );
+ }
+ else
+ {
+ dirsDeselected.addElement( "" );
+ }
+ }
+ else
+ {
+ dirsExcluded.addElement( "" );
+ }
+ }
+ else
+ {
+ dirsNotIncluded.addElement( "" );
+ }
+ scandir( basedir, "", true );
+ }
+
+ /**
+ * Top level invocation for a slow scan. A slow scan builds up a full
+ * list of excluded/included files/directories, whereas a fast scan
+ * will only have full results for included files, as it ignores
+ * directories which can't possibly hold any included files/directories.
+ *
+ * Returns immediately if a slow scan has already been completed.
+ */
+ protected void slowScan()
+ {
+ if ( haveSlowResults )
+ {
+ return;
+ }
+
+ String[] excl = new String[dirsExcluded.size()];
+ dirsExcluded.copyInto( excl );
+
+ String[] notIncl = new String[dirsNotIncluded.size()];
+ dirsNotIncluded.copyInto( notIncl );
+
+ for ( int i = 0; i < excl.length; i++ )
+ {
+ if ( !couldHoldIncluded( excl[i] ) )
+ {
+ scandir( new File( basedir, excl[i] ),
+ excl[i] + File.separator, false );
+ }
+ }
+
+ for ( int i = 0; i < notIncl.length; i++ )
+ {
+ if ( !couldHoldIncluded( notIncl[i] ) )
+ {
+ scandir( new File( basedir, notIncl[i] ),
+ notIncl[i] + File.separator, false );
+ }
+ }
+
+ haveSlowResults = true;
+ }
+
+ /**
+ * Scans the given directory for files and directories. Found files and
+ * directories are placed in their respective collections, based on the
+ * matching of includes, excludes, and the selectors. When a directory
+ * is found, it is scanned recursively.
+ *
+ * @param dir The directory to scan. Must not be null
.
+ * @param vpath The path relative to the base directory (needed to
+ * prevent problems with an absolute path when using
+ * dir). Must not be null
.
+ * @param fast Whether or not this call is part of a fast scan.
+ * @see #filesIncluded
+ * @see #filesNotIncluded
+ * @see #filesExcluded
+ * @see #dirsIncluded
+ * @see #dirsNotIncluded
+ * @see #dirsExcluded
+ * @see #slowScan
+ */
+ protected void scandir( File dir, String vpath, boolean fast )
+ {
+ String[] newfiles = dir.list();
+
+ if ( newfiles == null )
+ {
+ /*
+ * two reasons are mentioned in the API docs for File.list
+ * (1) dir is not a directory. This is impossible as
+ * we wouldn't get here in this case.
+ * (2) an IO error occurred (why doesn't it throw an exception
+ * then???)
+ */
+ //throw new Exception( "IO error scanning directory " + dir.getAbsolutePath() );
+ }
+
+ if ( !followSymlinks )
+ {
+ Vector noLinks = new Vector();
+ for ( int i = 0; i < newfiles.length; i++ )
+ {
+ try
+ {
+ if ( isSymbolicLink( dir, newfiles[i] ) )
+ {
+ String name = vpath + newfiles[i];
+ File file = new File( dir, newfiles[i] );
+ if ( file.isDirectory() )
+ {
+ dirsExcluded.addElement( name );
+ }
+ else
+ {
+ filesExcluded.addElement( name );
+ }
+ }
+ else
+ {
+ noLinks.addElement( newfiles[i] );
+ }
+ }
+ catch ( IOException ioe )
+ {
+ String msg = "IOException caught while checking "
+ + "for links, couldn't get cannonical path!";
+ // will be caught and redirected to Ant's logging system
+ System.err.println( msg );
+ noLinks.addElement( newfiles[i] );
+ }
+ }
+ newfiles = new String[noLinks.size()];
+ noLinks.copyInto( newfiles );
+ }
+
+ for ( int i = 0; i < newfiles.length; i++ )
+ {
+ String name = vpath + newfiles[i];
+ File file = new File( dir, newfiles[i] );
+ if ( file.isDirectory() )
+ {
+ if ( isIncluded( name ) )
+ {
+ if ( !isExcluded( name ) )
+ {
+ if ( isSelected( name, file ) )
+ {
+ dirsIncluded.addElement( name );
+ if ( fast )
+ {
+ scandir( file, name + File.separator, fast );
+ }
+ }
+ else
+ {
+ everythingIncluded = false;
+ dirsDeselected.addElement( name );
+ if ( fast && couldHoldIncluded( name ) )
+ {
+ scandir( file, name + File.separator, fast );
+ }
+ }
+
+ }
+ else
+ {
+ everythingIncluded = false;
+ dirsExcluded.addElement( name );
+ if ( fast && couldHoldIncluded( name ) )
+ {
+ scandir( file, name + File.separator, fast );
+ }
+ }
+ }
+ else
+ {
+ everythingIncluded = false;
+ dirsNotIncluded.addElement( name );
+ if ( fast && couldHoldIncluded( name ) )
+ {
+ scandir( file, name + File.separator, fast );
+ }
+ }
+ if ( !fast )
+ {
+ scandir( file, name + File.separator, fast );
+ }
+ }
+ else if ( file.isFile() )
+ {
+ if ( isIncluded( name ) )
+ {
+ if ( !isExcluded( name ) )
+ {
+ if ( isSelected( name, file ) )
+ {
+ filesIncluded.addElement( name );
+ }
+ else
+ {
+ everythingIncluded = false;
+ filesDeselected.addElement( name );
+ }
+ }
+ else
+ {
+ everythingIncluded = false;
+ filesExcluded.addElement( name );
+ }
+ }
+ else
+ {
+ everythingIncluded = false;
+ filesNotIncluded.addElement( name );
+ }
+ }
+ }
+ }
+
+ /**
+ * Tests whether or not a name matches against at least one include
+ * pattern.
+ *
+ * @param name The name to match. Must not be null
.
+ * @return true
when the name matches against at least one
+ * include pattern, or false
otherwise.
+ */
+ protected boolean isIncluded( String name )
+ {
+ for ( int i = 0; i < includes.length; i++ )
+ {
+ if ( matchPath( includes[i], name, isCaseSensitive ) )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tests whether or not a name matches the start of at least one include
+ * pattern.
+ *
+ * @param name The name to match. Must not be null
.
+ * @return true
when the name matches against the start of at
+ * least one include pattern, or false
otherwise.
+ */
+ protected boolean couldHoldIncluded( String name )
+ {
+ for ( int i = 0; i < includes.length; i++ )
+ {
+ if ( matchPatternStart( includes[i], name, isCaseSensitive ) )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tests whether or not a name matches against at least one exclude
+ * pattern.
+ *
+ * @param name The name to match. Must not be null
.
+ * @return true
when the name matches against at least one
+ * exclude pattern, or false
otherwise.
+ */
+ protected boolean isExcluded( String name )
+ {
+ for ( int i = 0; i < excludes.length; i++ )
+ {
+ if ( matchPath( excludes[i], name, isCaseSensitive ) )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tests whether a name should be selected.
+ *
+ * @param name the filename to check for selecting
+ * @param file the java.io.File object for this filename
+ * @return false
when the selectors says that the file
+ * should not be selected, true
otherwise.
+ */
+ protected boolean isSelected( String name, File file )
+ {
+ return true;
+ }
+
+ /**
+ * Returns the names of the files which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ * The names are relative to the base directory.
+ *
+ * @return the names of the files which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ */
+ public String[] getIncludedFiles()
+ {
+ String[] files = new String[filesIncluded.size()];
+ filesIncluded.copyInto( files );
+ return files;
+ }
+
+ /**
+ * Returns the names of the files which matched none of the include
+ * patterns. The names are relative to the base directory. This involves
+ * performing a slow scan if one has not already been completed.
+ *
+ * @return the names of the files which matched none of the include
+ * patterns.
+ * @see #slowScan
+ */
+ public String[] getNotIncludedFiles()
+ {
+ slowScan();
+ String[] files = new String[filesNotIncluded.size()];
+ filesNotIncluded.copyInto( files );
+ return files;
+ }
+
+ /**
+ * Returns the names of the files which matched at least one of the
+ * include patterns and at least one of the exclude patterns.
+ * The names are relative to the base directory. This involves
+ * performing a slow scan if one has not already been completed.
+ *
+ * @return the names of the files which matched at least one of the
+ * include patterns and at at least one of the exclude patterns.
+ * @see #slowScan
+ */
+ public String[] getExcludedFiles()
+ {
+ slowScan();
+ String[] files = new String[filesExcluded.size()];
+ filesExcluded.copyInto( files );
+ return files;
+ }
+
+ /**
+ * Returns the names of the files which were selected out and + * therefore not ultimately included.
+ * + *The names are relative to the base directory. This involves + * performing a slow scan if one has not already been completed.
+ * + * @return the names of the files which were deselected. + * @see #slowScan + */ + public String[] getDeselectedFiles() + { + slowScan(); + String[] files = new String[filesDeselected.size()]; + filesDeselected.copyInto( files ); + return files; + } + + /** + * Returns the names of the directories which matched at least one of the + * include patterns and none of the exclude patterns. + * The names are relative to the base directory. + * + * @return the names of the directories which matched at least one of the + * include patterns and none of the exclude patterns. + */ + public String[] getIncludedDirectories() + { + String[] directories = new String[dirsIncluded.size()]; + dirsIncluded.copyInto( directories ); + return directories; + } + + /** + * Returns the names of the directories which matched none of the include + * patterns. The names are relative to the base directory. This involves + * performing a slow scan if one has not already been completed. + * + * @return the names of the directories which matched none of the include + * patterns. + * @see #slowScan + */ + public String[] getNotIncludedDirectories() + { + slowScan(); + String[] directories = new String[dirsNotIncluded.size()]; + dirsNotIncluded.copyInto( directories ); + return directories; + } + + /** + * Returns the names of the directories which matched at least one of the + * include patterns and at least one of the exclude patterns. + * The names are relative to the base directory. This involves + * performing a slow scan if one has not already been completed. + * + * @return the names of the directories which matched at least one of the + * include patterns and at least one of the exclude patterns. + * @see #slowScan + */ + public String[] getExcludedDirectories() + { + slowScan(); + String[] directories = new String[dirsExcluded.size()]; + dirsExcluded.copyInto( directories ); + return directories; + } + + /** + *Returns the names of the directories which were selected out and + * therefore not ultimately included.
+ * + *The names are relative to the base directory. This involves + * performing a slow scan if one has not already been completed.
+ * + * @return the names of the directories which were deselected. + * @see #slowScan + */ + public String[] getDeselectedDirectories() + { + slowScan(); + String[] directories = new String[dirsDeselected.size()]; + dirsDeselected.copyInto( directories ); + return directories; + } + + /** + * Adds default exclusions to the current exclusions set. + */ + public void addDefaultExcludes() + { + int excludesLength = excludes == null ? 0 : excludes.length; + String[] newExcludes; + newExcludes = new String[excludesLength + DEFAULTEXCLUDES.length]; + if ( excludesLength > 0 ) + { + System.arraycopy( excludes, 0, newExcludes, 0, excludesLength ); + } + for ( int i = 0; i < DEFAULTEXCLUDES.length; i++ ) + { + newExcludes[i + excludesLength] = DEFAULTEXCLUDES[i].replace( '/', + File.separatorChar ).replace( '\\', File.separatorChar ); + } + excludes = newExcludes; + } + + /** + * Checks whether a given file is a symbolic link. + * + *It doesn't really test for symbolic links but whether the + * canonical and absolute paths of the file are identical - this + * may lead to false positives on some platforms.
+ * + * @param parent the parent directory of the file to test + * @param name the name of the file to test. + * @since Ant 1.5 + */ + public boolean isSymbolicLink( File parent, String name ) + throws IOException + { + File resolvedParent = new File( parent.getCanonicalPath() ); + File toTest = new File( resolvedParent, name ); + return !toTest.getAbsolutePath().equals( toTest.getCanonicalPath() ); + } + +} diff --git a/maven-mboot2/src/main/java/FileUtils.java b/maven-mboot2/src/main/java/FileUtils.java new file mode 100644 index 0000000000..d755b33389 --- /dev/null +++ b/maven-mboot2/src/main/java/FileUtils.java @@ -0,0 +1,1234 @@ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; + +/** + * This class provides basic facilities for manipulating files and file paths. + * + *Methods exist to retrieve the components of a typical file path. For example
+ * /www/hosted/mysite/index.html
, can be broken into:
+ *
/www/hosted/mysite/
-- retrievable through {@link #getPath}index.html
-- retrievable through {@link #removePath}/www/hosted/mysite/index
-- retrievable through {@link #removeExtension}html
-- retrievable through {@link #getExtension}File
manager.
+ */
+ public static File getFile( String fileName )
+ {
+ return new File( fileName );
+ }
+
+ /**
+ * Given a directory and an array of extensions return an array of compliant files.
+ *
+ * TODO Should an ignore list be passed in?
+ * TODO Should a recurse flag be passed in?
+ *
+ * The given extensions should be like "java" and not like ".java"
+ */
+ public static String[] getFilesFromExtension( String directory, String[] extensions )
+ {
+
+ Vector files = new Vector();
+
+ java.io.File currentDir = new java.io.File( directory );
+
+ String[] unknownFiles = currentDir.list();
+
+ if ( unknownFiles == null )
+ {
+ return new String[0];
+ }
+
+ for ( int i = 0; i < unknownFiles.length; ++i )
+ {
+ String currentFileName = directory + System.getProperty( "file.separator" ) + unknownFiles[i];
+ java.io.File currentFile = new java.io.File( currentFileName );
+
+ if ( currentFile.isDirectory() )
+ {
+
+
+ //ignore all CVS directories...
+ if ( currentFile.getName().equals( "CVS" ) )
+ {
+ continue;
+ }
+
+
+ //ok... transverse into this directory and get all the files... then combine
+ //them with the current list.
+
+ String[] fetchFiles = getFilesFromExtension( currentFileName, extensions );
+ files = blendFilesToVector( files, fetchFiles );
+
+ }
+ else
+ {
+ //ok... add the file
+
+ String add = currentFile.getAbsolutePath();
+ if ( isValidFile( add, extensions ) )
+ {
+ files.addElement( add );
+
+ }
+
+ }
+ }
+
+ //ok... move the Vector into the files list...
+
+ String[] foundFiles = new String[files.size()];
+ files.copyInto( foundFiles );
+
+ return foundFiles;
+
+ }
+
+
+ /**
+ * Private hepler method for getFilesFromExtension()
+ */
+ private static Vector blendFilesToVector( Vector v, String[] files )
+ {
+
+ for ( int i = 0; i < files.length; ++i )
+ {
+ v.addElement( files[i] );
+ }
+
+ return v;
+ }
+
+ /**
+ * Checks to see if a file is of a particular type(s).
+ * Note that if the file does not have an extension, an empty string
+ * ("") is matched for.
+ */
+ private static boolean isValidFile( String file, String[] extensions )
+ {
+
+
+ String extension = extension( file );
+ if ( extension == null )
+ {
+ extension = "";
+ }
+
+ //ok.. now that we have the "extension" go through the current know
+ //excepted extensions and determine if this one is OK.
+
+ for ( int i = 0; i < extensions.length; ++i )
+ {
+ if ( extensions[i].equals( extension ) )
+ return true;
+ }
+
+ return false;
+
+ }
+
+ /**
+ * Simple way to make a directory
+ */
+ public static void mkdir( String dir )
+ {
+ File file = new File( dir );
+ if ( !file.exists() )
+ {
+ file.mkdirs();
+ }
+ }
+
+ /**
+ * Convert from a URL
to a File
.
+ *
+ * @param url File URL.
+ * @return The equivalent File
object, or null
if the URL's protocol
+ * is not file
+ */
+ public static File toFile( final URL url )
+ {
+ if ( url.getProtocol().equals( "file" ) == false )
+ {
+ return null;
+ }
+ else
+ {
+ final String filename = url.getFile().replace( '/', File.separatorChar );
+ return new File( filename );
+ }
+ }
+
+ /**
+ * Convert the array of Files into a list of URLs.
+ *
+ * @param files the array of files
+ * @return the array of URLs
+ * @throws IOException if an error occurs
+ */
+ public static URL[] toURLs( final File[] files )
+ throws IOException
+ {
+ final URL[] urls = new URL[files.length];
+
+ for ( int i = 0; i < urls.length; i++ )
+ {
+ urls[i] = files[i].toURL();
+ }
+
+ return urls;
+ }
+
+ /**
+ * Remove extension from filename.
+ * ie
+ * + * foo.txt --> foo + * a\b\c.jpg --> a\b\c + * a\b\c --> a\b\c + *+ * + * @param filename the filename + * @return the filename minus extension + */ + public static String removeExtension( final String filename ) + { + final int index = filename.lastIndexOf( '.' ); + + if ( -1 == index ) + { + return filename; + } + else + { + return filename.substring( 0, index ); + } + } + + /** + * Get extension from filename. + * ie + *
+ * foo.txt --> "txt" + * a\b\c.jpg --> "jpg" + * a\b\c --> "" + *+ * + * @param filename the filename + * @return the extension of filename or "" if none + */ + public static String getExtension( final String filename ) + { + final int index = filename.lastIndexOf( '.' ); + + if ( -1 == index ) + { + return ""; + } + else + { + return filename.substring( index + 1 ); + } + } + + /** + * Remove path from filename. Equivalent to the unix command
basename
+ * ie.
+ * + * a/b/c.txt --> c.txt + * a.txt --> a.txt + *+ * + * @param filepath the filepath + * @return the filename minus path + */ + public static String removePath( final String filepath ) + { + return removePath( filepath, File.separatorChar ); + } + + /** + * Remove path from filename. + * ie. + *
+ * a/b/c.txt --> c.txt + * a.txt --> a.txt + *+ * + * @param filepath the filepath + * @return the filename minus path + */ + public static String removePath( final String filepath, final char fileSeparatorChar ) + { + final int index = filepath.lastIndexOf( fileSeparatorChar ); + + if ( -1 == index ) + { + return filepath; + } + else + { + return filepath.substring( index + 1 ); + } + } + + /** + * Get path from filename. Roughly equivalent to the unix command
dirname
.
+ * ie.
+ * + * a/b/c.txt --> a/b + * a.txt --> "" + *+ * + * @param filepath the filepath + * @return the filename minus path + */ + public static String getPath( final String filepath ) + { + return getPath( filepath, File.separatorChar ); + } + + /** + * Get path from filename. + * ie. + *
+ * a/b/c.txt --> a/b + * a.txt --> "" + *+ * + * @param filepath the filepath + * @return the filename minus path + */ + public static String getPath( final String filepath, final char fileSeparatorChar ) + { + final int index = filepath.lastIndexOf( fileSeparatorChar ); + if ( -1 == index ) + { + return ""; + } + else + { + return filepath.substring( 0, index ); + } + } + + /** + * Copy file from source to destination. If
destinationDirectory
does not exist, it
+ * (and any parent directories) will be created. If a file source
in
+ * destinationDirectory
exists, it will be overwritten.
+ *
+ * @param source An existing File
to copy.
+ * @param destinationDirectory A directory to copy source
into.
+ * @throws java.io.FileNotFoundException if source
isn't a normal file.
+ * @throws IllegalArgumentException if destinationDirectory
isn't a directory.
+ * @throws IOException if source
does not exist, the file in
+ * destinationDirectory
cannot be written to, or an IO error occurs during copying.
+ */
+ public static void copyFileToDirectory( final String source,
+ final String destinationDirectory )
+ throws IOException
+ {
+ copyFileToDirectory( new File( source ),
+ new File( destinationDirectory ) );
+ }
+
+ /**
+ * Copy file from source to destination. If destinationDirectory
does not exist, it
+ * (and any parent directories) will be created. If a file source
in
+ * destinationDirectory
exists, it will be overwritten.
+ *
+ * @param source An existing File
to copy.
+ * @param destinationDirectory A directory to copy source
into.
+ * @throws java.io.FileNotFoundException if source
isn't a normal file.
+ * @throws IllegalArgumentException if destinationDirectory
isn't a directory.
+ * @throws IOException if source
does not exist, the file in
+ * destinationDirectory
cannot be written to, or an IO error occurs during copying.
+ */
+ public static void copyFileToDirectory( final File source,
+ final File destinationDirectory )
+ throws IOException
+ {
+ if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() )
+ {
+ throw new IllegalArgumentException( "Destination is not a directory" );
+ }
+
+ copyFile( source, new File( destinationDirectory, source.getName() ) );
+ }
+
+ /**
+ * Copy file from source to destination. The directories up to destination
will be
+ * created if they don't already exist. destination
will be overwritten if it
+ * already exists.
+ *
+ * @param source An existing non-directory File
to copy bytes from.
+ * @param destination A non-directory File
to write bytes to (possibly
+ * overwriting).
+ * @throws IOException if source
does not exist, destination
cannot be
+ * written to, or an IO error occurs during copying.
+ * @throws java.io.FileNotFoundException if destination
is a directory
+ * (use {@link #copyFileToDirectory}).
+ */
+ public static void copyFile( final File source, final File destination )
+ throws IOException
+ {
+ //check source exists
+ if ( !source.exists() )
+ {
+ final String message = "File " + source + " does not exist";
+ throw new IOException( message );
+ }
+
+ //does destinations directory exist ?
+ if ( destination.getParentFile() != null &&
+ !destination.getParentFile().exists() )
+ {
+ destination.getParentFile().mkdirs();
+ }
+
+ //make sure we can write to destination
+ if ( destination.exists() && !destination.canWrite() )
+ {
+ final String message = "Unable to open file " +
+ destination + " for writing.";
+ throw new IOException( message );
+ }
+
+ final FileInputStream input = new FileInputStream( source );
+ final FileOutputStream output = new FileOutputStream( destination );
+ IOUtil.copy( input, output );
+
+ input.close();
+ output.close();
+
+ if ( source.length() != destination.length() )
+ {
+ final String message = "Failed to copy full contents from " + source +
+ " to " + destination;
+ throw new IOException( message );
+ }
+ }
+
+ /**
+ * Copies bytes from the URL source
to a file destination
.
+ * The directories up to destination
will be created if they don't already exist.
+ * destination
will be overwritten if it already exists.
+ *
+ * @param source A URL
to copy bytes from.
+ * @param destination A non-directory File
to write bytes to (possibly
+ * overwriting).
+ * @throws IOException if
+ * source
URL cannot be openeddestination
cannot be written tonull
if the ..'s went past the
+ * root.
+ * Eg:
+ * + * /foo// --> /foo/ + * /foo/./ --> /foo/ + * /foo/../bar --> /bar + * /foo/../bar/ --> /bar/ + * /foo/../bar/../baz --> /baz + * //foo//./bar --> /foo/bar + * /../ --> null + *+ * + * @param path the path to normalize + * @return the normalized String, or
null
if too many ..'s.
+ */
+ public static String normalize( final String path )
+ {
+ String normalized = path;
+ // Resolve occurrences of "//" in the normalized path
+ while ( true )
+ {
+ int index = normalized.indexOf( "//" );
+ if ( index < 0 )
+ break;
+ normalized = normalized.substring( 0, index ) +
+ normalized.substring( index + 1 );
+ }
+
+ // Resolve occurrences of "/./" in the normalized path
+ while ( true )
+ {
+ int index = normalized.indexOf( "/./" );
+ if ( index < 0 )
+ break;
+ normalized = normalized.substring( 0, index ) +
+ normalized.substring( index + 2 );
+ }
+
+ // Resolve occurrences of "/../" in the normalized path
+ while ( true )
+ {
+ int index = normalized.indexOf( "/../" );
+ if ( index < 0 )
+ break;
+ if ( index == 0 )
+ return null; // Trying to go outside our context
+ int index2 = normalized.lastIndexOf( '/', index - 1 );
+ normalized = normalized.substring( 0, index2 ) +
+ normalized.substring( index + 3 );
+ }
+
+ // Return the normalized path that we have completed
+ return normalized;
+ }
+
+ /**
+ * Will concatenate 2 paths. Paths with ..
will be
+ * properly handled.
+ * Eg.,
+ * /a/b/c
+ d
= /a/b/d
+ * /a/b/c
+ ../d
= /a/d
+ *
filename
to it's canonical form. If filename
is
+ * relative (doesn't start with /
), it will be resolved relative to
+ * baseFile
, otherwise it is treated as a normal root-relative path.
+ *
+ * @param baseFile Where to resolve filename
from, if filename
is
+ * relative.
+ * @param filename Absolute or relative file path to resolve.
+ * @return The canonical File
of filename
.
+ */
+ public static File resolveFile( final File baseFile, String filename )
+ {
+ String filenm = filename;
+ if ( '/' != File.separatorChar )
+ {
+ filenm = filename.replace( '/', File.separatorChar );
+ }
+
+ if ( '\\' != File.separatorChar )
+ {
+ filenm = filename.replace( '\\', File.separatorChar );
+ }
+
+ // deal with absolute files
+ if ( filenm.startsWith( File.separator ) )
+ {
+ File file = new File( filenm );
+
+ try
+ {
+ file = file.getCanonicalFile();
+ }
+ catch ( final IOException ioe )
+ {
+ }
+
+ return file;
+ }
+ // FIXME: I'm almost certain this // removal is unnecessary, as getAbsoluteFile() strips
+ // them. However, I'm not sure about this UNC stuff. (JT)
+ final char[] chars = filename.toCharArray();
+ final StringBuffer sb = new StringBuffer();
+
+ //remove duplicate file separators in succession - except
+ //on win32 at start of filename as UNC filenames can
+ //be \\AComputer\AShare\myfile.txt
+ int start = 0;
+ if ( '\\' == File.separatorChar )
+ {
+ sb.append( filenm.charAt( 0 ) );
+ start++;
+ }
+
+ for ( int i = start; i < chars.length; i++ )
+ {
+ final boolean doubleSeparator =
+ File.separatorChar == chars[i] && File.separatorChar == chars[i - 1];
+
+ if ( !doubleSeparator )
+ {
+ sb.append( chars[i] );
+ }
+ }
+
+ filenm = sb.toString();
+
+ //must be relative
+ File file = ( new File( baseFile, filenm ) ).getAbsoluteFile();
+
+ try
+ {
+ file = file.getCanonicalFile();
+ }
+ catch ( final IOException ioe )
+ {
+ }
+
+ return file;
+ }
+
+ /**
+ * Delete a file. If file is directory delete it and all sub-directories.
+ */
+ public static void forceDelete( final String file )
+ throws IOException
+ {
+ forceDelete( new File( file ) );
+ }
+
+ /**
+ * Delete a file. If file is directory delete it and all sub-directories.
+ */
+ public static void forceDelete( final File file )
+ throws IOException
+ {
+ if ( file.isDirectory() )
+ {
+ deleteDirectory( file );
+ }
+ else
+ {
+ if ( !file.delete() )
+ {
+ final String message =
+ "File " + file + " unable to be deleted.";
+ throw new IOException( message );
+ }
+ }
+ }
+
+ /**
+ * Schedule a file to be deleted when JVM exits.
+ * If file is directory delete it and all sub-directories.
+ */
+ public static void forceDeleteOnExit( final File file )
+ throws IOException
+ {
+ if ( file.isDirectory() )
+ {
+ deleteDirectoryOnExit( file );
+ }
+ else
+ {
+ file.deleteOnExit();
+ }
+ }
+
+ /**
+ * Recursively schedule directory for deletion on JVM exit.
+ */
+ private static void deleteDirectoryOnExit( final File directory )
+ throws IOException
+ {
+ if ( !directory.exists() )
+ {
+ return;
+ }
+
+ cleanDirectoryOnExit( directory );
+ directory.deleteOnExit();
+ }
+
+ /**
+ * Clean a directory without deleting it.
+ */
+ private static void cleanDirectoryOnExit( final File directory )
+ throws IOException
+ {
+ if ( !directory.exists() )
+ {
+ final String message = directory + " does not exist";
+ throw new IllegalArgumentException( message );
+ }
+
+ if ( !directory.isDirectory() )
+ {
+ final String message = directory + " is not a directory";
+ throw new IllegalArgumentException( message );
+ }
+
+ IOException exception = null;
+
+ final File[] files = directory.listFiles();
+ for ( int i = 0; i < files.length; i++ )
+ {
+ final File file = files[i];
+ try
+ {
+ forceDeleteOnExit( file );
+ }
+ catch ( final IOException ioe )
+ {
+ exception = ioe;
+ }
+ }
+
+ if ( null != exception )
+ {
+ throw exception;
+ }
+ }
+
+
+ /**
+ * Make a directory. If there already exists a file with specified name or
+ * the directory is unable to be created then an exception is thrown.
+ */
+ public static void forceMkdir( final File file )
+ throws IOException
+ {
+ if ( file.exists() )
+ {
+ if ( file.isFile() )
+ {
+ final String message = "File " + file + " exists and is " +
+ "not a directory. Unable to create directory.";
+ throw new IOException( message );
+ }
+ }
+ else
+ {
+ if ( false == file.mkdirs() )
+ {
+ final String message = "Unable to create directory " + file;
+ throw new IOException( message );
+ }
+ }
+ }
+
+ /**
+ * Recursively delete a directory.
+ */
+ public static void deleteDirectory( final String directory )
+ throws IOException
+ {
+ deleteDirectory( new File( directory ) );
+ }
+
+ /**
+ * Recursively delete a directory.
+ */
+ public static void deleteDirectory( final File directory )
+ throws IOException
+ {
+ if ( !directory.exists() )
+ {
+ return;
+ }
+
+ cleanDirectory( directory );
+ if ( !directory.delete() )
+ {
+ final String message =
+ "Directory " + directory + " unable to be deleted.";
+ throw new IOException( message );
+ }
+ }
+
+ /**
+ * Clean a directory without deleting it.
+ */
+ public static void cleanDirectory( final String directory )
+ throws IOException
+ {
+ cleanDirectory( new File( directory ) );
+ }
+
+ /**
+ * Clean a directory without deleting it.
+ */
+ public static void cleanDirectory( final File directory )
+ throws IOException
+ {
+ if ( !directory.exists() )
+ {
+ final String message = directory + " does not exist";
+ throw new IllegalArgumentException( message );
+ }
+
+ if ( !directory.isDirectory() )
+ {
+ final String message = directory + " is not a directory";
+ throw new IllegalArgumentException( message );
+ }
+
+ IOException exception = null;
+
+ final File[] files = directory.listFiles();
+ for ( int i = 0; i < files.length; i++ )
+ {
+ final File file = files[i];
+ try
+ {
+ forceDelete( file );
+ }
+ catch ( final IOException ioe )
+ {
+ exception = ioe;
+ }
+ }
+
+ if ( null != exception )
+ {
+ throw exception;
+ }
+ }
+
+ /**
+ * Recursively count size of a directory.
+ *
+ * @return size of directory in bytes.
+ */
+ public static long sizeOfDirectory( final String directory )
+ {
+ return sizeOfDirectory( new File( directory ) );
+ }
+
+ /**
+ * Recursively count size of a directory.
+ *
+ * @return size of directory in bytes.
+ */
+ public static long sizeOfDirectory( final File directory )
+ {
+ if ( !directory.exists() )
+ {
+ final String message = directory + " does not exist";
+ throw new IllegalArgumentException( message );
+ }
+
+ if ( !directory.isDirectory() )
+ {
+ final String message = directory + " is not a directory";
+ throw new IllegalArgumentException( message );
+ }
+
+ long size = 0;
+
+ final File[] files = directory.listFiles();
+ for ( int i = 0; i < files.length; i++ )
+ {
+ final File file = files[i];
+
+ if ( file.isDirectory() )
+ {
+ size += sizeOfDirectory( file );
+ }
+ else
+ {
+ size += file.length();
+ }
+ }
+
+ return size;
+ }
+
+ public static List getFiles( File directory, String includes, String excludes )
+ throws IOException
+ {
+ return getFiles( directory, includes, excludes, true );
+ }
+
+ public static List getFiles( File directory, String includes, String excludes, boolean includeBasedir )
+ throws IOException
+ {
+ List fileNames = getFileNames( directory, includes, excludes, includeBasedir );
+
+ List files = new ArrayList();
+
+ for ( Iterator i = fileNames.iterator(); i.hasNext(); )
+ {
+ files.add( new File( (String) i.next() ) );
+ }
+
+ return files;
+ }
+
+ public static String FS = System.getProperty( "file.separator" );
+
+ public static List getFileNames( File directory, String includes, String excludes, boolean includeBasedir )
+ throws IOException
+ {
+ DirectoryScanner scanner = new DirectoryScanner();
+
+ scanner.setBasedir( directory );
+
+ if ( includes != null )
+ {
+ scanner.setIncludes( StringUtils.split( includes, "," ) );
+ }
+
+ if ( excludes != null )
+ {
+ scanner.setExcludes( StringUtils.split( excludes, "," ) );
+ }
+
+ scanner.scan();
+
+ String[] files = scanner.getIncludedFiles();
+
+ List list = new ArrayList();
+
+ for ( int i = 0; i < files.length; i++ )
+ {
+ if ( includeBasedir )
+ {
+ list.add( directory + FS + files[i] );
+ }
+ else
+ {
+ list.add( files[i] );
+ }
+ }
+
+ return list;
+ }
+
+ public static void copyDirectory( File sourceDirectory, File destinationDirectory )
+ throws IOException
+ {
+ if ( !sourceDirectory.exists() )
+ {
+ return;
+ }
+
+ List files = getFiles( sourceDirectory, "**", null );
+
+ for ( Iterator i = files.iterator(); i.hasNext(); )
+ {
+ File file = (File) i.next();
+
+ copyFileToDirectory( file, destinationDirectory );
+ }
+ }
+}
diff --git a/maven-mboot2/src/main/java/HttpUtils.java b/maven-mboot2/src/main/java/HttpUtils.java
new file mode 100644
index 0000000000..a756013890
--- /dev/null
+++ b/maven-mboot2/src/main/java/HttpUtils.java
@@ -0,0 +1,398 @@
+/* ====================================================================
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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.
+ * ====================================================================
+ */
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.Authenticator;
+import java.net.HttpURLConnection;
+import java.net.PasswordAuthentication;
+import java.net.URL;
+import java.net.URLConnection;
+
+/**
+ * Http utils for retrieving files.
+ *
+ * @author costin@dnt.ro
+ * @author gg@grtmail.com (Added Java 1.1 style HTTP basic auth)
+ * @author Jason van Zyl
+ * @todo Need to add a timeout so we can flip to a backup repository.
+ * @todo Download everything in a single session.
+ * @todo Throw meaningful exception when authentication fails.
+ */
+public class HttpUtils
+{
+ /**
+ * Use a proxy to bypass the firewall with or without authentication
+ *
+ * @param proxyHost Proxy Host (if proxy is required), or null
+ * @param proxyPort Proxy Port (if proxy is required), or null
+ * @param proxyUserName Proxy Username (if authentification is required),
+ * or null
+ * @param proxyPassword Proxy Password (if authentification is required),
+ * or null
+ * @throws SecurityException if an operation is not authorized by the
+ * SecurityManager
+ */
+ public static void useProxyUser( final String proxyHost,
+ final String proxyPort,
+ final String proxyUserName,
+ final String proxyPassword )
+ {
+ if ( proxyHost != null && proxyPort != null )
+ {
+ System.getProperties().put( "proxySet", "true" );
+ System.getProperties().put( "proxyHost", proxyHost );
+ System.getProperties().put( "proxyPort", proxyPort );
+
+ if ( proxyUserName != null )
+ {
+ Authenticator.setDefault( new Authenticator()
+ {
+ protected PasswordAuthentication getPasswordAuthentication()
+ {
+ return new PasswordAuthentication( proxyUserName,
+ proxyPassword == null ? new char[0] : proxyPassword.toCharArray() );
+ }
+ } );
+ }
+ }
+ }
+
+ /**
+ * Retrieve a remote file. Throws an Exception on errors unless the
+ * ifnoreErrors flag is set to True
+ *
+ * @param url the of the file to retrieve
+ * @param destinationFile where to store it
+ * @param ignoreErrors whether to ignore errors during I/O or throw an
+ * exception when they happen
+ * @param useTimestamp whether to check the modified timestamp on the
+ * destinationFile
against the remote source
+ * @param proxyHost Proxy Host (if proxy is required), or null
+ * @param proxyPort Proxy Port (if proxy is required), or null
+ * @param proxyUserName Proxy Username (if authentification is required),
+ * or null.
+ * @param proxyPassword Proxy Password (if authentification is required),
+ * or null.
+ * @param useChecksum Flag to indicate the use of the checksum for the retrieved
+ * artifact if it is available.
+ * @throws IOException If an I/O exception occurs.
+ */
+ public static void getFile( String url,
+ File destinationFile,
+ boolean ignoreErrors,
+ boolean useTimestamp,
+ String proxyHost,
+ String proxyPort,
+ String proxyUserName,
+ String proxyPassword,
+ boolean useChecksum )
+ throws IOException
+ {
+ // Get the requested file.
+ getFile( url,
+ destinationFile,
+ ignoreErrors,
+ useTimestamp,
+ proxyHost,
+ proxyPort,
+ proxyUserName,
+ proxyPassword );
+
+ // Get the checksum if requested.
+ if ( useChecksum )
+ {
+ File checksumFile = new File( destinationFile + ".md5" );
+
+ try
+ {
+ getFile( url + ".md5",
+ checksumFile,
+ ignoreErrors,
+ useTimestamp,
+ proxyHost,
+ proxyPort,
+ proxyUserName,
+ proxyPassword );
+ }
+ catch ( Exception e )
+ {
+ // do nothing we will check later in the process
+ // for the checksums.
+ }
+ }
+ }
+
+ /**
+ * Retrieve a remote file. Throws an Exception on errors unless the
+ * ifnoreErrors flag is set to True
+ *
+ * @param url the of the file to retrieve
+ * @param destinationFile where to store it
+ * @param ignoreErrors whether to ignore errors during I/O or throw an
+ * exception when they happen
+ * @param useTimestamp whether to check the modified timestamp on the
+ * destinationFile
against the remote source
+ * @param proxyHost Proxy Host (if proxy is required), or null
+ * @param proxyPort Proxy Port (if proxy is required), or null
+ * @param proxyUserName Proxy Username (if authentification is required),
+ * or null
+ * @param proxyPassword Proxy Password (if authentification is required),
+ * or null
+ * @throws IOException If an I/O exception occurs.
+ */
+ public static void getFile( String url,
+ File destinationFile,
+ boolean ignoreErrors,
+ boolean useTimestamp,
+ String proxyHost,
+ String proxyPort,
+ String proxyUserName,
+ String proxyPassword )
+ throws IOException
+ {
+ //set the timestamp to the file date.
+ long timestamp = -1;
+ if ( useTimestamp && destinationFile.exists() )
+ {
+ timestamp = destinationFile.lastModified();
+ }
+
+ try
+ {
+ getFile( url,
+ destinationFile,
+ timestamp,
+ proxyHost,
+ proxyPort,
+ proxyUserName,
+ proxyPassword );
+ }
+ catch ( IOException ex )
+ {
+ if ( !ignoreErrors )
+ {
+ throw ex;
+ }
+ }
+ }
+
+ /**
+ * Retrieve a remote file.
+ *
+ * @param url the URL of the file to retrieve
+ * @param destinationFile where to store it
+ * @param timestamp if provided, the remote URL is only retrieved if it was
+ * modified more recently than timestamp. Otherwise, negative value indicates that
+ * the remote URL should be retrieved unconditionally.
+ * @param proxyHost Proxy Host (if proxy is required), or null
+ * @param proxyPort Proxy Port (if proxy is required), or null
+ * @param proxyUserName Proxy Username (if authentification is required),
+ * or null
+ * @param proxyPassword Proxy Password (if authentification is required),
+ * or null
+ * @throws IOException If an I/O exception occurs.
+ */
+ public static void getFile( String url,
+ File destinationFile,
+ long timestamp,
+ String proxyHost,
+ String proxyPort,
+ String proxyUserName,
+ String proxyPassword )
+ throws IOException
+ {
+ String[] s = parseUrl( url );
+ String username = s[0];
+ String password = s[1];
+ String parsedUrl = s[2];
+
+ URL source = new URL( parsedUrl );
+
+ //set proxy connection
+ useProxyUser( proxyHost, proxyPort, proxyUserName, proxyPassword );
+
+ //set up the URL connection
+ URLConnection connection = source.openConnection();
+
+ //modify the headers
+ if ( timestamp >= 0 )
+ {
+ connection.setIfModifiedSince( timestamp );
+ }
+ // prepare Java 1.1 style credentials
+ if ( username != null || password != null )
+ {
+ String up = username + ":" + password;
+ String encoding = Base64.encode( up.getBytes(), false );
+ connection.setRequestProperty( "Authorization", "Basic " + encoding );
+ }
+
+ //connect to the remote site (may take some time)
+ connection.connect();
+ //next test for a 304 result (HTTP only)
+ if ( connection instanceof HttpURLConnection )
+ {
+ HttpURLConnection httpConnection = (HttpURLConnection) connection;
+ // although HTTPUrlConnection javadocs says FileNotFoundException should be
+ // thrown on a 404 error, that certainly does not appear to be the case, so
+ // test for 404 ourselves, and throw FileNotFoundException as needed
+ if ( httpConnection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND )
+ {
+ throw new FileNotFoundException( url.toString() + " (HTTP Error: "
+ + httpConnection.getResponseCode() + " " + httpConnection.getResponseMessage() + ")" );
+ }
+ if ( httpConnection.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED )
+ {
+ return;
+ }
+ // test for 401 result (HTTP only)
+ if ( httpConnection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED )
+ {
+ throw new IOException( "Not authorized." );
+ }
+ // test for 407 result (HTTP only)
+ if ( httpConnection.getResponseCode() == HttpURLConnection.HTTP_PROXY_AUTH )
+ {
+ throw new IOException( "Not authorized by proxy." );
+ }
+ }
+
+ // REVISIT: at this point even non HTTP connections may support the
+ // if-modified-since behaviour - we just check the date of the
+ // content and skip the write if it is not newer.
+ // Some protocols (FTP) dont include dates, of course.
+
+ InputStream is = null;
+ IOException isException = null;
+ for ( int i = 0; i < 3; i++ )
+ {
+ try
+ {
+ is = connection.getInputStream();
+ break;
+ }
+ catch ( IOException ex )
+ {
+ isException = ex;
+ }
+ }
+ if ( is == null )
+ {
+ throw isException;
+ }
+
+ if ( connection.getLastModified() <= timestamp &&
+ connection.getLastModified() != 0 )
+ {
+ return;
+ }
+
+ FileOutputStream fos = new FileOutputStream( destinationFile );
+
+ byte[] buffer = new byte[100 * 1024];
+ int length;
+
+ while ( ( length = is.read( buffer ) ) >= 0 )
+ {
+ fos.write( buffer, 0, length );
+ System.out.print( "." );
+ }
+
+ System.out.println();
+ fos.close();
+ is.close();
+
+ // if (and only if) the use file time option is set, then the
+ // saved file now has its timestamp set to that of the downloaded
+ // file
+ if ( timestamp >= 0 )
+ {
+ long remoteTimestamp = connection.getLastModified();
+ if ( remoteTimestamp != 0 )
+ {
+ touchFile( destinationFile, remoteTimestamp );
+ }
+ }
+ }
+
+ /**
+ * Parse an url which might contain a username and password. If the
+ * given url doesn't contain a username and password then return the
+ * origin url unchanged.
+ *
+ * @param url The url to parse.
+ * @return The username, password and url.
+ * @throws RuntimeException if the url is (very) invalid
+ */
+ static String[] parseUrl( String url )
+ {
+ String[] parsedUrl = new String[3];
+ parsedUrl[0] = null;
+ parsedUrl[1] = null;
+ parsedUrl[2] = url;
+
+ // We want to be able to deal with Basic Auth where the username
+ // and password are part of the URL. An example of the URL string
+ // we would like to be able to parse is like the following:
+ //
+ // http://username:password@repository.mycompany.com
+
+ int i = url.indexOf( "@" );
+ if ( i > 0 )
+ {
+ String protocol = url.substring( 0, url.indexOf( "://" ) ) + "://";
+ String s = url.substring( protocol.length(), i );
+ int j = s.indexOf( ":" );
+ parsedUrl[0] = s.substring( 0, j );
+ parsedUrl[1] = s.substring( j + 1 );
+ parsedUrl[2] = protocol + url.substring( i + 1 );
+ }
+
+ return parsedUrl;
+ }
+
+ /**
+ * set the timestamp of a named file to a specified time.
+ *
+ * @param file the file to touch
+ * @param timemillis in milliseconds since the start of the era
+ * @return true if it succeeded. False means that this is a java1.1 system
+ * and that file times can not be set
+ * @throws RuntimeException Thrown in unrecoverable error. Likely this
+ * comes from file access failures.
+ */
+ private static boolean touchFile( File file, long timemillis )
+ {
+ long modifiedTime;
+
+ if ( timemillis < 0 )
+ {
+ modifiedTime = System.currentTimeMillis();
+ }
+ else
+ {
+ modifiedTime = timemillis;
+ }
+
+ file.setLastModified( modifiedTime );
+ return true;
+ }
+}
diff --git a/maven-mboot2/src/main/java/IOUtil.java b/maven-mboot2/src/main/java/IOUtil.java
new file mode 100644
index 0000000000..993ac3eb7f
--- /dev/null
+++ b/maven-mboot2/src/main/java/IOUtil.java
@@ -0,0 +1,759 @@
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+
+/**
+ * General IO Stream manipulation.
+ *
+ * This class provides static utility methods for input/output operations, particularly buffered
+ * copying between sources (InputStream
, Reader
, String
and
+ * byte[]
) and destinations (OutputStream
, Writer
,
+ * String
and byte[]
).
+ *
+ *
+ * Unless otherwise noted, these copy
methods do not flush or close the
+ * streams. Often, doing so would require making non-portable assumptions about the streams' origin
+ * and further use. This means that both streams' close()
methods must be called after
+ * copying. if one omits this step, then the stream resources (sockets, file descriptors) are
+ * released when the associated Stream is garbage-collected. It is not a good idea to rely on this
+ * mechanism. For a good overview of the distinction between "memory management" and "resource
+ * management", see this
+ * UnixReview article
For each copy
method, a variant is provided that allows the caller to specify the
+ * buffer size (the default is 4k). As the buffer size can have a fairly large impact on speed, this
+ * may be worth tweaking. Often "large buffer -> faster" does not hold, even for large data
+ * transfers.
For byte-to-char methods, a copy
variant allows the encoding to be selected
+ * (otherwise the platform default is used).
The copy
methods use an internal buffer when copying. It is therefore advisable
+ * not to deliberately wrap the stream arguments to the copy
methods in
+ * Buffered*
streams. For example, don't do the
+ * following:
copy( new BufferedInputStream( in ), new BufferedOutputStream( out ) );
+ *
+ * The rationale is as follows:
+ * + *Imagine that an InputStream's read() is a very expensive operation, which would usually suggest
+ * wrapping in a BufferedInputStream. The BufferedInputStream works by issuing infrequent
+ * {@link java.io.InputStream#read(byte[] b, int off, int len)} requests on the underlying InputStream, to
+ * fill an internal buffer, from which further read
requests can inexpensively get
+ * their data (until the buffer runs out).
However, the copy
methods do the same thing, keeping an internal buffer,
+ * populated by {@link InputStream#read(byte[] b, int off, int len)} requests. Having two buffers
+ * (or three if the destination stream is also buffered) is pointless, and the unnecessary buffer
+ * management hurts performance slightly (about 3%, according to some simple experiments).
InputStream
to an OutputStream
.
+ */
+ public static void copy( final InputStream input, final OutputStream output )
+ throws IOException
+ {
+ copy( input, output, DEFAULT_BUFFER_SIZE );
+ }
+
+ /**
+ * Copy bytes from an InputStream
to an OutputStream
.
+ *
+ * @param bufferSize Size of internal buffer to use.
+ */
+ public static void copy( final InputStream input,
+ final OutputStream output,
+ final int bufferSize )
+ throws IOException
+ {
+ final byte[] buffer = new byte[bufferSize];
+ int n = 0;
+ while ( -1 != ( n = input.read( buffer ) ) )
+ {
+ output.write( buffer, 0, n );
+ }
+ }
+
+ /**
+ * Copy chars from a Reader
to a Writer
.
+ */
+ public static void copy( final Reader input, final Writer output )
+ throws IOException
+ {
+ copy( input, output, DEFAULT_BUFFER_SIZE );
+ }
+
+ /**
+ * Copy chars from a Reader
to a Writer
.
+ *
+ * @param bufferSize Size of internal buffer to use.
+ */
+ public static void copy( final Reader input, final Writer output, final int bufferSize )
+ throws IOException
+ {
+ final char[] buffer = new char[bufferSize];
+ int n = 0;
+ while ( -1 != ( n = input.read( buffer ) ) )
+ {
+ output.write( buffer, 0, n );
+ }
+ output.flush();
+ }
+
+ ///////////////////////////////////////////////////////////////
+ // Derived copy methods
+ // InputStream -> *
+ ///////////////////////////////////////////////////////////////
+
+
+ ///////////////////////////////////////////////////////////////
+ // InputStream -> Writer
+
+ /**
+ * Copy and convert bytes from an InputStream
to chars on a
+ * Writer
.
+ * The platform's default encoding is used for the byte-to-char conversion.
+ */
+ public static void copy( final InputStream input, final Writer output )
+ throws IOException
+ {
+ copy( input, output, DEFAULT_BUFFER_SIZE );
+ }
+
+ /**
+ * Copy and convert bytes from an InputStream
to chars on a
+ * Writer
.
+ * The platform's default encoding is used for the byte-to-char conversion.
+ *
+ * @param bufferSize Size of internal buffer to use.
+ */
+ public static void copy( final InputStream input, final Writer output, final int bufferSize )
+ throws IOException
+ {
+ final InputStreamReader in = new InputStreamReader( input );
+ copy( in, output, bufferSize );
+ }
+
+ /**
+ * Copy and convert bytes from an InputStream
to chars on a
+ * Writer
, using the specified encoding.
+ *
+ * @param encoding The name of a supported character encoding. See the
+ * IANA
+ * Charset Registry for a list of valid encoding types.
+ */
+ public static void copy( final InputStream input, final Writer output, final String encoding )
+ throws IOException
+ {
+ final InputStreamReader in = new InputStreamReader( input, encoding );
+ copy( in, output );
+ }
+
+ /**
+ * Copy and convert bytes from an InputStream
to chars on a
+ * Writer
, using the specified encoding.
+ *
+ * @param encoding The name of a supported character encoding. See the
+ * IANA
+ * Charset Registry for a list of valid encoding types.
+ * @param bufferSize Size of internal buffer to use.
+ */
+ public static void copy( final InputStream input,
+ final Writer output,
+ final String encoding,
+ final int bufferSize )
+ throws IOException
+ {
+ final InputStreamReader in = new InputStreamReader( input, encoding );
+ copy( in, output, bufferSize );
+ }
+
+
+ ///////////////////////////////////////////////////////////////
+ // InputStream -> String
+
+ /**
+ * Get the contents of an InputStream
as a String.
+ * The platform's default encoding is used for the byte-to-char conversion.
+ */
+ public static String toString( final InputStream input )
+ throws IOException
+ {
+ return toString( input, DEFAULT_BUFFER_SIZE );
+ }
+
+ /**
+ * Get the contents of an InputStream
as a String.
+ * The platform's default encoding is used for the byte-to-char conversion.
+ *
+ * @param bufferSize Size of internal buffer to use.
+ */
+ public static String toString( final InputStream input, final int bufferSize )
+ throws IOException
+ {
+ final StringWriter sw = new StringWriter();
+ copy( input, sw, bufferSize );
+ return sw.toString();
+ }
+
+ /**
+ * Get the contents of an InputStream
as a String.
+ *
+ * @param encoding The name of a supported character encoding. See the
+ * IANA
+ * Charset Registry for a list of valid encoding types.
+ */
+ public static String toString( final InputStream input, final String encoding )
+ throws IOException
+ {
+ return toString( input, encoding, DEFAULT_BUFFER_SIZE );
+ }
+
+ /**
+ * Get the contents of an InputStream
as a String.
+ *
+ * @param encoding The name of a supported character encoding. See the
+ * IANA
+ * Charset Registry for a list of valid encoding types.
+ * @param bufferSize Size of internal buffer to use.
+ */
+ public static String toString( final InputStream input,
+ final String encoding,
+ final int bufferSize )
+ throws IOException
+ {
+ final StringWriter sw = new StringWriter();
+ copy( input, sw, encoding, bufferSize );
+ return sw.toString();
+ }
+
+ ///////////////////////////////////////////////////////////////
+ // InputStream -> byte[]
+
+ /**
+ * Get the contents of an InputStream
as a byte[]
.
+ */
+ public static byte[] toByteArray( final InputStream input )
+ throws IOException
+ {
+ return toByteArray( input, DEFAULT_BUFFER_SIZE );
+ }
+
+ /**
+ * Get the contents of an InputStream
as a byte[]
.
+ *
+ * @param bufferSize Size of internal buffer to use.
+ */
+ public static byte[] toByteArray( final InputStream input, final int bufferSize )
+ throws IOException
+ {
+ final ByteArrayOutputStream output = new ByteArrayOutputStream();
+ copy( input, output, bufferSize );
+ return output.toByteArray();
+ }
+
+
+ ///////////////////////////////////////////////////////////////
+ // Derived copy methods
+ // Reader -> *
+ ///////////////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////////////////
+ // Reader -> OutputStream
+ /**
+ * Serialize chars from a Reader
to bytes on an OutputStream
, and
+ * flush the OutputStream
.
+ */
+ public static void copy( final Reader input, final OutputStream output )
+ throws IOException
+ {
+ copy( input, output, DEFAULT_BUFFER_SIZE );
+ }
+
+ /**
+ * Serialize chars from a Reader
to bytes on an OutputStream
, and
+ * flush the OutputStream
.
+ *
+ * @param bufferSize Size of internal buffer to use.
+ */
+ public static void copy( final Reader input, final OutputStream output, final int bufferSize )
+ throws IOException
+ {
+ final OutputStreamWriter out = new OutputStreamWriter( output );
+ copy( input, out, bufferSize );
+ // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush
+ // here.
+ out.flush();
+ }
+
+ ///////////////////////////////////////////////////////////////
+ // Reader -> String
+ /**
+ * Get the contents of a Reader
as a String.
+ */
+ public static String toString( final Reader input )
+ throws IOException
+ {
+ return toString( input, DEFAULT_BUFFER_SIZE );
+ }
+
+ /**
+ * Get the contents of a Reader
as a String.
+ *
+ * @param bufferSize Size of internal buffer to use.
+ */
+ public static String toString( final Reader input, final int bufferSize )
+ throws IOException
+ {
+ final StringWriter sw = new StringWriter();
+ copy( input, sw, bufferSize );
+ return sw.toString();
+ }
+
+
+ ///////////////////////////////////////////////////////////////
+ // Reader -> byte[]
+ /**
+ * Get the contents of a Reader
as a byte[]
.
+ */
+ public static byte[] toByteArray( final Reader input )
+ throws IOException
+ {
+ return toByteArray( input, DEFAULT_BUFFER_SIZE );
+ }
+
+ /**
+ * Get the contents of a Reader
as a byte[]
.
+ *
+ * @param bufferSize Size of internal buffer to use.
+ */
+ public static byte[] toByteArray( final Reader input, final int bufferSize )
+ throws IOException
+ {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ copy( input, output, bufferSize );
+ return output.toByteArray();
+ }
+
+
+ ///////////////////////////////////////////////////////////////
+ // Derived copy methods
+ // String -> *
+ ///////////////////////////////////////////////////////////////
+
+
+ ///////////////////////////////////////////////////////////////
+ // String -> OutputStream
+
+ /**
+ * Serialize chars from a String
to bytes on an OutputStream
, and
+ * flush the OutputStream
.
+ */
+ public static void copy( final String input, final OutputStream output )
+ throws IOException
+ {
+ copy( input, output, DEFAULT_BUFFER_SIZE );
+ }
+
+ /**
+ * Serialize chars from a String
to bytes on an OutputStream
, and
+ * flush the OutputStream
.
+ *
+ * @param bufferSize Size of internal buffer to use.
+ */
+ public static void copy( final String input, final OutputStream output, final int bufferSize )
+ throws IOException
+ {
+ final StringReader in = new StringReader( input );
+ final OutputStreamWriter out = new OutputStreamWriter( output );
+ copy( in, out, bufferSize );
+ // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush
+ // here.
+ out.flush();
+ }
+
+
+
+ ///////////////////////////////////////////////////////////////
+ // String -> Writer
+
+ /**
+ * Copy chars from a String
to a Writer
.
+ */
+ public static void copy( final String input, final Writer output )
+ throws IOException
+ {
+ output.write( input );
+ }
+
+ /**
+ * Copy bytes from an InputStream
to an
+ * OutputStream
, with buffering.
+ * This is equivalent to passing a
+ * {@link java.io.BufferedInputStream} and
+ * {@link java.io.BufferedOutputStream} to {@link #copy(InputStream, OutputStream)},
+ * and flushing the output stream afterwards. The streams are not closed
+ * after the copy.
+ *
+ * @deprecated Buffering streams is actively harmful! See the class description as to why. Use
+ * {@link #copy(InputStream, OutputStream)} instead.
+ */
+ public static void bufferedCopy( final InputStream input, final OutputStream output )
+ throws IOException
+ {
+ final BufferedInputStream in = new BufferedInputStream( input );
+ final BufferedOutputStream out = new BufferedOutputStream( output );
+ copy( in, out );
+ out.flush();
+ }
+
+
+ ///////////////////////////////////////////////////////////////
+ // String -> byte[]
+ /**
+ * Get the contents of a String
as a byte[]
.
+ */
+ public static byte[] toByteArray( final String input )
+ throws IOException
+ {
+ return toByteArray( input, DEFAULT_BUFFER_SIZE );
+ }
+
+ /**
+ * Get the contents of a String
as a byte[]
.
+ *
+ * @param bufferSize Size of internal buffer to use.
+ */
+ public static byte[] toByteArray( final String input, final int bufferSize )
+ throws IOException
+ {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ copy( input, output, bufferSize );
+ return output.toByteArray();
+ }
+
+
+
+ ///////////////////////////////////////////////////////////////
+ // Derived copy methods
+ // byte[] -> *
+ ///////////////////////////////////////////////////////////////
+
+
+ ///////////////////////////////////////////////////////////////
+ // byte[] -> Writer
+
+ /**
+ * Copy and convert bytes from a byte[]
to chars on a
+ * Writer
.
+ * The platform's default encoding is used for the byte-to-char conversion.
+ */
+ public static void copy( final byte[] input, final Writer output )
+ throws IOException
+ {
+ copy( input, output, DEFAULT_BUFFER_SIZE );
+ }
+
+ /**
+ * Copy and convert bytes from a byte[]
to chars on a
+ * Writer
.
+ * The platform's default encoding is used for the byte-to-char conversion.
+ *
+ * @param bufferSize Size of internal buffer to use.
+ */
+ public static void copy( final byte[] input, final Writer output, final int bufferSize )
+ throws IOException
+ {
+ final ByteArrayInputStream in = new ByteArrayInputStream( input );
+ copy( in, output, bufferSize );
+ }
+
+ /**
+ * Copy and convert bytes from a byte[]
to chars on a
+ * Writer
, using the specified encoding.
+ *
+ * @param encoding The name of a supported character encoding. See the
+ * IANA
+ * Charset Registry for a list of valid encoding types.
+ */
+ public static void copy( final byte[] input, final Writer output, final String encoding )
+ throws IOException
+ {
+ final ByteArrayInputStream in = new ByteArrayInputStream( input );
+ copy( in, output, encoding );
+ }
+
+ /**
+ * Copy and convert bytes from a byte[]
to chars on a
+ * Writer
, using the specified encoding.
+ *
+ * @param encoding The name of a supported character encoding. See the
+ * IANA
+ * Charset Registry for a list of valid encoding types.
+ * @param bufferSize Size of internal buffer to use.
+ */
+ public static void copy( final byte[] input,
+ final Writer output,
+ final String encoding,
+ final int bufferSize )
+ throws IOException
+ {
+ final ByteArrayInputStream in = new ByteArrayInputStream( input );
+ copy( in, output, encoding, bufferSize );
+ }
+
+
+ ///////////////////////////////////////////////////////////////
+ // byte[] -> String
+
+ /**
+ * Get the contents of a byte[]
as a String.
+ * The platform's default encoding is used for the byte-to-char conversion.
+ */
+ public static String toString( final byte[] input )
+ throws IOException
+ {
+ return toString( input, DEFAULT_BUFFER_SIZE );
+ }
+
+ /**
+ * Get the contents of a byte[]
as a String.
+ * The platform's default encoding is used for the byte-to-char conversion.
+ *
+ * @param bufferSize Size of internal buffer to use.
+ */
+ public static String toString( final byte[] input, final int bufferSize )
+ throws IOException
+ {
+ final StringWriter sw = new StringWriter();
+ copy( input, sw, bufferSize );
+ return sw.toString();
+ }
+
+ /**
+ * Get the contents of a byte[]
as a String.
+ *
+ * @param encoding The name of a supported character encoding. See the
+ * IANA
+ * Charset Registry for a list of valid encoding types.
+ */
+ public static String toString( final byte[] input, final String encoding )
+ throws IOException
+ {
+ return toString( input, encoding, DEFAULT_BUFFER_SIZE );
+ }
+
+ /**
+ * Get the contents of a byte[]
as a String.
+ *
+ * @param encoding The name of a supported character encoding. See the
+ * IANA
+ * Charset Registry for a list of valid encoding types.
+ * @param bufferSize Size of internal buffer to use.
+ */
+ public static String toString( final byte[] input,
+ final String encoding,
+ final int bufferSize )
+ throws IOException
+ {
+ final StringWriter sw = new StringWriter();
+ copy( input, sw, encoding, bufferSize );
+ return sw.toString();
+ }
+
+
+ ///////////////////////////////////////////////////////////////
+ // byte[] -> OutputStream
+
+ /**
+ * Copy bytes from a byte[]
to an OutputStream
.
+ */
+ public static void copy( final byte[] input, final OutputStream output )
+ throws IOException
+ {
+ copy( input, output, DEFAULT_BUFFER_SIZE );
+ }
+
+ /**
+ * Copy bytes from a byte[]
to an OutputStream
.
+ *
+ * @param bufferSize Size of internal buffer to use.
+ */
+ public static void copy( final byte[] input,
+ final OutputStream output,
+ final int bufferSize )
+ throws IOException
+ {
+ output.write( input );
+ }
+
+ /**
+ * Compare the contents of two Streams to determine if they are equal or not.
+ *
+ * @param input1 the first stream
+ * @param input2 the second stream
+ * @return true if the content of the streams are equal or they both don't exist, false otherwise
+ */
+ public static boolean contentEquals( final InputStream input1,
+ final InputStream input2 )
+ throws IOException
+ {
+ final InputStream bufferedInput1 = new BufferedInputStream( input1 );
+ final InputStream bufferedInput2 = new BufferedInputStream( input2 );
+
+ int ch = bufferedInput1.read();
+ while ( -1 != ch )
+ {
+ final int ch2 = bufferedInput2.read();
+ if ( ch != ch2 )
+ {
+ return false;
+ }
+ ch = bufferedInput1.read();
+ }
+
+ final int ch2 = bufferedInput2.read();
+ if ( -1 != ch2 )
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////
+ // close()
+
+ /**
+ * A IOException ignoring method that simply closes the stream.
+ *
+ * @param inputStream The stream to close.
+ */
+ public static void close( InputStream inputStream )
+ {
+ try
+ {
+ inputStream.close();
+ }
+ catch ( IOException ex )
+ {
+ // ignore
+ }
+ }
+
+ /**
+ * A IOException ignoring method that simply closes the stream.
+ *
+ * @param outputStream The stream to close.
+ */
+ public static void close( OutputStream outputStream )
+ {
+ try
+ {
+ outputStream.close();
+ }
+ catch ( IOException ex )
+ {
+ // ignore
+ }
+ }
+
+ /**
+ * A IOException ignoring method that simply closes the reader.
+ *
+ * @param reader The reader to close.
+ */
+ public static void close( Reader reader )
+ {
+ try
+ {
+ reader.close();
+ }
+ catch ( IOException ex )
+ {
+ // ignore
+ }
+ }
+
+ /**
+ * A IOException ignoring method that simply closes the stream.
+ *
+ * @param wrtier The writer to close.
+ */
+ public static void close( Writer writer )
+ {
+ try
+ {
+ writer.close();
+ }
+ catch ( IOException ex )
+ {
+ // ignore
+ }
+ }
+}
diff --git a/maven-mboot2/src/main/java/IsolatedClassLoader.java b/maven-mboot2/src/main/java/IsolatedClassLoader.java
new file mode 100644
index 0000000000..aaec51df46
--- /dev/null
+++ b/maven-mboot2/src/main/java/IsolatedClassLoader.java
@@ -0,0 +1,51 @@
+
+import java.net.URLClassLoader;
+import java.net.URL;
+
+public class IsolatedClassLoader
+ extends URLClassLoader
+{
+ private ClassLoader parent = ClassLoader.getSystemClassLoader();
+
+ public IsolatedClassLoader()
+ {
+ super( new URL[0], null );
+ }
+
+ public void addURL( URL url )
+ {
+ super.addURL( url );
+ }
+
+ public synchronized Class loadClass( String className )
+ throws ClassNotFoundException
+ {
+ Class c = findLoadedClass( className );
+
+ ClassNotFoundException ex = null;
+
+ if ( c == null )
+ {
+ try
+ {
+ c = findClass( className );
+ }
+ catch ( ClassNotFoundException e )
+ {
+ ex = e;
+
+ if ( parent != null )
+ {
+ c = parent.loadClass( className );
+ }
+ }
+ }
+
+ if ( c == null )
+ {
+ throw ex;
+ }
+
+ return c;
+ }
+}
diff --git a/maven-mboot2/src/main/java/JarMojo.java b/maven-mboot2/src/main/java/JarMojo.java
new file mode 100644
index 0000000000..670bca7bce
--- /dev/null
+++ b/maven-mboot2/src/main/java/JarMojo.java
@@ -0,0 +1,206 @@
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+public class JarMojo
+{
+ private byte[] buffer = new byte[4096];
+
+ public void execute( File basedir, String outputDirectory, String jarName )
+ throws Exception
+ {
+ File jarFile = new File( new File( outputDirectory ), jarName + ".jar" );
+
+ Map includes = new LinkedHashMap();
+
+ addDirectory( includes, "**/**", "**/package.html", "", basedir );
+
+ createJar( jarFile, includes );
+ }
+
+ /**
+ * Add all files in the specified directory to the archive.
+ *
+ * @param includes a map This is a utility class used by selectors and DirectoryScanner. The + * functionality more properly belongs just to selectors, but unfortunately + * DirectoryScanner exposed these as protected methods. Thus we have to + * support any subclasses of DirectoryScanner that may access these methods. + *
+ *This is a Singleton.
+ * + * @author Arnout J. Kuiper + * ajkuiper@wxs.nl + * @author Magesh Umasankar + * @author Bruce Atherton + * @since 1.5 + */ +public final class SelectorUtils +{ + + private static SelectorUtils instance = new SelectorUtils(); + + /** + * Private Constructor + */ + private SelectorUtils() + { + } + + /** + * Retrieves the manager of the Singleton. + */ + public static SelectorUtils getInstance() + { + return instance; + } + + /** + * Tests whether or not a given path matches the start of a given + * pattern up to the first "**". + * + * This is not a general purpose test and should only be used if you + * can live with false positives. For example,pattern=**\a
+ * and str=b
will yield true
.
+ *
+ * @param pattern The pattern to match against. Must not be
+ * null
.
+ * @param str The path to match, as a String. Must not be
+ * null
.
+ * @return whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ */
+ public static boolean matchPatternStart( String pattern, String str )
+ {
+ return matchPatternStart( pattern, str, true );
+ }
+
+ /**
+ * Tests whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ *
+ * This is not a general purpose test and should only be used if you
+ * can live with false positives. For example, pattern=**\a
+ * and str=b
will yield true
.
+ *
+ * @param pattern The pattern to match against. Must not be
+ * null
.
+ * @param str The path to match, as a String. Must not be
+ * null
.
+ * @param isCaseSensitive Whether or not matching should be performed
+ * case sensitively.
+ * @return whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ */
+ public static boolean matchPatternStart( String pattern, String str,
+ boolean isCaseSensitive )
+ {
+ // When str starts with a File.separator, pattern has to start with a
+ // File.separator.
+ // When pattern starts with a File.separator, str has to start with a
+ // File.separator.
+ if ( str.startsWith( File.separator ) !=
+ pattern.startsWith( File.separator ) )
+ {
+ return false;
+ }
+
+ Vector patDirs = tokenizePath( pattern );
+ Vector strDirs = tokenizePath( str );
+
+ int patIdxStart = 0;
+ int patIdxEnd = patDirs.size() - 1;
+ int strIdxStart = 0;
+ int strIdxEnd = strDirs.size() - 1;
+
+ // up to first '**'
+ while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
+ {
+ String patDir = (String) patDirs.elementAt( patIdxStart );
+ if ( patDir.equals( "**" ) )
+ {
+ break;
+ }
+ if ( !match( patDir, (String) strDirs.elementAt( strIdxStart ),
+ isCaseSensitive ) )
+ {
+ return false;
+ }
+ patIdxStart++;
+ strIdxStart++;
+ }
+
+ if ( strIdxStart > strIdxEnd )
+ {
+ // String is exhausted
+ return true;
+ }
+ else if ( patIdxStart > patIdxEnd )
+ {
+ // String not exhausted, but pattern is. Failure.
+ return false;
+ }
+ else
+ {
+ // pattern now holds ** while string is not exhausted
+ // this will generate false positives but we can live with that.
+ return true;
+ }
+ }
+
+ /**
+ * Tests whether or not a given path matches a given pattern.
+ *
+ * @param pattern The pattern to match against. Must not be
+ * null
.
+ * @param str The path to match, as a String. Must not be
+ * null
.
+ * @return true
if the pattern matches against the string,
+ * or false
otherwise.
+ */
+ public static boolean matchPath( String pattern, String str )
+ {
+ return matchPath( pattern, str, true );
+ }
+
+ /**
+ * Tests whether or not a given path matches a given pattern.
+ *
+ * @param pattern The pattern to match against. Must not be
+ * null
.
+ * @param str The path to match, as a String. Must not be
+ * null
.
+ * @param isCaseSensitive Whether or not matching should be performed
+ * case sensitively.
+ * @return true
if the pattern matches against the string,
+ * or false
otherwise.
+ */
+ public static boolean matchPath( String pattern, String str,
+ boolean isCaseSensitive )
+ {
+ // When str starts with a File.separator, pattern has to start with a
+ // File.separator.
+ // When pattern starts with a File.separator, str has to start with a
+ // File.separator.
+ if ( str.startsWith( File.separator ) !=
+ pattern.startsWith( File.separator ) )
+ {
+ return false;
+ }
+
+ Vector patDirs = tokenizePath( pattern );
+ Vector strDirs = tokenizePath( str );
+
+ int patIdxStart = 0;
+ int patIdxEnd = patDirs.size() - 1;
+ int strIdxStart = 0;
+ int strIdxEnd = strDirs.size() - 1;
+
+ // up to first '**'
+ while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
+ {
+ String patDir = (String) patDirs.elementAt( patIdxStart );
+ if ( patDir.equals( "**" ) )
+ {
+ break;
+ }
+ if ( !match( patDir, (String) strDirs.elementAt( strIdxStart ),
+ isCaseSensitive ) )
+ {
+ patDirs = null;
+ strDirs = null;
+ return false;
+ }
+ patIdxStart++;
+ strIdxStart++;
+ }
+ if ( strIdxStart > strIdxEnd )
+ {
+ // String is exhausted
+ for ( int i = patIdxStart; i <= patIdxEnd; i++ )
+ {
+ if ( !patDirs.elementAt( i ).equals( "**" ) )
+ {
+ patDirs = null;
+ strDirs = null;
+ return false;
+ }
+ }
+ return true;
+ }
+ else
+ {
+ if ( patIdxStart > patIdxEnd )
+ {
+ // String not exhausted, but pattern is. Failure.
+ patDirs = null;
+ strDirs = null;
+ return false;
+ }
+ }
+
+ // up to last '**'
+ while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
+ {
+ String patDir = (String) patDirs.elementAt( patIdxEnd );
+ if ( patDir.equals( "**" ) )
+ {
+ break;
+ }
+ if ( !match( patDir, (String) strDirs.elementAt( strIdxEnd ),
+ isCaseSensitive ) )
+ {
+ patDirs = null;
+ strDirs = null;
+ return false;
+ }
+ patIdxEnd--;
+ strIdxEnd--;
+ }
+ if ( strIdxStart > strIdxEnd )
+ {
+ // String is exhausted
+ for ( int i = patIdxStart; i <= patIdxEnd; i++ )
+ {
+ if ( !patDirs.elementAt( i ).equals( "**" ) )
+ {
+ patDirs = null;
+ strDirs = null;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd )
+ {
+ int patIdxTmp = -1;
+ for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ )
+ {
+ if ( patDirs.elementAt( i ).equals( "**" ) )
+ {
+ patIdxTmp = i;
+ break;
+ }
+ }
+ if ( patIdxTmp == patIdxStart + 1 )
+ {
+ // '**/**' situation, so skip one
+ patIdxStart++;
+ continue;
+ }
+ // Find the pattern between padIdxStart & padIdxTmp in str between
+ // strIdxStart & strIdxEnd
+ int patLength = ( patIdxTmp - patIdxStart - 1 );
+ int strLength = ( strIdxEnd - strIdxStart + 1 );
+ int foundIdx = -1;
+ strLoop:
+ for ( int i = 0; i <= strLength - patLength; i++ )
+ {
+ for ( int j = 0; j < patLength; j++ )
+ {
+ String subPat = (String) patDirs.elementAt( patIdxStart + j + 1 );
+ String subStr = (String) strDirs.elementAt( strIdxStart + i + j );
+ if ( !match( subPat, subStr, isCaseSensitive ) )
+ {
+ continue strLoop;
+ }
+ }
+
+ foundIdx = strIdxStart + i;
+ break;
+ }
+
+ if ( foundIdx == -1 )
+ {
+ patDirs = null;
+ strDirs = null;
+ return false;
+ }
+
+ patIdxStart = patIdxTmp;
+ strIdxStart = foundIdx + patLength;
+ }
+
+ for ( int i = patIdxStart; i <= patIdxEnd; i++ )
+ {
+ if ( !patDirs.elementAt( i ).equals( "**" ) )
+ {
+ patDirs = null;
+ strDirs = null;
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Tests whether or not a string matches against a pattern.
+ * The pattern may contain two special characters:null
.
+ * @param str The string which must be matched against the pattern.
+ * Must not be null
.
+ * @return true
if the string matches against the pattern,
+ * or false
otherwise.
+ */
+ public static boolean match( String pattern, String str )
+ {
+ return match( pattern, str, true );
+ }
+
+ /**
+ * Tests whether or not a string matches against a pattern.
+ * The pattern may contain two special characters:null
.
+ * @param str The string which must be matched against the pattern.
+ * Must not be null
.
+ * @param isCaseSensitive Whether or not matching should be performed
+ * case sensitively.
+ * @return true
if the string matches against the pattern,
+ * or false
otherwise.
+ */
+ public static boolean match( String pattern, String str,
+ boolean isCaseSensitive )
+ {
+ char[] patArr = pattern.toCharArray();
+ char[] strArr = str.toCharArray();
+ int patIdxStart = 0;
+ int patIdxEnd = patArr.length - 1;
+ int strIdxStart = 0;
+ int strIdxEnd = strArr.length - 1;
+ char ch;
+
+ boolean containsStar = false;
+ for ( int i = 0; i < patArr.length; i++ )
+ {
+ if ( patArr[i] == '*' )
+ {
+ containsStar = true;
+ break;
+ }
+ }
+
+ if ( !containsStar )
+ {
+ // No '*'s, so we make a shortcut
+ if ( patIdxEnd != strIdxEnd )
+ {
+ return false; // Pattern and string do not have the same size
+ }
+ for ( int i = 0; i <= patIdxEnd; i++ )
+ {
+ ch = patArr[i];
+ if ( ch != '?' )
+ {
+ if ( isCaseSensitive && ch != strArr[i] )
+ {
+ return false;// Character mismatch
+ }
+ if ( !isCaseSensitive && Character.toUpperCase( ch ) !=
+ Character.toUpperCase( strArr[i] ) )
+ {
+ return false; // Character mismatch
+ }
+ }
+ }
+ return true; // String matches against pattern
+ }
+
+ if ( patIdxEnd == 0 )
+ {
+ return true; // Pattern contains only '*', which matches anything
+ }
+
+ // Process characters before first star
+ while ( ( ch = patArr[patIdxStart] ) != '*' && strIdxStart <= strIdxEnd )
+ {
+ if ( ch != '?' )
+ {
+ if ( isCaseSensitive && ch != strArr[strIdxStart] )
+ {
+ return false;// Character mismatch
+ }
+ if ( !isCaseSensitive && Character.toUpperCase( ch ) !=
+ Character.toUpperCase( strArr[strIdxStart] ) )
+ {
+ return false;// Character mismatch
+ }
+ }
+ patIdxStart++;
+ strIdxStart++;
+ }
+ if ( strIdxStart > strIdxEnd )
+ {
+ // All characters in the string are used. Check if only '*'s are
+ // left in the pattern. If so, we succeeded. Otherwise failure.
+ for ( int i = patIdxStart; i <= patIdxEnd; i++ )
+ {
+ if ( patArr[i] != '*' )
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Process characters after last star
+ while ( ( ch = patArr[patIdxEnd] ) != '*' && strIdxStart <= strIdxEnd )
+ {
+ if ( ch != '?' )
+ {
+ if ( isCaseSensitive && ch != strArr[strIdxEnd] )
+ {
+ return false;// Character mismatch
+ }
+ if ( !isCaseSensitive && Character.toUpperCase( ch ) !=
+ Character.toUpperCase( strArr[strIdxEnd] ) )
+ {
+ return false;// Character mismatch
+ }
+ }
+ patIdxEnd--;
+ strIdxEnd--;
+ }
+ if ( strIdxStart > strIdxEnd )
+ {
+ // All characters in the string are used. Check if only '*'s are
+ // left in the pattern. If so, we succeeded. Otherwise failure.
+ for ( int i = patIdxStart; i <= patIdxEnd; i++ )
+ {
+ if ( patArr[i] != '*' )
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // process pattern between stars. padIdxStart and patIdxEnd point
+ // always to a '*'.
+ while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd )
+ {
+ int patIdxTmp = -1;
+ for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ )
+ {
+ if ( patArr[i] == '*' )
+ {
+ patIdxTmp = i;
+ break;
+ }
+ }
+ if ( patIdxTmp == patIdxStart + 1 )
+ {
+ // Two stars next to each other, skip the first one.
+ patIdxStart++;
+ continue;
+ }
+ // Find the pattern between padIdxStart & padIdxTmp in str between
+ // strIdxStart & strIdxEnd
+ int patLength = ( patIdxTmp - patIdxStart - 1 );
+ int strLength = ( strIdxEnd - strIdxStart + 1 );
+ int foundIdx = -1;
+ strLoop:
+ for ( int i = 0; i <= strLength - patLength; i++ )
+ {
+ for ( int j = 0; j < patLength; j++ )
+ {
+ ch = patArr[patIdxStart + j + 1];
+ if ( ch != '?' )
+ {
+ if ( isCaseSensitive && ch != strArr[strIdxStart + i + j] )
+ {
+ continue strLoop;
+ }
+ if ( !isCaseSensitive && Character.toUpperCase( ch ) !=
+ Character.toUpperCase( strArr[strIdxStart + i + j] ) )
+ {
+ continue strLoop;
+ }
+ }
+ }
+
+ foundIdx = strIdxStart + i;
+ break;
+ }
+
+ if ( foundIdx == -1 )
+ {
+ return false;
+ }
+
+ patIdxStart = patIdxTmp;
+ strIdxStart = foundIdx + patLength;
+ }
+
+ // All characters in the string are used. Check if only '*'s are left
+ // in the pattern. If so, we succeeded. Otherwise failure.
+ for ( int i = patIdxStart; i <= patIdxEnd; i++ )
+ {
+ if ( patArr[i] != '*' )
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Breaks a path up into a Vector of path elements, tokenizing on
+ * File.separator
.
+ *
+ * @param path Path to tokenize. Must not be null
.
+ * @return a Vector of path elements from the tokenized path
+ */
+ public static Vector tokenizePath( String path )
+ {
+ Vector ret = new Vector();
+ StringTokenizer st = new StringTokenizer( path, File.separator );
+ while ( st.hasMoreTokens() )
+ {
+ ret.addElement( st.nextToken() );
+ }
+ return ret;
+ }
+
+
+ /**
+ * Returns dependency information on these two files. If src has been
+ * modified later than target, it returns true. If target doesn't exist,
+ * it likewise returns true. Otherwise, target is newer than src and
+ * is not out of date, thus the method returns false. It also returns
+ * false if the src file doesn't even exist, since how could the
+ * target then be out of date.
+ *
+ * @param src the original file
+ * @param target the file being compared against
+ * @param granularity the amount in seconds of slack we will give in
+ * determining out of dateness
+ * @return whether the target is out of date
+ */
+ public static boolean isOutOfDate( File src, File target, int granularity )
+ {
+ if ( !src.exists() )
+ {
+ return false;
+ }
+ if ( !target.exists() )
+ {
+ return true;
+ }
+ if ( ( src.lastModified() - granularity ) > target.lastModified() )
+ {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * "Flattens" a string by removing all whitespace (space, tab, linefeed,
+ * carriage return, and formfeed). This uses StringTokenizer and the
+ * default set of tokens as documented in the single arguement constructor.
+ *
+ * @param input a String to remove all whitespace.
+ * @return a String that has had all whitespace removed.
+ */
+ public static String removeWhitespace( String input )
+ {
+ StringBuffer result = new StringBuffer();
+ if ( input != null )
+ {
+ StringTokenizer st = new StringTokenizer( input );
+ while ( st.hasMoreTokens() )
+ {
+ result.append( st.nextToken() );
+ }
+ }
+ return result.toString();
+ }
+}
diff --git a/maven-mboot2/src/main/java/StreamPumper.java b/maven-mboot2/src/main/java/StreamPumper.java
new file mode 100644
index 0000000000..21ea135bd8
--- /dev/null
+++ b/maven-mboot2/src/main/java/StreamPumper.java
@@ -0,0 +1,59 @@
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * @author Jason van Zyl
+ * @version $Id$
+ */
+public class StreamPumper
+ extends Thread
+{
+ private static final int BUFFER_SIZE = 512;
+
+ private BufferedInputStream stream;
+ private boolean endOfStream = false;
+ private int SLEEP_TIME = 5;
+ private OutputStream out;
+
+ public StreamPumper( BufferedInputStream is, OutputStream out )
+ {
+ this.stream = is;
+ this.out = out;
+ }
+
+ public void pumpStream() throws IOException
+ {
+ byte[] buf = new byte[BUFFER_SIZE];
+ if ( !endOfStream )
+ {
+ int bytesRead = stream.read( buf, 0, BUFFER_SIZE );
+
+ if ( bytesRead > 0 )
+ {
+ out.write( buf, 0, bytesRead );
+ }
+ else if ( bytesRead == -1 )
+ {
+ endOfStream = true;
+ }
+ }
+ }
+
+ public void run()
+ {
+ try
+ {
+ while ( !endOfStream )
+ {
+ pumpStream();
+ sleep( SLEEP_TIME );
+ }
+ }
+ catch ( Exception e )
+ {
+ // getLogger().warn("Jikes.run()", e);
+ }
+ }
+}
diff --git a/maven-mboot2/src/main/java/StringUtils.java b/maven-mboot2/src/main/java/StringUtils.java
new file mode 100644
index 0000000000..2c73eeafad
--- /dev/null
+++ b/maven-mboot2/src/main/java/StringUtils.java
@@ -0,0 +1,159 @@
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+/**
+ * Common String
manipulation routines.
Originally from + * Turbine and the + * GenerationJavaCore library.
+ * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * @version $Id$ + * @since 1.0 + */ +public class StringUtils +{ + public static String[] split( String str ) + { + return split( str, null, -1 ); + } + + public static String[] split( String text, String separator ) + { + return split( text, separator, -1 ); + } + + /** + *Splits the provided text into a array, based on a given separator.
+ * + *The separator is not included in the returned String array. The
+ * maximum number of splits to perfom can be controlled. A null
+ * separator will cause parsing to be on whitespace.
This is useful for quickly splitting a String directly into
+ * an array of tokens, instead of an enumeration of tokens (as
+ * StringTokenizer
does).
null
, splits on whitespace.
+ * @param max The maximum number of elements to include in the
+ * array. A zero or negative value implies no limit.
+ * @return an array of parsed Strings
+ */
+ public static String[] split( String str, String separator, int max )
+ {
+ StringTokenizer tok = null;
+ if ( separator == null )
+ {
+ // Null separator means we're using StringTokenizer's default
+ // delimiter, which comprises all whitespace characters.
+ tok = new StringTokenizer( str );
+ }
+ else
+ {
+ tok = new StringTokenizer( str, separator );
+ }
+
+ int listSize = tok.countTokens();
+ if ( max > 0 && listSize > max )
+ {
+ listSize = max;
+ }
+
+ String[] list = new String[listSize];
+ int i = 0;
+ int lastTokenBegin = 0;
+ int lastTokenEnd = 0;
+ while ( tok.hasMoreTokens() )
+ {
+ if ( max > 0 && i == listSize - 1 )
+ {
+ // In the situation where we hit the max yet have
+ // tokens left over in our input, the last list
+ // element gets all remaining text.
+ String endToken = tok.nextToken();
+ lastTokenBegin = str.indexOf( endToken, lastTokenEnd );
+ list[i] = str.substring( lastTokenBegin );
+ break;
+ }
+ else
+ {
+ list[i] = tok.nextToken();
+ lastTokenBegin = str.indexOf( list[i], lastTokenEnd );
+ lastTokenEnd = lastTokenBegin + list[i].length();
+ }
+ i++;
+ }
+ return list;
+ }
+
+ public static String replaceOnce( String text, String repl, String with )
+ {
+ return replace( text, repl, with, 1 );
+ }
+
+ public static String replace( String text, String repl, String with )
+ {
+ return replace( text, repl, with, -1 );
+ }
+
+ public static String replace( String text, String repl, String with, int max )
+ {
+ if ( text == null || repl == null || with == null || repl.length() == 0 )
+ {
+ return text;
+ }
+
+ StringBuffer buf = new StringBuffer( text.length() );
+ int start = 0, end = 0;
+ while ( ( end = text.indexOf( repl, start ) ) != -1 )
+ {
+ buf.append( text.substring( start, end ) ).append( with );
+ start = end + repl.length();
+
+ if ( --max == 0 )
+ {
+ break;
+ }
+ }
+ buf.append( text.substring( start ) );
+ return buf.toString();
+ }
+
+
+
+ public static String interpolate( String text, Map namespace )
+ {
+ Iterator keys = namespace.keySet().iterator();
+
+ while ( keys.hasNext() )
+ {
+ String key = keys.next().toString();
+
+ Object obj = namespace.get( key );
+
+ String value = obj.toString();
+
+ text = StringUtils.replace( text, "${" + key + "}", value );
+
+ if ( key.indexOf( " " ) == -1 )
+ {
+ text = StringUtils.replace( text, "$" + key, value );
+ }
+ }
+ return text;
+ }
+}
diff --git a/maven-mboot2/src/main/java/SurefireBooter.java b/maven-mboot2/src/main/java/SurefireBooter.java
new file mode 100644
index 0000000000..11d9fd4519
--- /dev/null
+++ b/maven-mboot2/src/main/java/SurefireBooter.java
@@ -0,0 +1,183 @@
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public class SurefireBooter
+{
+ private List batteries = new ArrayList();
+
+ private List reports = new ArrayList();
+
+ private List classpathUrls = new ArrayList();
+
+ public SurefireBooter()
+ {
+ }
+
+ public void addBattery( String battery, Object[] params )
+ {
+ batteries.add( new Object[]{ battery, params } );
+ }
+
+ public void addBattery( String battery )
+ {
+ batteries.add( new Object[]{ battery, null } );
+ }
+
+ public void addReport( String report )
+ {
+ reports.add( report );
+ }
+
+ public void addClassPathUrl( String path )
+ {
+ if ( !classpathUrls.contains( path ) )
+ {
+ classpathUrls.add( path );
+ }
+ }
+
+ public boolean run()
+ throws Exception
+ {
+ IsolatedClassLoader surefireClassLoader = new IsolatedClassLoader();
+
+ for ( Iterator i = classpathUrls.iterator(); i.hasNext(); )
+ {
+ String url = (String) i.next();
+
+ if ( url != null )
+ {
+ surefireClassLoader.addURL( new File( url ).toURL() );
+ }
+ }
+
+ Class batteryExecutorClass = surefireClassLoader.loadClass( "org.codehaus.surefire.Surefire" );
+
+ Object batteryExecutor = batteryExecutorClass.newInstance();
+
+ Method run = batteryExecutorClass.getMethod( "run", new Class[] { List.class, List.class, ClassLoader.class } );
+
+ ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
+
+ Thread.currentThread().setContextClassLoader( surefireClassLoader );
+
+ Boolean result = (Boolean) run.invoke( batteryExecutor, new Object[]{ reports, batteries, surefireClassLoader } );
+
+ Thread.currentThread().setContextClassLoader( oldContextClassLoader );
+
+ return result.booleanValue();
+ }
+
+ public void reset()
+ {
+ batteries.clear();
+
+ reports.clear();
+
+ classpathUrls.clear();
+ }
+
+ // ----------------------------------------------------------------------
+ // Main
+ // ----------------------------------------------------------------------
+
+ public static void main( String[] args )
+ throws Exception
+ {
+ String basedir = args[0];
+
+ System.setProperty( "basedir", basedir );
+
+ String mavenRepoLocal = args[1];
+
+ File dependenciesFile = new File( args[2] );
+
+ List dependencies = new ArrayList();
+
+ BufferedReader buf = new BufferedReader( new FileReader( dependenciesFile ) );
+
+ String line;
+
+ while ( ( line = buf.readLine() ) != null )
+ {
+ dependencies.add( line );
+ }
+
+ buf.close();
+
+ File includesFile = new File( args[3] );
+
+ List includes = new ArrayList();
+
+ buf = new BufferedReader( new FileReader( includesFile ) );
+
+ line = buf.readLine();
+
+ String includesStr = line.substring( line.indexOf( "@" ) + 1 );
+
+ StringTokenizer st = new StringTokenizer( includesStr, "," );
+
+ while ( st.hasMoreTokens() )
+ {
+ String inc = st.nextToken().trim();
+
+ includes.add( inc );
+ }
+
+ buf.close();
+
+ File excludesFile = new File( args[4] );
+
+ List excludes = new ArrayList();
+
+ buf = new BufferedReader( new FileReader( excludesFile ) );
+
+ line = buf.readLine();
+
+ String excludesStr = line.substring( line.indexOf( "@" ) + 1 );
+
+ st = new StringTokenizer( excludesStr, "," );
+
+ while ( st.hasMoreTokens() )
+ {
+ excludes.add( st.nextToken().trim() );
+ }
+
+ buf.close();
+
+ SurefireBooter surefireBooter = new SurefireBooter();
+
+ surefireBooter.addBattery( "org.codehaus.surefire.battery.DirectoryBattery", new Object[]{ basedir, includes, excludes } );
+
+ surefireBooter.addClassPathUrl( new File( mavenRepoLocal, "junit/jars/junit-3.8.1.jar" ).getPath() );
+
+ surefireBooter.addClassPathUrl( new File( mavenRepoLocal, "surefire/jars/surefire-1.1.jar" ).getPath() );
+
+ surefireBooter.addClassPathUrl( new File( basedir, "target/classes/" ).getPath() );
+
+ surefireBooter.addClassPathUrl( new File( basedir, "target/test-classes/" ).getPath() );
+
+ processDependencies( dependencies, surefireBooter );
+
+ surefireBooter.addReport( "org.codehaus.surefire.report.ConsoleReport" );
+
+ surefireBooter.run();
+ }
+
+ private static void processDependencies( List dependencies, SurefireBooter sureFire )
+ throws Exception
+ {
+ for ( Iterator i = dependencies.iterator(); i.hasNext(); )
+ {
+ String dep = (String) i.next();
+
+ sureFire.addClassPathUrl( new File( dep ).getPath() );
+ }
+ }
+}
diff --git a/maven-mboot2/src/main/java/SurefirePlugin.java b/maven-mboot2/src/main/java/SurefirePlugin.java
new file mode 100644
index 0000000000..f61c527cd6
--- /dev/null
+++ b/maven-mboot2/src/main/java/SurefirePlugin.java
@@ -0,0 +1,38 @@
+import java.io.File;
+import java.util.List;
+
+public class SurefirePlugin
+{
+ public boolean execute( String mavenRepoLocal,
+ String basedir,
+ String classesDirectory,
+ String testClassesDirectory,
+ List includes,
+ List excludes,
+ String[] classpathElements )
+ throws Exception
+ {
+ System.setProperty( "basedir", basedir );
+
+ SurefireBooter surefireBooter = new SurefireBooter();
+
+ surefireBooter.addBattery( "org.codehaus.surefire.battery.DirectoryBattery", new Object[]{basedir, includes, excludes} );
+
+ surefireBooter.addClassPathUrl( new File( mavenRepoLocal, "junit/jars/junit-3.8.1.jar" ).getPath() );
+
+ surefireBooter.addClassPathUrl( new File( mavenRepoLocal, "surefire/jars/surefire-1.1.jar" ).getPath() );
+
+ surefireBooter.addClassPathUrl( new File( classesDirectory ).getPath() );
+
+ surefireBooter.addClassPathUrl( new File( testClassesDirectory ).getPath() );
+
+ for ( int i = 0; i < classpathElements.length; i++ )
+ {
+ surefireBooter.addClassPathUrl( classpathElements[i] );
+ }
+
+ surefireBooter.addReport( "org.codehaus.surefire.report.ConsoleReport" );
+
+ return surefireBooter.run();
+ }
+}
diff --git a/maven-mboot2/src/main/resources/mboot.deps b/maven-mboot2/src/main/resources/mboot.deps
new file mode 100644
index 0000000000..309ad68d9e
--- /dev/null
+++ b/maven-mboot2/src/main/resources/mboot.deps
@@ -0,0 +1,8 @@
+junit/jars/junit-3.8.1.jar
+surefire/jars/surefire-booter-1.1.jar
+surefire/jars/surefire-1.1.jar
+modello/jars/modello-1.0-SNAPSHOT.jar
+xpp3/jars/xpp3-1.1.3.3.jar
+xstream/jars/xstream-1.0-SNAPSHOT.jar
+qdox/jars/qdox-1.2.jar
+maven/jars/maven-plugin-2.0-SNAPSHOT.jar