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>
+ *   <acommandline executable="/executable/to/run">
+ *     <argument value="argument 1" />
+ *     <argument line="argument_1 argument_2 argument_3" />
+ *     <argument value="argument 4" />
+ *   </acommandline>
+ * </someelement>
+ *
+ * The element 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).

+ * + * @see #createArgument(boolean) + * @return the argument object. + */ + public Argument createArgument() + { + return this.createArgument( false ); + } + + /** + * Creates an argument object and adds it to our list of args. + * + *

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 by addLine, + * 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.separators + * at the beginning of the pattern and the string to match:
+ * When a pattern starts with a 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:
+ * '*' matches zero or more characters
+ * '?' matches one character. + *

+ * Examples: + *

+ * "**\*.class" matches all .class files/dirs in a directory tree. + *

+ * "test\a??.java" matches all files/dirs which start with an 'a', then two + * more characters and then ".java", in a directory called test. + *

+ * "**" matches everything in a directory tree. + *

+ * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where + * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123"). + *

+ * Case sensitivity may be turned off if necessary. By default, it is + * turned on. + *

+ * Example of usage: + *

+ *   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:
+ * '*' means zero or more characters
+ * '?' means one and only one character + * + * @param pattern The pattern to match against. + * Must not be 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:
+ * '*' means zero or more characters
+ * '?' means one and only one character + * + * @param pattern The pattern to match against. + * Must not be 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. + *

+ *

Path-related methods

+ *

+ *

Methods exist to retrieve the components of a typical file path. For example + * /www/hosted/mysite/index.html, can be broken into: + *

+ * There are also methods to {@link #catPath concatenate two paths}, {@link #resolveFile resolve a + * path relative to a File} and {@link #normalize} a path. + *

+ *

+ *

File-related methods

+ *

+ * There are methods to create a {@link #toFile File from a URL}, copy a + * {@link #copyFileToDirectory File to a directory}, + * copy a {@link #copyFile File to another File}, + * copy a {@link #copyURLToFile URL's contents to a File}, + * as well as methods to {@link #deleteDirectory(File) delete} and {@link #cleanDirectory(File) + * clean} a directory. + *

+ *

+ * Common {@link java.io.File} manipulation routines. + *

+ * Taken from the commons-utils repo. + * Also code from Alexandria's FileUtils. + * And from Avalon Excalibur's IO. + * + * @author Kevin A. Burton + * @author Scott Sanders + * @author Daniel Rall + * @author Christoph.Reck + * @author Peter Donald + * @author Jeff Turner + * @version $Id$ + */ +public class FileUtils +{ + /** + * The number of bytes in a kilobyte. + */ + public static final int ONE_KB = 1024; + + /** + * The number of bytes in a megabyte. + */ + public static final int ONE_MB = ONE_KB * ONE_KB; + + /** + * The number of bytes in a gigabyte. + */ + public static final int ONE_GB = ONE_KB * ONE_MB; + + /** + * Returns a human-readable version of the file size (original is in + * bytes). + * + * @param size The number of bytes. + * @return A human-readable display value (includes units). + */ + public static String byteCountToDisplaySize( int size ) + { + String displaySize; + + if ( size / ONE_GB > 0 ) + { + displaySize = String.valueOf( size / ONE_GB ) + " GB"; + } + else if ( size / ONE_MB > 0 ) + { + displaySize = String.valueOf( size / ONE_MB ) + " MB"; + } + else if ( size / ONE_KB > 0 ) + { + displaySize = String.valueOf( size / ONE_KB ) + " KB"; + } + else + { + displaySize = String.valueOf( size ) + " bytes"; + } + + return displaySize; + } + + /** + * Returns the directory path portion of a file specification string. + * Matches the equally named unix command. + * + * @return The directory portion excluding the ending file separator. + */ + public static String dirname( String filename ) + { + int i = filename.lastIndexOf( File.separator ); + return ( i >= 0 ? filename.substring( 0, i ) : "" ); + } + + /** + * Returns the filename portion of a file specification string. + * + * @return The filename string with extension. + */ + public static String filename( String filename ) + { + int i = filename.lastIndexOf( File.separator ); + return ( i >= 0 ? filename.substring( i + 1 ) : filename ); + } + + /** + * Returns the filename portion of a file specification string. + * Matches the equally named unix command. + * + * @return The filename string without extension. + */ + public static String basename( String filename ) + { + return basename( filename, extension( filename ) ); + } + + /** + * Returns the filename portion of a file specification string. + * Matches the equally named unix command. + */ + public static String basename( String filename, String suffix ) + { + int i = filename.lastIndexOf( File.separator ) + 1; + int lastDot = ( ( suffix != null ) && ( suffix.length() > 0 ) ) + ? filename.lastIndexOf( suffix ) : -1; + + if ( lastDot >= 0 ) + { + return filename.substring( i, lastDot ); + } + else if ( i > 0 ) + { + return filename.substring( i ); + } + else + { + return filename; // else returns all (no path and no extension) + } + } + + /** + * Returns the extension portion of a file specification string. + * This everything after the last dot '.' in the filename (NOT including + * the dot). + */ + public static String extension( String filename ) + { + int lastDot = filename.lastIndexOf( '.' ); + + if ( lastDot >= 0 ) + { + return filename.substring( lastDot + 1 ); + } + else + { + return ""; + } + } + + /** + * Check if a file exits. + * + * @param fileName The name of the file to check. + * @return true if file exists. + */ + public static boolean fileExists( String fileName ) + { + File file = new File( fileName ); + return file.exists(); + } + + public static String fileRead( String file ) + throws IOException + { + return fileRead( new File( file ) ); + } + + public static String fileRead( File file ) + throws IOException + { + StringBuffer buf = new StringBuffer(); + + FileInputStream in = new FileInputStream( file ); + + int count; + byte[] b = new byte[512]; + while ( ( count = in.read( b ) ) > 0 ) // blocking read + { + buf.append( new String( b, 0, count ) ); + } + + in.close(); + + return buf.toString(); + } + + /** + * Writes data to a file. The file will be created if it does not exist. + * + * @param fileName The name of the file to write. + * @param data The content to write to the file. + */ + public static void fileWrite( String fileName, String data ) throws Exception + { + FileOutputStream out = new FileOutputStream( fileName ); + out.write( data.getBytes() ); + out.close(); + } + + /** + * Deletes a file. + * + * @param fileName The name of the file to delete. + */ + public static void fileDelete( String fileName ) + { + File file = new File( fileName ); + file.delete(); + } + + /** + * Waits for NFS to propagate a file creation, imposing a timeout. + * + * @param fileName The name of the file. + * @param seconds The maximum time in seconds to wait. + * @return True if file exists. + */ + public static boolean waitFor( String fileName, int seconds ) + { + File file = new File( fileName ); + int timeout = 0; + int tick = 0; + while ( !file.exists() ) + { + if ( tick++ >= 10 ) + { + tick = 0; + if ( timeout++ > seconds ) + { + return false; + } + } + try + { + Thread.sleep( 100 ); + } + catch ( InterruptedException ignore ) + { + } + catch ( Exception ex ) + { + break; + } + } + return true; + } + + /** + * Creates a file handle. + * + * @param fileName The name of the file. + * @return A 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 + * + */ + public static void copyURLToFile( final URL source, final File destination ) + throws IOException + { + //does destination 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 InputStream input = source.openStream(); + final FileOutputStream output = new FileOutputStream( destination ); + IOUtil.copy( input, output ); + + input.close(); + output.close(); + } + + /** + * Normalize a path. + * Eliminates "/../" and "/./" in a string. Returns null 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
+ *

+ *

+ * Thieved from Tomcat sources... + * + * @return The concatenated paths, or null if error occurs + */ + public static String catPath( final String lookupPath, final String path ) + { + // Cut off the last slash and everything beyond + int index = lookupPath.lastIndexOf( "/" ); + String lookup = lookupPath.substring( 0, index ); + String pth = path; + + // Deal with .. by chopping dirs off the lookup path + while ( pth.startsWith( "../" ) ) + { + if ( lookup.length() > 0 ) + { + index = lookup.lastIndexOf( "/" ); + lookup = lookup.substring( 0, index ); + } + else + { + // More ..'s than dirs, return null + return null; + } + + index = pth.indexOf( "../" ) + 3; + pth = pth.substring( index ); + } + + return new StringBuffer( lookup ).append( "/" ).append( pth ).toString(); + } + + /** + * Resolve a file 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).

+ * + * @author Peter Donald + * @author Jeff Turner + * @version CVS $Revision$ $Date$ + * @since 4.0 + */ + +/* + * Behold, intrepid explorers; a map of this class: + * + * Method Input Output Dependency + * ------ ----- ------ ------- + * 1 copy InputStream OutputStream (primitive) + * 2 copy Reader Writer (primitive) + * + * 3 copy InputStream Writer 2 + * 4 toString InputStream String 3 + * 5 toByteArray InputStream byte[] 1 + * + * 6 copy Reader OutputStream 2 + * 7 toString Reader String 2 + * 8 toByteArray Reader byte[] 6 + * + * 9 copy String OutputStream 2 + * 10 copy String Writer (trivial) + * 11 toByteArray String byte[] 9 + * + * 12 copy byte[] Writer 3 + * 13 toString byte[] String 12 + * 14 copy byte[] OutputStream (trivial) + * + * + * Note that only the first two methods shuffle bytes; the rest use these two, or (if possible) copy + * using native Java copy methods. As there are method variants to specify buffer size and encoding, + * each row may correspond to up to 4 methods. + * + */ + +public final class IOUtil +{ + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + + /** + * Private constructor to prevent instantiation. + */ + private IOUtil() + { + } + + /////////////////////////////////////////////////////////////// + // Core copy methods + /////////////////////////////////////////////////////////////// + + /** + * Copy bytes from an 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 of items to be include in the outpur + * @param baseDir the directory to add + */ + protected void addDirectory( Map includes, File baseDir ) throws IOException + { + addDirectory( includes, "", baseDir ); + } + + /** + * Add all files in the specified directory to the archive. + * + * @param includes a map of items to be include in the outpur + * @param prefix value to be added to the front of jar entry names + * @param baseDir the directory to add + */ + protected void addDirectory( Map includes, String prefix, File baseDir ) throws IOException + { + addDirectory( includes, null, null, prefix, baseDir ); + } + + /** + * Add all files in the specified directory to the archive. + * + * @param includes a map of items to be include in the outpur + * @param includesPattern Sets the list of include patterns to use + * @param excludesPattern Sets the list of exclude patterns to use + * @param prefix value to be added to the front of jar entry names + * @param baseDir the directory to add + */ + protected void addDirectory( Map includes, String includesPattern, String excludesPattern, String prefix, File baseDir ) + throws IOException + { + if ( !baseDir.exists() ) + { + return; + } + + DirectoryScanner scanner = new DirectoryScanner(); + scanner.setBasedir( baseDir ); + if ( includesPattern != null ) + { + scanner.setIncludes( StringUtils.split( includesPattern, "," ) ); + } + + if ( excludesPattern != null ) + { + scanner.setExcludes( StringUtils.split( excludesPattern, "," ) ); + } + scanner.scan(); + String[] files = scanner.getIncludedFiles(); + for ( int i = 0; i < files.length; i++ ) + { + String file = files[i]; + file = file.replace( '\\', '/' ); // todo shouldn't the scanner return platform independent names? + includes.put( prefix + file, new File( baseDir, file ) ); + } + } + + /** + * Create the jar file specified and include the listed files. + * + * @param jarFile the jar file to create + * @param includes a Mapof items to include; the key is the jar entry name + * @throws IOException if there is a problem writing the archive or reading the sources + */ + protected void createJar( File jarFile, Map includes ) throws IOException + { + File parentJarFile = jarFile.getParentFile(); + if ( !parentJarFile.exists() ) + { + parentJarFile.mkdirs(); + } + JarOutputStream jos = createJar( jarFile, createManifest() ); + try + { + addEntries( jos, includes ); + } + finally + { + jos.close(); + } + } + + /** + * Create a manifest for the jar file + * + * @return a default manifest; the Manifest-Version and Created-By attributes are initialized + */ + protected Manifest createManifest() + { + Manifest mf = new Manifest(); + Attributes attrs = mf.getMainAttributes(); + attrs.putValue( Attributes.Name.MANIFEST_VERSION.toString(), "1.0" ); + attrs.putValue( "Created-By", "2.0 (Apache Maven)" ); + return mf; + } + + /** + * Create the specified jar file and return a JarOutputStream to it + * + * @param jarFile the jar file to create + * @param mf the manifest to use + * @return a JarOutputStream that can be used to write to that file + * @throws IOException if there was a problem opening the file + */ + protected JarOutputStream createJar( File jarFile, Manifest mf ) throws IOException + { + jarFile.getParentFile().mkdirs(); + FileOutputStream fos = new FileOutputStream( jarFile ); + try + { + return new JarOutputStream( fos, mf ); + } + catch ( IOException e ) + { + try + { + fos.close(); + jarFile.delete(); + } + catch ( IOException e1 ) + { + // ignore + } + throw e; + } + } + + /** + * Add all entries in the supplied Map to the jar + * + * @param jos a JarOutputStream that can be used to write to the jar + * @param includes a Map of entries to add + * @throws IOException if there is a problem writing the archive or reading the sources + */ + protected void addEntries( JarOutputStream jos, Map includes ) throws IOException + { + for ( Iterator i = includes.entrySet().iterator(); i.hasNext(); ) + { + Map.Entry entry = (Map.Entry) i.next(); + String name = (String) entry.getKey(); + File file = (File) entry.getValue(); + addEntry( jos, name, file ); + } + } + + /** + * Add a single entry to the jar + * + * @param jos a JarOutputStream that can be used to write to the jar + * @param name the entry name to use; must be '/' delimited + * @param source the file to add + * @throws IOException if there is a problem writing the archive or reading the sources + */ + protected void addEntry( JarOutputStream jos, String name, File source ) throws IOException + { + FileInputStream fis = new FileInputStream( source ); + try + { + jos.putNextEntry( new JarEntry( name ) ); + int count; + while ( ( count = fis.read( buffer ) ) > 0 ) + { + jos.write( buffer, 0, count ); + } + jos.closeEntry(); + } + finally + { + fis.close(); + } + } + +} diff --git a/maven-mboot2/src/main/java/JavacCompiler.java b/maven-mboot2/src/main/java/JavacCompiler.java new file mode 100644 index 0000000000..68dd7feefb --- /dev/null +++ b/maven-mboot2/src/main/java/JavacCompiler.java @@ -0,0 +1,158 @@ + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; + +public class JavacCompiler + extends AbstractCompiler +{ + static final int OUTPUT_BUFFER_SIZE = 1024; + + public JavacCompiler() + { + } + + public List compile( String[] classpathElements, String[] sourceDirectories, String destinationDirectory ) + throws Exception + { + File destinationDir = new File( destinationDirectory ); + + if ( !destinationDir.exists() ) + { + destinationDir.mkdirs(); + } + + String[] sources = getSourceFiles( sourceDirectories ); + + int j = 5; + + String[] args = new String[sources.length + j]; + + args[0] = "-d"; + + args[1] = destinationDir.getAbsolutePath(); + + args[2] = "-nowarn"; + + args[3] = "-classpath"; + + args[4] = getClasspathString( classpathElements ); + + for ( int i = 0; i < sources.length; i++ ) + { + args[i + j] = sources[i]; + } + + IsolatedClassLoader cl = new IsolatedClassLoader(); + + File toolsJar = new File( System.getProperty( "java.home" ), "../lib/tools.jar" ); + + cl.addURL( toolsJar.toURL() ); + + Class c = cl.loadClass( "sun.tools.javac.Main" ); + + Constructor cons = c.getConstructor( new Class[]{OutputStream.class, String.class} ); + + ByteArrayOutputStream err = new ByteArrayOutputStream(); + + Object compiler = cons.newInstance( new Object[]{err, "javac"} ); + + Method compile = c.getMethod( "compile", new Class[]{String[].class} ); + + Boolean ok = (Boolean) compile.invoke( compiler, new Object[]{args} ); + + List messages = parseModernStream( new BufferedReader( new InputStreamReader( new ByteArrayInputStream( err.toByteArray() ) ) ) ); + + return messages; + } + + protected List parseModernStream( BufferedReader input ) + throws IOException + { + List errors = new ArrayList(); + + String line = null; + + StringBuffer buffer = null; + + while ( true ) + { + // cleanup the buffer + buffer = new StringBuffer(); // this is quicker than clearing it + + // most errors terminate with the '^' char + do + { + if ( ( line = input.readLine() ) == null ) + { + return errors; + } + + buffer.append( line ); + + buffer.append( '\n' ); + } + while ( !line.endsWith( "^" ) ); + + // add the error bean + errors.add( parseModernError( buffer.toString() ) ); + } + } + + private CompilerError parseModernError( String error ) + { + StringTokenizer tokens = new StringTokenizer( error, ":" ); + + try + { + String file = tokens.nextToken(); + + if ( file.length() == 1 ) + { + file = new StringBuffer( file ).append( ":" ).append( tokens.nextToken() ).toString(); + } + + int line = Integer.parseInt( tokens.nextToken() ); + + String message = tokens.nextToken( "\n" ).substring( 1 ); + + String context = tokens.nextToken( "\n" ); + + String pointer = tokens.nextToken( "\n" ); + + int startcolumn = pointer.indexOf( "^" ); + + int endcolumn = context.indexOf( " ", startcolumn ); + + if ( endcolumn == -1 ) + { + endcolumn = context.length(); + } + + return new CompilerError( file, false, line, startcolumn, line, endcolumn, message ); + } + catch ( NoSuchElementException nse ) + { + return new CompilerError( "no more tokens - could not parse error message: " + error ); + } + catch ( Exception nse ) + { + return new CompilerError( "could not parse error message: " + error ); + } + } + + public String toString() + { + return "Sun Javac Compiler"; + } +} diff --git a/maven-mboot2/src/main/java/MBoot.java b/maven-mboot2/src/main/java/MBoot.java new file mode 100644 index 0000000000..389c12dd1a --- /dev/null +++ b/maven-mboot2/src/main/java/MBoot.java @@ -0,0 +1,1253 @@ + +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.DefaultHandler; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.io.StringWriter; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; + +public class MBoot +{ + // I need: + // o pom install (done) + // o jar install (done) + // o bean generation - this is not entirely necessary + // o jelly generation - this is not entirely necessary + // o plugin descriptor (done) + // o xpp3/reader and writer (done) + // o compilation of generated sources in target/generated-sources (done) + // o need to run modello to generate the sources (done) + // o make this work over and over again (done) + // o hide the dependencies for mboot in the JAR (done) + + // maven-core + // maven-plugin + // maven-plugins + + // ---------------------------------------------------------------------- + // Standard locations for resources in Maven projects. + // ---------------------------------------------------------------------- + + private static final String SOURCES = "src/main/java"; + + private static final String TEST_SOURCES = "src/test/java"; + + private static final String RESOURCES = "src/main/resources"; + + private static final String TEST_RESOURCES = "src/test/resources"; + + private static final String CLASSES = "target/classes"; + + private static final String TEST_CLASSES = "target/test-classes"; + + private static final String BUILD_DIR = "target"; + + private static final String GENERATED_SOURCES = "target/generated-sources"; + + // ---------------------------------------------------------------------- + // Per-session entities which we can reuse while building many projects. + // ---------------------------------------------------------------------- + + private ArtifactDownloader downloader; + + private ModelReader reader; + + private Properties properties; + + private String repoLocal; + + private List mbootDependencies; + + private List coreDeps; + + // ---------------------------------------------------------------------- + // + // ---------------------------------------------------------------------- + + public static void main( String[] args ) + throws Exception + { + MBoot mboot = new MBoot(); + + mboot.run( args ); + } + + public void run( String[] args ) + throws Exception + { + properties = loadProperties( new File( System.getProperty( "user.home" ), "build.properties" ) ); + + downloader = new ArtifactDownloader( properties ); + + repoLocal = downloader.getMavenRepoLocal().getPath(); + + reader = new ModelReader(); + + String basedir = System.getProperty( "user.dir" ); + + checkMBootDeps(); + + File instructions = new File( basedir, "mboot.txt" ); + + if ( instructions.exists() ) + { + FileInputStream is = new FileInputStream( instructions ); + + StringWriter w = new StringWriter(); + + IOUtil.copy( is, w ); + + String[] deps = StringUtils.split( w.toString(), "\n" ); + + for ( int i = 0; i < deps.length; i++ ) + { + String directory = new File( basedir, deps[i] ).getAbsolutePath(); + + System.out.println( "Building project in " + directory + " ..." ); + + System.out.println( "--------------------------------------------------------------------" ); + + System.setProperty( "basedir", directory ); + + buildProject( directory ); + + if ( reader.artifactId.equals( "maven-core" ) ) + { + coreDeps = reader.getDependencies(); + } + + reader.reset(); + + System.out.println( "--------------------------------------------------------------------" ); + } + + // build the installation + + String mavenHome; + + if ( args.length == 1 ) + { + mavenHome = args[0]; + } + else + { + mavenHome = System.getProperty( "M2_HOME" ); + + System.out.println( "mavenHome = " + mavenHome ); + + if ( mavenHome == null ) + { + mavenHome = new File( System.getProperty( "user.home" ), "m2" ).getAbsolutePath(); + } + } + + File dist = new File( mavenHome ); + + FileUtils.deleteDirectory( dist ); + + // ---------------------------------------------------------------------- + // bin + // ---------------------------------------------------------------------- + + String bin = new File( dist, "bin" ).getAbsolutePath(); + + FileUtils.mkdir( new File( bin ).getPath() ); + + FileUtils.copyFileToDirectory( new File( basedir, "maven-core/src/bin/m2" ).getAbsolutePath(), bin ); + + FileUtils.copyFileToDirectory( new File( basedir, "maven-core/src/bin/m2.bat" ).getAbsolutePath(), bin ); + + FileUtils.copyFileToDirectory( new File( basedir, "maven-core/src/bin/classworlds.conf" ).getAbsolutePath(), bin ); + + if ( Os.isFamily( "unix" ) ) + { + Commandline cli = new Commandline(); + + cli.setExecutable( "chmod" ); + + cli.createArgument().setValue( "+x" ); + + cli.createArgument().setValue( new File( dist, "bin/m2" ).getAbsolutePath() ); + + cli.execute(); + } + + // ---------------------------------------------------------------------- + // core + // ---------------------------------------------------------------------- + + String core = new File( dist, "core" ).getAbsolutePath(); + + FileUtils.mkdir( new File( core ).getPath() ); + + FileUtils.copyFileToDirectory( repoLocal + "/classworlds/jars/classworlds-1.1-SNAPSHOT.jar", core ); + + FileUtils.copyFileToDirectory( repoLocal + "/plexus/jars/plexus-0.14-SNAPSHOT.jar", core ); + + FileUtils.copyFileToDirectory( repoLocal + "/xpp3/jars/xpp3-1.1.3.3.jar", core ); + + FileUtils.copyFileToDirectory( repoLocal + "/xstream/jars/xstream-1.0-SNAPSHOT.jar", core ); + + // ---------------------------------------------------------------------- + // lib + // ---------------------------------------------------------------------- + + String lib = new File( dist, "lib" ).getAbsolutePath(); + + FileUtils.mkdir( new File( lib ).getPath() ); + + for ( Iterator i = coreDeps.iterator(); i.hasNext(); ) + { + Dependency d = (Dependency) i.next(); + + if ( d.getArtifactId().equals( "classworlds" ) || + d.artifactId.equals( "plexus" ) || + d.artifactId.equals( "xstream" ) || + d.artifactId.equals( "xpp3" ) ) + { + continue; + } + + FileUtils.copyFileToDirectory( repoLocal + "/" + getArtifactPath( d, "/" ), lib ); + } + + // Copy maven itself + + FileUtils.copyFileToDirectory( repoLocal + "/maven/jars/maven-core-2.0-SNAPSHOT.jar", lib ); + + // ---------------------------------------------------------------------- + // plugins + // ---------------------------------------------------------------------- + + String plugins = new File( dist, "plugins" ).getAbsolutePath(); + + FileUtils.mkdir( new File( plugins ).getPath() ); + + List libs = FileUtils.getFiles( new File( basedir, "maven-plugins" ), "**/target/*.jar", null ); + + for ( Iterator i = libs.iterator(); i.hasNext(); ) + { + File f = (File) i.next(); + + FileUtils.copyFileToDirectory( f.getAbsolutePath(), plugins ); + } + } + else + { + buildProject( basedir ); + } + } + + public void buildProject( String basedir ) + throws Exception + { + System.out.println( "Building project in " + basedir ); + + if ( !reader.parse( new File( basedir, "pom.xml" ) ) ) + { + System.err.println( "Could not parse pom.xml" ); + + System.exit( 1 ); + } + + String sources = new File( basedir, SOURCES ).getAbsolutePath(); + + String resources = new File( basedir, RESOURCES ).getAbsolutePath(); + + String classes = new File( basedir, CLASSES ).getAbsolutePath(); + + String testSources = new File( basedir, TEST_SOURCES ).getAbsolutePath(); + + String testResources = new File( basedir, TEST_RESOURCES ).getAbsolutePath(); + + String testClasses = new File( basedir, TEST_CLASSES ).getAbsolutePath(); + + String generatedSources = new File( basedir, GENERATED_SOURCES ).getAbsolutePath(); + + String buildDir = new File( basedir, BUILD_DIR ).getAbsolutePath(); + + // ---------------------------------------------------------------------- + // Download deps + // ---------------------------------------------------------------------- + + System.out.println( "Downloading dependencies ..." ); + + downloadDependencies( reader.getDependencies() ); + + // ---------------------------------------------------------------------- + // Generating sources + // ---------------------------------------------------------------------- + + File model = new File( basedir, "maven.mdo" ); + + if ( model.exists() ) + { + System.out.println( "Model exists!" ); + + File generatedSourcesDirectory = new File( basedir, "target/generated-sources" ); + + if ( !generatedSourcesDirectory.exists() ) + { + generatedSourcesDirectory.mkdirs(); + } + + generateSources( model.getAbsolutePath(), "java", generatedSourcesDirectory.getAbsolutePath(), "4.0.0", "false" ); + + generateSources( model.getAbsolutePath(), "java", generatedSourcesDirectory.getAbsolutePath(), "3.0.0", "true" ); + + generateSources( model.getAbsolutePath(), "xpp3", generatedSourcesDirectory.getAbsolutePath(), "4.0.0", "false" ); + + generateSources( model.getAbsolutePath(), "xpp3", generatedSourcesDirectory.getAbsolutePath(), "3.0.0", "true" ); + } + + // ---------------------------------------------------------------------- + // Standard compile + // ---------------------------------------------------------------------- + + System.out.println( "Compiling sources ..." ); + + if ( new File( generatedSources ).exists() ) + { + compile( reader.getDependencies(), sources, classes, null, generatedSources ); + } + else + { + compile( reader.getDependencies(), sources, classes, null, null ); + } + + // ---------------------------------------------------------------------- + // Plugin descriptor generation + // ---------------------------------------------------------------------- + + if ( reader.type != null && reader.type.equals( "plugin" ) ) + { + System.out.println( "Generating maven plugin descriptor ..." ); + + generatePluginDescriptor( sources, + new File( classes, "META-INF/maven" ).getAbsolutePath(), + new File( basedir, "pom.xml" ).getAbsolutePath() ); + } + + // ---------------------------------------------------------------------- + // Standard resources + // ---------------------------------------------------------------------- + + System.out.println( "Packaging resources ..." ); + + copyResources( resources, classes ); + + // ---------------------------------------------------------------------- + // Test compile + // ---------------------------------------------------------------------- + + System.out.println( "Compiling test sources ..." ); + + compile( reader.getDependencies(), testSources, testClasses, basedir + "/target/classes", null ); + + // ---------------------------------------------------------------------- + // Test resources + // ---------------------------------------------------------------------- + + System.out.println( "Packaging test resources ..." ); + + copyResources( testResources, testClasses ); + + // ---------------------------------------------------------------------- + // Run tests + // ---------------------------------------------------------------------- + + runTests( basedir, classes, testClasses ); + + // ---------------------------------------------------------------------- + // Create JAR + // ---------------------------------------------------------------------- + + createJar( classes, buildDir ); + + installPom( basedir, repoLocal ); + + installJar( basedir, repoLocal ); + } + + private void generatePluginDescriptor( String sourceDirectory, String outputDirectory, String pom ) + throws Exception + { + IsolatedClassLoader cl = new IsolatedClassLoader(); + + for ( Iterator i = mbootDependencies.iterator(); i.hasNext(); ) + { + String dependency = (String) i.next(); + + File f = new File( repoLocal, dependency ); + + cl.addURL( f.toURL() ); + } + + Class c = cl.loadClass( "org.apache.maven.plugin.generator.PluginDescriptorGenerator" ); + + Object generator = c.newInstance(); + + Method m = c.getMethod( "execute", new Class[]{String.class, String.class, String.class} ); + + m.invoke( generator, new Object[]{sourceDirectory, outputDirectory, pom} ); + } + + private void generateSources( String model, String mode, String dir, String modelVersion, String packageWithVersion ) + throws Exception + { + IsolatedClassLoader cl = new IsolatedClassLoader(); + + for ( Iterator i = mbootDependencies.iterator(); i.hasNext(); ) + { + String dependency = (String) i.next(); + + File f = new File( repoLocal, dependency ); + + cl.addURL( f.toURL() ); + } + + Class c = cl.loadClass( "org.codehaus.modello.Modello" ); + + Object generator = c.newInstance(); + + Method m = c.getMethod( "main", new Class[]{String[].class} ); + + String[] args = new String[]{model, mode, dir, modelVersion, packageWithVersion}; + + ClassLoader old = Thread.currentThread().getContextClassLoader(); + + Thread.currentThread().setContextClassLoader( cl ); + + m.invoke( generator, new Object[]{args} ); + + Thread.currentThread().setContextClassLoader( old ); + } + + private void checkMBootDeps() + throws Exception + { + System.out.println( "Checking for MBoot's dependencies ..." ); + + InputStream is = MBoot.class.getClassLoader().getResourceAsStream( "mboot.deps" ); + + StringWriter w = new StringWriter(); + + IOUtil.copy( is, w ); + + String[] deps = StringUtils.split( w.toString(), "\n" ); + + mbootDependencies = Arrays.asList( deps ); + + downloader.downloadDependencies( mbootDependencies ); + } + + private void createJar( String classes, String buildDir ) + throws Exception + { + JarMojo jarMojo = new JarMojo(); + + String artifactId = reader.artifactId; + + String version = reader.version; + + jarMojo.execute( new File( classes ), buildDir, artifactId + "-" + version ); + } + + private void installPom( String basedir, String repoLocal ) + throws Exception + { + String artifactId = reader.artifactId; + + String version = reader.version; + + String groupId = reader.groupId; + + FileUtils.copyFile( new File( basedir, "pom.xml" ), + new File( repoLocal, "/" + groupId + "/poms/" + artifactId + "-" + version + ".pom" ) ); + } + + private void installJar( String basedir, String repoLocal ) + throws Exception + { + String artifactId = reader.artifactId; + + String version = reader.version; + + String groupId = reader.groupId; + + FileUtils.copyFile( new File( basedir, BUILD_DIR + "/" + artifactId + "-" + version + ".jar" ), + new File( repoLocal, "/" + groupId + "/jars/" + artifactId + "-" + version + ".jar" ) ); + } + + private void runTests( String basedir, String classes, String testClasses ) + throws Exception + { + SurefirePlugin testRunner = new SurefirePlugin(); + + List includes; + + List excludes; + + if ( reader.getUnitTests() != null ) + { + includes = reader.getUnitTests().getIncludes(); + + excludes = reader.getUnitTests().getExcludes(); + } + else + { + includes = new ArrayList(); + + includes.add( "**/*Test.java" ); + + excludes = new ArrayList(); + + excludes.add( "**/*Abstract*.java" ); + } + + testRunner.execute( repoLocal, basedir, classes, testClasses, includes, excludes, classpath( reader.getDependencies(), null ) ); + + } + + // ---------------------------------------------------------------------- + // Download dependencies + // ---------------------------------------------------------------------- + + private void downloadDependencies( List dependencies ) + throws Exception + { + List list = new ArrayList(); + + for ( Iterator i = dependencies.iterator(); i.hasNext(); ) + { + Dependency d = (Dependency) i.next(); + + list.add( getArtifactPath( d, "/" ) ); + } + + downloader.downloadDependencies( list ); + } + + // ---------------------------------------------------------------------- + // Compile + // ---------------------------------------------------------------------- + + private String[] classpath( List dependencies, String extraClasspath ) + { + String classpath[] = new String[dependencies.size() + 1]; + + for ( int i = 0; i < dependencies.size(); i++ ) + { + Dependency d = (Dependency) dependencies.get( i ); + + classpath[i] = repoLocal + "/" + getArtifactPath( d, "/" ); + } + + classpath[classpath.length - 1] = extraClasspath; + + return classpath; + } + + private void compile( List dependencies, + String sourceDirectory, + String outputDirectory, + String extraClasspath, + String generatedSources ) + throws Exception + { + JavacCompiler compiler = new JavacCompiler(); + + String[] sourceDirectories = null; + + if ( generatedSources != null ) + { + // We might only have generated sources + + if ( new File( sourceDirectory ).exists() ) + { + sourceDirectories = new String[]{sourceDirectory, generatedSources}; + } + else + { + sourceDirectories = new String[]{generatedSources}; + } + } + else + { + if ( new File( sourceDirectory ).exists() ) + { + + sourceDirectories = new String[]{sourceDirectory}; + } + } + + if ( sourceDirectories != null ) + { + List errors = compiler.compile( classpath( dependencies, extraClasspath ), sourceDirectories, outputDirectory ); + + for ( Iterator i = errors.iterator(); i.hasNext(); ) + { + System.out.println( i.next() ); + } + } + } + + // ---------------------------------------------------------------------- + // Resource copying + // ---------------------------------------------------------------------- + + private void copyResources( String sourceDirectory, String destinationDirectory ) + throws Exception + { + File sd = new File( sourceDirectory ); + + if ( !sd.exists() ) + { + return; + } + + List files = FileUtils.getFiles( sd, "**/**", "**/CVS/**", false ); + + for ( Iterator i = files.iterator(); i.hasNext(); ) + { + File f = (File) i.next(); + + File source = new File( sourceDirectory, f.getPath() ); + + File dest = new File( destinationDirectory, f.getPath() ); + + if ( !dest.getParentFile().exists() ) + { + dest.getParentFile().mkdirs(); + } + + FileUtils.copyFile( source, dest ); + } + } + + private String getArtifactPath( Dependency d, String pathSeparator ) + { + return d.getArtifactDirectory() + pathSeparator + "jars" + pathSeparator + d.getArtifact(); + } + + private Properties loadProperties( File file ) + { + try + { + return loadProperties( new FileInputStream( file ) ); + } + catch ( Exception e ) + { + // ignore + } + + return new Properties(); + } + + private static Properties loadProperties( InputStream is ) + { + Properties properties = new Properties(); + + try + { + if ( is != null ) + { + properties.load( is ); + } + } + catch ( IOException e ) + { + // ignore + } + finally + { + try + { + if ( is != null ) + { + is.close(); + } + } + catch ( IOException e ) + { + // ignore + } + } + + return properties; + } + + class ModelReader + extends DefaultHandler + { + String artifactId; + + String version; + + String groupId; + + String type; + + private String parentGroupId; + + private String parentArtifactId; + + private String parentVersion; + + private List dependencies = new ArrayList(); + + private UnitTests unitTests; + + private List resources = new ArrayList(); + + private Dependency currentDependency; + + private Resource currentResource; + + private SAXParserFactory saxFactory; + + private boolean insideParent = false; + + private boolean insideDependency = false; + + private boolean insideUnitTest = false; + + private boolean insideResource = false; + + private StringBuffer bodyText = new StringBuffer(); + + private File file; + + public void reset() + { + dependencies = new ArrayList(); + } + + public List getDependencies() + { + return dependencies; + } + + public UnitTests getUnitTests() + { + return unitTests; + } + + public List getResources() + { + return resources; + } + + public boolean parse( File file ) + { + this.file = file; + + try + { + saxFactory = SAXParserFactory.newInstance(); + + SAXParser parser = saxFactory.newSAXParser(); + + InputSource is = new InputSource( new FileInputStream( file ) ); + + parser.parse( is, this ); + + return true; + } + catch ( Exception e ) + { + e.printStackTrace(); + + return false; + } + } + + public void startElement( String uri, String localName, String rawName, Attributes attributes ) + { + if ( rawName.equals( "parent" ) ) + { + insideParent = true; + } + else if ( rawName.equals( "unitTest" ) ) + { + unitTests = new UnitTests(); + + insideUnitTest = true; + } + else if ( rawName.equals( "dependency" ) ) + { + currentDependency = new Dependency(); + + insideDependency = true; + } + else if ( rawName.equals( "resource" ) ) + { + currentResource = new Resource(); + + insideResource = true; + } + } + + public void characters( char buffer[], int start, int length ) + { + bodyText.append( buffer, start, length ); + } + + private String getBodyText() + { + return bodyText.toString().trim(); + } + + public void endElement( String uri, String localName, String rawName ) + throws SAXException + { + // support both v3 and v4 + if ( rawName.equals( "parent" ) ) + { + File f; + + if ( parentArtifactId == null || parentArtifactId.trim().length() == 0 ) + { + throw new SAXException( "Missing required element in : artifactId." ); + } + + if ( parentGroupId == null || parentGroupId.trim().length() == 0 ) + { + throw new SAXException( "Missing required element in : groupId." ); + } + + if ( parentVersion == null || parentVersion.trim().length() == 0 ) + { + throw new SAXException( "Missing required element in : version." ); + } + + f = new File( downloader.getMavenRepoLocal(), parentGroupId + "/poms/" + parentArtifactId + "-" + parentVersion + ".pom" ); + + ModelReader p = new ModelReader(); + + if ( !p.parse( f ) ) + { + throw new SAXException( "Could not parse parent pom.xml" ); + } + + dependencies.addAll( p.getDependencies() ); + + unitTests = p.getUnitTests(); + + resources.addAll( p.getResources() ); + + insideParent = false; + } + else if ( rawName.equals( "unitTest" ) ) + { + insideUnitTest = false; + } + else if ( rawName.equals( "dependency" ) ) + { + dependencies.add( currentDependency ); + + insideDependency = false; + } + else if ( rawName.equals( "resource" ) ) + { + if ( insideUnitTest ) + { + unitTests.addResource( currentResource ); + } + else + { + resources.add( currentResource ); + } + + insideResource = false; + } + else if ( insideParent ) + { + if ( rawName.equals( "groupId" ) ) + { + parentGroupId = getBodyText(); + } + else if ( rawName.equals( "artifactId" ) ) + { + parentArtifactId = getBodyText(); + } + else if ( rawName.equals( "version" ) ) + { + parentVersion = getBodyText(); + } + } + else if ( insideDependency ) + { + if ( rawName.equals( "id" ) ) + { + currentDependency.setId( getBodyText() ); + } + else if ( rawName.equals( "version" ) ) + { + currentDependency.setVersion( getBodyText() ); + } + else if ( rawName.equals( "jar" ) ) + { + currentDependency.setJar( getBodyText() ); + } + else if ( rawName.equals( "type" ) ) + { + currentDependency.setType( getBodyText() ); + } + else if ( rawName.equals( "groupId" ) ) + { + currentDependency.setGroupId( getBodyText() ); + } + else if ( rawName.equals( "artifactId" ) ) + { + currentDependency.setArtifactId( getBodyText() ); + } + } + else if ( insideResource ) + { + if ( rawName.equals( "directory" ) ) + { + currentResource.setDirectory( getBodyText() ); + } + else if ( rawName.equals( "targetPath" ) ) + { + currentResource.setTargetPath( getBodyText() ); + } + else if ( rawName.equals( "include" ) ) + { + currentResource.addInclude( getBodyText() ); + } + else if ( rawName.equals( "exclude" ) ) + { + currentResource.addExclude( getBodyText() ); + } + } + else if ( !insideResource && insideUnitTest ) + { + if ( rawName.equals( "include" ) ) + { + unitTests.addInclude( getBodyText() ); + } + else if ( rawName.equals( "exclude" ) ) + { + unitTests.addExclude( getBodyText() ); + } + } + else if ( rawName.equals( "artifactId" ) ) + { + artifactId = getBodyText(); + } + else if ( rawName.equals( "version" ) ) + { + version = getBodyText(); + } + else if ( rawName.equals( "groupId" ) ) + { + groupId = getBodyText(); + } + else if ( rawName.equals( "type" ) ) + { + type = getBodyText(); + } + + bodyText = new StringBuffer(); + } + + public void warning( SAXParseException spe ) + { + printParseError( "Warning", spe ); + } + + public void error( SAXParseException spe ) + { + printParseError( "Error", spe ); + } + + public void fatalError( SAXParseException spe ) + { + printParseError( "Fatal Error", spe ); + } + + private final void printParseError( String type, SAXParseException spe ) + { + System.err.println( type + " [line " + spe.getLineNumber() + + ", row " + spe.getColumnNumber() + "]: " + + spe.getMessage() ); + } + } + + public static class Dependency + { + private String id; + + private String version; + + private String url; + + private String jar; + + private String artifactId; + + private String groupId; + + private String type = "jar"; + + public Dependency() + { + } + + public void setId( String id ) + { + this.id = id; + } + + public String getId() + { + if ( isValid( getGroupId() ) + && isValid( getArtifactId() ) ) + { + return getGroupId() + ":" + getArtifactId(); + } + + return id; + } + + public void setGroupId( String groupId ) + { + this.groupId = groupId; + } + + public String getGroupId() + { + return groupId; + } + + public String getArtifactDirectory() + { + if ( isValid( getGroupId() ) ) + { + return getGroupId(); + } + + return getId(); + } + + public String getArtifactId() + { + return artifactId; + } + + public void setArtifactId( String artifactId ) + { + this.artifactId = artifactId; + } + + public String getArtifact() + { + // If the jar name has been explicty set then use that. This + // is when the element is explicity used in the POM. + if ( jar != null ) + { + return jar; + } + + if ( isValid( getArtifactId() ) ) + { + return getArtifactId() + "-" + getVersion() + "." + getType(); + } + else + { + return getId() + "-" + getVersion() + "." + getType(); + } + } + + public void setVersion( String version ) + { + this.version = version; + } + + public String getVersion() + { + return version; + } + + public void setJar( String jar ) + { + // This is a check we need because of the jelly interpolation + // process. If we don't check an empty string will be set and + // screw up getArtifact() above. + if ( jar.trim().length() == 0 ) + { + return; + } + + this.jar = jar; + } + + public String getJar() + { + return jar; + } + + public void setUrl( String url ) + { + this.url = url; + } + + public String getUrl() + { + return url; + } + + public String getType() + { + return type; + } + + public void setType( String type ) + { + this.type = type; + } + + private boolean isValid( String value ) + { + if ( value != null + && value.trim().equals( "" ) == false ) + { + return true; + } + + return false; + } + } + + public static class UnitTests + implements Serializable + { + private String directory; + + private List unitTestIncludes = new ArrayList(); + + private List unitTestExcludes = new ArrayList(); + + private List unitTestResources = new ArrayList(); + + public void addInclude( String pattern ) + { + unitTestIncludes.add( pattern ); + } + + public void addExclude( String pattern ) + { + unitTestExcludes.add( pattern ); + } + + public void addResource( Resource resource ) + { + unitTestResources.add( resource ); + } + + public List getIncludes() + { + return unitTestIncludes; + } + + public List getExcludes() + { + return unitTestExcludes; + } + + public List getResources() + { + return unitTestResources; + } + + public void setDirectory( String directory ) + { + this.directory = directory; + } + + public String getDirectory() + { + return this.directory; + } + } + + public static class Resource + implements Serializable + { + private String directory; + + private String targetPath; + + private List includes = new ArrayList(); + + private List excludes = new ArrayList(); + + private boolean filtering; + + public void addInclude( String pattern ) + { + this.includes.add( pattern ); + } + + public void addExclude( String pattern ) + { + this.excludes.add( pattern ); + } + + public List getIncludes() + { + return this.includes; + } + + public List getExcludes() + { + return this.excludes; + } + + public void setDirectory( String directory ) + { + this.directory = directory; + } + + public String getDirectory() + { + return this.directory; + } + + public void setTargetPath( String targetPath ) + { + this.targetPath = targetPath; + } + + public String getTargetPath() + { + return targetPath; + } + + public boolean getFiltering() + { + return filtering; + } + + public void setFiltering( boolean filtering ) + { + this.filtering = filtering; + } + } +} diff --git a/maven-mboot2/src/main/java/Os.java b/maven-mboot2/src/main/java/Os.java new file mode 100644 index 0000000000..b6e18d24d9 --- /dev/null +++ b/maven-mboot2/src/main/java/Os.java @@ -0,0 +1,253 @@ +import java.util.Locale; + +/** + * Condition that tests the OS type. + * + * @author Stefan Bodewig + * @author Magesh Umasankar + * @since Ant 1.4 + * @version $Revision$ + */ +public class Os +{ + private static final String OS_NAME = System.getProperty( "os.name" ).toLowerCase( Locale.US ); + + private static final String OS_ARCH = System.getProperty( "os.arch" ).toLowerCase( Locale.US ); + + private static final String OS_VERSION = System.getProperty( "os.version" ).toLowerCase( Locale.US ); + + private static final String PATH_SEP = System.getProperty( "path.separator" ); + + private String family; + private String name; + private String version; + private String arch; + + /** + * Default constructor + * + */ + public Os() + { + } + + /** + * Constructor that sets the family attribute + * + * @param family a String value + */ + public Os( String family ) + { + setFamily( family ); + } + + /** + * Sets the desired OS family type + * + * @param f The OS family type desired
+ * Possible values:
+ *
    + *
  • dos
  • + *
  • mac
  • + *
  • netware
  • + *
  • os/2
  • + *
  • tandem
  • + *
  • unix
  • + *
  • windows
  • + *
  • win9x
  • + *
  • z/os
  • + *
  • os/400
  • + *
+ */ + public void setFamily( String f ) + { + family = f.toLowerCase( Locale.US ); + } + + /** + * Sets the desired OS name + * + * @param name The OS name + */ + public void setName( String name ) + { + this.name = name.toLowerCase( Locale.US ); + } + + /** + * Sets the desired OS architecture + * + * @param arch The OS architecture + */ + public void setArch( String arch ) + { + this.arch = arch.toLowerCase( Locale.US ); + } + + /** + * Sets the desired OS version + * + * @param version The OS version + */ + public void setVersion( String version ) + { + this.version = version.toLowerCase( Locale.US ); + } + + /** + * Determines if the OS on which Ant is executing matches the type of + * that set in setFamily. + * @see Os#setFamily(String) + */ + public boolean eval() throws Exception + { + return isOs( family, name, arch, version ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family. + * @param family the family to check for + * @return true if the OS matches + * @since 1.5 + */ + public static boolean isFamily( String family ) + { + return isOs( family, null, null, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS name. + * + * @param name the OS name to check for + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isName( String name ) + { + return isOs( null, name, null, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS architecture. + * + * @param arch the OS architecture to check for + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isArch( String arch ) + { + return isOs( null, null, arch, null ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS version. + * + * @param version the OS version to check for + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isVersion( String version ) + { + return isOs( null, null, null, version ); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family, name, architecture and version + * + * @param family The OS family + * @param name The OS name + * @param arch The OS architecture + * @param version The OS version + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isOs( String family, String name, String arch, + String version ) + { + boolean retValue = false; + + if ( family != null || name != null || arch != null + || version != null ) + { + + boolean isFamily = true; + boolean isName = true; + boolean isArch = true; + boolean isVersion = true; + + if ( family != null ) + { + if ( family.equals( "windows" ) ) + { + isFamily = OS_NAME.indexOf( "windows" ) > -1; + } + else if ( family.equals( "os/2" ) ) + { + isFamily = OS_NAME.indexOf( "os/2" ) > -1; + } + else if ( family.equals( "netware" ) ) + { + isFamily = OS_NAME.indexOf( "netware" ) > -1; + } + else if ( family.equals( "dos" ) ) + { + isFamily = PATH_SEP.equals( ";" ) && !isFamily( "netware" ); + } + else if ( family.equals( "mac" ) ) + { + isFamily = OS_NAME.indexOf( "mac" ) > -1; + } + else if ( family.equals( "tandem" ) ) + { + isFamily = OS_NAME.indexOf( "nonstop_kernel" ) > -1; + } + else if ( family.equals( "unix" ) ) + { + isFamily = PATH_SEP.equals( ":" ) + && !isFamily( "openvms" ) + && ( !isFamily( "mac" ) || OS_NAME.endsWith( "x" ) ); + } + else if ( family.equals( "win9x" ) ) + { + isFamily = isFamily( "windows" ) + && ( OS_NAME.indexOf( "95" ) >= 0 + || OS_NAME.indexOf( "98" ) >= 0 + || OS_NAME.indexOf( "me" ) >= 0 + || OS_NAME.indexOf( "ce" ) >= 0 ); + } + else if ( family.equals( "z/os" ) ) + { + isFamily = OS_NAME.indexOf( "z/os" ) > -1 + || OS_NAME.indexOf( "os/390" ) > -1; + } + else if ( family.equals( "os/400" ) ) + { + isFamily = OS_NAME.indexOf( "os/400" ) > -1; + } + else if ( family.equals( "openvms" ) ) + { + isFamily = OS_NAME.indexOf( "openvms" ) > -1; + } + } + if ( name != null ) + { + isName = name.equals( OS_NAME ); + } + if ( arch != null ) + { + isArch = arch.equals( OS_ARCH ); + } + if ( version != null ) + { + isVersion = version.equals( OS_VERSION ); + } + retValue = isFamily && isName && isArch && isVersion; + } + return retValue; + } +} diff --git a/maven-mboot2/src/main/java/SelectorUtils.java b/maven-mboot2/src/main/java/SelectorUtils.java new file mode 100644 index 0000000000..e9212548d2 --- /dev/null +++ b/maven-mboot2/src/main/java/SelectorUtils.java @@ -0,0 +1,609 @@ + +import java.io.File; +import java.util.StringTokenizer; +import java.util.Vector; + +/** + *

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:
+ * '*' means zero or more characters
+ * '?' means one and only one character + * + * @param pattern The pattern to match against. + * Must not be 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:
+ * '*' means zero or more characters
+ * '?' means one and only one character + * + * @param pattern The pattern to match against. + * Must not be 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).

+ * + * @param str The string to parse. + * @param separator Characters used as the delimiters. If + * 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