Added locator reimplementation which is based on configurable stack of strategies...
authorJohn Dennis Casey <jdcasey@apache.org>
Tue, 6 Jun 2006 23:13:56 +0000 (23:13 +0000)
committerJohn Dennis Casey <jdcasey@apache.org>
Tue, 6 Jun 2006 23:13:56 +0000 (23:13 +0000)
git-svn-id: https://svn.apache.org/repos/asf/maven/shared/trunk@412235 13f79535-47bb-0310-9956-ffa450edef68

25 files changed:
pom.xml
src/main/java/org/apache/maven/shared/io/Locator.java [deleted file]
src/main/java/org/apache/maven/shared/io/download/DefaultDownloadManager.java [moved from src/main/java/org/apache/maven/shared/io/DefaultDownloadManager.java with 94% similarity]
src/main/java/org/apache/maven/shared/io/download/DownloadFailedException.java [moved from src/main/java/org/apache/maven/shared/io/DownloadFailedException.java with 91% similarity]
src/main/java/org/apache/maven/shared/io/download/DownloadManager.java [moved from src/main/java/org/apache/maven/shared/io/DownloadManager.java with 84% similarity]
src/main/java/org/apache/maven/shared/io/location/ArtifactBasedLocatorStrategy.java [new file with mode: 0644]
src/main/java/org/apache/maven/shared/io/location/ArtifactLocation.java [new file with mode: 0644]
src/main/java/org/apache/maven/shared/io/location/ClasspathResourceLocatorStrategy.java [new file with mode: 0644]
src/main/java/org/apache/maven/shared/io/location/FileBasedLocation.java [new file with mode: 0644]
src/main/java/org/apache/maven/shared/io/location/FileLocatorStrategy.java [new file with mode: 0644]
src/main/java/org/apache/maven/shared/io/location/Location.java [new file with mode: 0644]
src/main/java/org/apache/maven/shared/io/location/Locator.java [new file with mode: 0644]
src/main/java/org/apache/maven/shared/io/location/LocatorStrategy.java [new file with mode: 0644]
src/main/java/org/apache/maven/shared/io/location/URLLocation.java [new file with mode: 0644]
src/main/java/org/apache/maven/shared/io/location/UrlBasedLocatorStrategy.java [new file with mode: 0644]
src/main/java/org/apache/maven/shared/io/logging/DefaultMessageHolder.java [new file with mode: 0644]
src/main/java/org/apache/maven/shared/io/logging/MessageHolder.java [new file with mode: 0644]
src/test/java/org/apache/maven/shared/io/LocatorTest.java [deleted file]
src/test/java/org/apache/maven/shared/io/location/AbstractLocationTest.java [new file with mode: 0644]
src/test/java/org/apache/maven/shared/io/location/ArtifactLocationTest.java [new file with mode: 0644]
src/test/java/org/apache/maven/shared/io/location/FileBasedLocationTest.java [new file with mode: 0644]
src/test/java/org/apache/maven/shared/io/location/FileLocatorStrategyTest.java [new file with mode: 0644]
src/test/java/org/apache/maven/shared/io/location/LocatorTest.java [new file with mode: 0644]
src/test/java/org/apache/maven/shared/io/location/URLLocationTest.java [new file with mode: 0644]
src/test/java/org/apache/maven/shared/io/logging/DefaultMessageHolderTest.java [new file with mode: 0644]

diff --git a/pom.xml b/pom.xml
index 957d2a3..5f6a8e1 100644 (file)
--- a/pom.xml
+++ b/pom.xml
   <dependencies>
     <dependency>
       <groupId>org.apache.maven</groupId>
+      <artifactId>maven-artifact</artifactId>
+      <version>[2.0,)</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
       <artifactId>maven-artifact-manager</artifactId>
-      <version>2.0</version>
+      <version>[2.0,)</version>
     </dependency>
     <dependency>
       <groupId>org.apache.maven.wagon</groupId>
       <artifactId>wagon-provider-api</artifactId>
-      <version>1.0-alpha-6</version>
+      <version>[1.0-alpha-6,1.0]</version>
     </dependency>
     <dependency>
       <groupId>org.codehaus.plexus</groupId>
       <artifactId>plexus-utils</artifactId>
-      <version>1.0.4</version>
+      <version>[1.0.4,1.2]</version>
     </dependency>
   </dependencies>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>cobertura-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </reporting>
 </project>
diff --git a/src/main/java/org/apache/maven/shared/io/Locator.java b/src/main/java/org/apache/maven/shared/io/Locator.java
deleted file mode 100644 (file)
index 14d3b0f..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-package org.apache.maven.shared.io;
-
-/*
- * Copyright 2004-2005 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 org.codehaus.plexus.util.FileUtils;
-import org.codehaus.plexus.util.StringUtils;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.net.URL;
-
-/**
- * Performs Locator services for the <code>*Location</code> parameters in the 
- * Reports.
- *
- * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
- */
-public class Locator
-{
-    /**
-     * Create a Locator object.
-     * 
-     * @param logger the logger object to log with.
-     */
-    public Locator()
-    {
-    }
-
-    /**
-     * <p>
-     * Attempts to resolve a location parameter into a real file.
-     * </p>
-     * 
-     * <p>
-     * Checks a location string to for a resource, URL, or File that matches.
-     * If a resource or URL is found, then a local file is created with that
-     * locations contents.
-     * </p>
-     * 
-     * @param location the location string to match against.
-     * @return the File of the resolved location.
-     * @throws IOException if file is unable to be found or copied into <code>localfile</code> destination.
-     */
-    public File resolveLocation( String location )
-        throws IOException
-    {
-        File tmpFile = File.createTempFile( "locator.resolveLocation", ".result" );
-        tmpFile.deleteOnExit();
-        
-        return resolveLocation( location, tmpFile );
-    }
-
-    /**
-     * <p>
-     * Attempts to resolve a location parameter into a real file.
-     * </p>
-     * 
-     * <p>
-     * Checks a location string to for a resource, URL, or File that matches.
-     * If a resource or URL is found, then a local file is created with that
-     * locations contents.
-     * </p>
-     * 
-     * @param location the location string to match against.
-     * @param localfile the local file to use in case of resource or URL.
-     * @return the File of the resolved location.
-     * @throws IOException if file is unable to be found or copied into <code>localfile</code> destination.
-     */
-    public File resolveLocation( String location, String localfile )
-        throws IOException
-    {
-        File retFile = new File( localfile );
-        return resolveLocation( location, retFile );
-    }
-
-    /**
-     * <p>
-     * Attempts to resolve a location parameter into a real file.
-     * </p>
-     * 
-     * <p>
-     * Checks a location string to for a resource, URL, or File that matches.
-     * If a resource or URL is found, then a local file is created with that
-     * locations contents.
-     * </p>
-     * 
-     * @param location the location string to match against.
-     * @param localfile the local file to use in case of resource or URL.
-     * @return the File of the resolved location.
-     * @throws IOException if file is unable to be found or copied into <code>localfile</code> destination.
-     */
-    public File resolveLocation( String location, File localfile )
-        throws IOException
-    {
-        if ( StringUtils.isEmpty( location ) )
-        {
-            return null;
-        }
-
-        // Attempt a URL
-        if ( location.indexOf( "://" ) > 1 )
-        {
-            // Found a URL
-            URL url = new URL( location );
-            FileUtils.copyURLToFile( url, localfile );
-        }
-        else
-        {
-            // Attempt a File.
-            File fileLocation = new File( location );
-            if ( fileLocation.exists() )
-            {
-                // Found a File.
-                FileUtils.copyFile( fileLocation, localfile );
-            }
-            else
-            {
-                // Attempt a Resource.
-                URL url = this.getClass().getClassLoader().getResource( location );
-                if ( url != null )
-                {
-                    // Found a Resource.
-                    FileUtils.copyURLToFile( url, localfile );
-                }
-                else
-                {
-                    throw new IOException( "Unable to find location '" + location + "' as URL, File or Resource." );
-                }
-            }
-        }
-
-        if ( !localfile.exists() )
-        {
-            throw new FileNotFoundException( "Destination file does not exist." );
-        }
-
-        if ( localfile.length() <= 0 )
-        {
-            throw new IOException( "Destination file has no content." );
-        }
-
-        return localfile;
-    }
-}
@@ -1,4 +1,4 @@
-package org.apache.maven.shared.io;
+package org.apache.maven.shared.io.download;
 
 import java.io.File;
 import java.io.IOException;
@@ -24,17 +24,20 @@ public class DefaultDownloadManager implements DownloadManager
     
     private WagonManager wagonManager;
     
-    /* (non-Javadoc)
-     * @see org.apache.maven.shared.io.DownloadManager#download(java.lang.String)
-     */
+    public DefaultDownloadManager()
+    {
+    }
+    
+    public DefaultDownloadManager( WagonManager wagonManager )
+    {
+        this.wagonManager = wagonManager;
+    }
+    
     public File download( String url ) throws DownloadFailedException
     {
         return download( url, Collections.EMPTY_LIST );
     }
     
-    /* (non-Javadoc)
-     * @see org.apache.maven.shared.io.DownloadManager#download(java.lang.String, java.util.List)
-     */
     public File download( String url, List transferListeners ) throws DownloadFailedException
     {
         URL sourceUrl;
diff --git a/src/main/java/org/apache/maven/shared/io/location/ArtifactBasedLocatorStrategy.java b/src/main/java/org/apache/maven/shared/io/location/ArtifactBasedLocatorStrategy.java
new file mode 100644 (file)
index 0000000..1c19310
--- /dev/null
@@ -0,0 +1,103 @@
+package org.apache.maven.shared.io.location;
+
+import java.util.List;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.factory.ArtifactFactory;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
+import org.apache.maven.artifact.resolver.ArtifactResolutionException;
+import org.apache.maven.artifact.resolver.ArtifactResolver;
+import org.apache.maven.shared.io.logging.MessageHolder;
+
+public class ArtifactBasedLocatorStrategy
+    implements LocatorStrategy
+{
+    private final ArtifactFactory factory;
+
+    private final ArtifactResolver resolver;
+
+    private String defaultArtifactType = "jar";
+
+    private final ArtifactRepository localRepository;
+
+    private final List remoteRepositories;
+
+    public ArtifactBasedLocatorStrategy( ArtifactFactory factory, ArtifactResolver resolver,
+                                         ArtifactRepository localRepository, List remoteRepositories )
+    {
+        this.factory = factory;
+        this.resolver = resolver;
+        this.localRepository = localRepository;
+        this.remoteRepositories = remoteRepositories;
+    }
+
+    public ArtifactBasedLocatorStrategy( ArtifactFactory factory, ArtifactResolver resolver,
+                                         ArtifactRepository localRepository, List remoteRepositories,
+                                         String defaultArtifactType )
+    {
+        this.factory = factory;
+        this.resolver = resolver;
+        this.localRepository = localRepository;
+        this.remoteRepositories = remoteRepositories;
+        this.defaultArtifactType = defaultArtifactType;
+    }
+
+    public Location resolve( String locationSpecification, MessageHolder messageHolder )
+    {
+        String[] parts = locationSpecification.split( ":" );
+
+        Location location = null;
+
+        if ( parts.length > 2 )
+        {
+            String groupId = parts[0];
+            String artifactId = parts[1];
+            String version = parts[2];
+
+            String type = defaultArtifactType;
+            if ( parts.length > 3 )
+            {
+                type = parts[3];
+            }
+
+            String classifier = null;
+            if ( parts.length > 4 )
+            {
+                classifier = parts[4];
+            }
+
+            Artifact artifact;
+            if ( classifier == null )
+            {
+                artifact = factory.createArtifact( groupId, artifactId, version, null, type );
+            }
+            else
+            {
+                artifact = factory.createArtifactWithClassifier( groupId, artifactId, version, type, classifier );
+            }
+
+            messageHolder.append( "Resolving artifact: " + artifact.getId() );
+            try
+            {
+                resolver.resolve( artifact, remoteRepositories, localRepository );
+                
+                if ( artifact.getFile() != null )
+                {
+                    location = new ArtifactLocation( artifact, locationSpecification );
+                }
+            }
+            catch ( ArtifactResolutionException e )
+            {
+                messageHolder.append( e );
+            }
+            catch ( ArtifactNotFoundException e )
+            {
+                messageHolder.append( e );
+            }
+        }
+
+        return location;
+    }
+
+}
diff --git a/src/main/java/org/apache/maven/shared/io/location/ArtifactLocation.java b/src/main/java/org/apache/maven/shared/io/location/ArtifactLocation.java
new file mode 100644 (file)
index 0000000..952bd4f
--- /dev/null
@@ -0,0 +1,15 @@
+package org.apache.maven.shared.io.location;
+
+import org.apache.maven.artifact.Artifact;
+
+public class ArtifactLocation
+    extends FileBasedLocation
+{
+
+    public ArtifactLocation( Artifact artifact, String specification )
+    {
+        super( specification );
+        setFile( artifact.getFile() );
+    }
+
+}
diff --git a/src/main/java/org/apache/maven/shared/io/location/ClasspathResourceLocatorStrategy.java b/src/main/java/org/apache/maven/shared/io/location/ClasspathResourceLocatorStrategy.java
new file mode 100644 (file)
index 0000000..640942a
--- /dev/null
@@ -0,0 +1,48 @@
+package org.apache.maven.shared.io.location;
+
+import java.net.URL;
+
+import org.apache.maven.shared.io.logging.MessageHolder;
+
+public class ClasspathResourceLocatorStrategy
+    implements LocatorStrategy
+{
+
+    private String tempFilePrefix = "location.";
+
+    private String tempFileSuffix = ".cpurl";
+
+    private boolean tempFileDeleteOnExit = true;
+
+    public ClasspathResourceLocatorStrategy()
+    {
+    }
+
+    public ClasspathResourceLocatorStrategy( String tempFilePrefix, String tempFileSuffix, boolean tempFileDeleteOnExit )
+    {
+        this.tempFilePrefix = tempFilePrefix;
+        this.tempFileSuffix = tempFileSuffix;
+        this.tempFileDeleteOnExit = tempFileDeleteOnExit;
+    }
+
+    public Location resolve( String locationSpecification, MessageHolder messageHolder )
+    {
+        ClassLoader cloader = Thread.currentThread().getContextClassLoader();
+
+        URL resource = cloader.getResource( locationSpecification );
+
+        messageHolder.addMessage( "Resolved url: " + resource + " from classloader: " + cloader + " for location: "
+            + locationSpecification );
+        
+        Location location = null;
+
+        if ( resource != null )
+        {
+            location = new URLLocation( resource, locationSpecification, tempFilePrefix, tempFileSuffix,
+                                        tempFileDeleteOnExit );
+        }
+
+        return location;
+    }
+
+}
diff --git a/src/main/java/org/apache/maven/shared/io/location/FileBasedLocation.java b/src/main/java/org/apache/maven/shared/io/location/FileBasedLocation.java
new file mode 100644 (file)
index 0000000..9e8f261
--- /dev/null
@@ -0,0 +1,102 @@
+package org.apache.maven.shared.io.location;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+
+public class FileBasedLocation
+    implements Location
+{
+
+    private File file;
+    private FileChannel channel;
+    private final String specification;
+    
+    public FileBasedLocation( File file, String specification )
+    {
+        this.file = file;
+        this.specification = specification;
+    }
+    
+    protected FileBasedLocation( String specification )
+    {
+        this.specification = specification;
+    }
+
+    public void close()
+    {
+        if ( channel != null && channel.isOpen() )
+        {
+            try
+            {
+                channel.close();
+            }
+            catch ( IOException e )
+            {
+                //swallow it.
+            }
+        }        
+    }
+
+    public File getFile()
+        throws IOException
+    {
+        initFile();
+        
+        return unsafeGetFile();
+    }
+    
+    protected File unsafeGetFile()
+    {
+        return file;
+    }
+
+    protected void initFile()
+        throws IOException
+    {
+        // TODO: Log this in the debug log-level...
+        if ( file == null )
+        {
+            file = new File( specification );
+        }
+    }
+    
+    protected void setFile( File file )
+    {
+        if ( channel != null )
+        {
+            throw new IllegalStateException( "Location is already open; cannot setFile(..)." ); 
+        }
+        
+        this.file = file;
+    }
+
+    public String getSpecification()
+    {
+        return specification;
+    }
+
+    public void open()
+        throws IOException
+    {
+        initFile();
+        
+        channel = new FileInputStream( file ).getChannel();
+    }
+
+    public int read( ByteBuffer buffer )
+        throws IOException
+    {
+        return channel.read( buffer );
+    }
+
+    public int read( byte[] buffer )
+        throws IOException
+    {
+        return channel.read( ByteBuffer.wrap( buffer ) );
+    }
+
+}
diff --git a/src/main/java/org/apache/maven/shared/io/location/FileLocatorStrategy.java b/src/main/java/org/apache/maven/shared/io/location/FileLocatorStrategy.java
new file mode 100644 (file)
index 0000000..6307447
--- /dev/null
@@ -0,0 +1,40 @@
+package org.apache.maven.shared.io.location;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.maven.shared.io.logging.MessageHolder;
+
+public class FileLocatorStrategy
+    implements LocatorStrategy
+{
+
+    public Location resolve( String locationSpecification, MessageHolder messageHolder )
+    {
+        File file = new File( locationSpecification );
+        
+        try
+        {
+            File canFile = file.getCanonicalFile();
+            file = canFile;
+        }
+        catch ( IOException e )
+        {
+            messageHolder.addMessage( "Failed to canonicalize: " + file.getAbsolutePath(), e );
+        }
+        
+        Location location = null;
+        
+        if ( file.exists() )
+        {
+            location = new FileBasedLocation( file, locationSpecification );
+        }
+        else
+        {
+            messageHolder.addMessage( "File: " + file.getAbsolutePath() + " does not exist." );
+        }
+        
+        return location;
+    }
+
+}
diff --git a/src/main/java/org/apache/maven/shared/io/location/Location.java b/src/main/java/org/apache/maven/shared/io/location/Location.java
new file mode 100644 (file)
index 0000000..c618a50
--- /dev/null
@@ -0,0 +1,22 @@
+package org.apache.maven.shared.io.location;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public interface Location
+{
+    
+    File getFile() throws IOException;
+    
+    void open() throws IOException;
+    
+    void close();
+    
+    int read( ByteBuffer buffer ) throws IOException;
+    
+    int read( byte[] buffer ) throws IOException;
+    
+    String getSpecification();
+
+}
diff --git a/src/main/java/org/apache/maven/shared/io/location/Locator.java b/src/main/java/org/apache/maven/shared/io/location/Locator.java
new file mode 100644 (file)
index 0000000..3550599
--- /dev/null
@@ -0,0 +1,63 @@
+package org.apache.maven.shared.io.location;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.maven.shared.io.logging.DefaultMessageHolder;
+import org.apache.maven.shared.io.logging.MessageHolder;
+
+public final class Locator
+{
+    
+    private List strategies;
+    private final MessageHolder messageHolder;
+    
+    public Locator( List strategies, MessageHolder messageHolder )
+    {
+        this.messageHolder = messageHolder;
+        this.strategies = new ArrayList( strategies );
+    }
+    
+    public Locator()
+    {
+        this.messageHolder = new DefaultMessageHolder();
+        this.strategies = new ArrayList();
+    }
+    
+    public void addStrategy( LocatorStrategy strategy )
+    {
+        this.strategies.add( strategy );
+    }
+
+    public void removeStrategy( LocatorStrategy strategy )
+    {
+        this.strategies.remove( strategy );
+    }
+    
+    public void setStrategies( List strategies )
+    {
+        this.strategies.clear();
+        this.strategies.addAll( strategies );
+    }
+
+    public List getStrategies()
+    {
+        return strategies;
+    }
+    
+    public Location resolve( String locationSpecification )
+    {
+        Location location = null;
+        
+        for ( Iterator it = strategies.iterator(); location == null && it.hasNext(); )
+        {
+            LocatorStrategy strategy = (LocatorStrategy) it.next();
+            
+            location = strategy.resolve( locationSpecification, messageHolder );
+        }
+        
+        return location;
+    }
+    
+}
diff --git a/src/main/java/org/apache/maven/shared/io/location/LocatorStrategy.java b/src/main/java/org/apache/maven/shared/io/location/LocatorStrategy.java
new file mode 100644 (file)
index 0000000..ccdd589
--- /dev/null
@@ -0,0 +1,10 @@
+package org.apache.maven.shared.io.location;
+
+import org.apache.maven.shared.io.logging.MessageHolder;
+
+public interface LocatorStrategy
+{
+    
+    Location resolve( String locationSpecification, MessageHolder messageHolder );
+
+}
diff --git a/src/main/java/org/apache/maven/shared/io/location/URLLocation.java b/src/main/java/org/apache/maven/shared/io/location/URLLocation.java
new file mode 100644 (file)
index 0000000..c00930c
--- /dev/null
@@ -0,0 +1,51 @@
+package org.apache.maven.shared.io.location;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+import org.codehaus.plexus.util.FileUtils;
+
+public class URLLocation
+    extends FileBasedLocation
+{
+
+    private final URL url;
+
+    private final String tempFilePrefix;
+
+    private final String tempFileSuffix;
+
+    private final boolean tempFileDeleteOnExit;
+
+    public URLLocation( URL url, String specification, String tempFilePrefix, String tempFileSuffix,
+                        boolean tempFileDeleteOnExit )
+    {
+        super( specification );
+
+        this.url = url;
+        this.tempFilePrefix = tempFilePrefix;
+        this.tempFileSuffix = tempFileSuffix;
+        this.tempFileDeleteOnExit = tempFileDeleteOnExit;
+    }
+
+    protected void initFile()
+        throws IOException
+    {
+        // TODO: Log this in the debug log-level...
+        if ( unsafeGetFile() == null )
+        {
+            File tempFile = File.createTempFile( tempFilePrefix, tempFileSuffix );
+
+            if ( tempFileDeleteOnExit )
+            {
+                tempFile.deleteOnExit();
+            }
+
+            FileUtils.copyURLToFile( url, tempFile );
+            
+            setFile( tempFile );
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/maven/shared/io/location/UrlBasedLocatorStrategy.java b/src/main/java/org/apache/maven/shared/io/location/UrlBasedLocatorStrategy.java
new file mode 100644 (file)
index 0000000..0d32c99
--- /dev/null
@@ -0,0 +1,49 @@
+package org.apache.maven.shared.io.location;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.maven.shared.io.logging.MessageHolder;
+
+public class UrlBasedLocatorStrategy
+    implements LocatorStrategy
+{
+
+    private String tempFilePrefix = "location.";
+
+    private String tempFileSuffix = ".url";
+
+    private boolean tempFileDeleteOnExit = true;
+
+    public UrlBasedLocatorStrategy()
+    {
+    }
+
+    public UrlBasedLocatorStrategy( String tempFilePrefix, String tempFileSuffix, boolean tempFileDeleteOnExit )
+    {
+        this.tempFilePrefix = tempFilePrefix;
+        this.tempFileSuffix = tempFileSuffix;
+        this.tempFileDeleteOnExit = tempFileDeleteOnExit;
+    }
+
+    public Location resolve( String locationSpecification, MessageHolder messageHolder )
+    {
+        Location location = null;
+
+        messageHolder.append( "Building URL from location: " + locationSpecification );
+        try
+        {
+            URL url = new URL( locationSpecification );
+
+            location = new URLLocation( url, locationSpecification, tempFilePrefix, tempFileSuffix,
+                                        tempFileDeleteOnExit );
+        }
+        catch ( MalformedURLException e )
+        {
+            messageHolder.append( e );
+        }
+
+        return location;
+    }
+
+}
diff --git a/src/main/java/org/apache/maven/shared/io/logging/DefaultMessageHolder.java b/src/main/java/org/apache/maven/shared/io/logging/DefaultMessageHolder.java
new file mode 100644 (file)
index 0000000..a2d5e21
--- /dev/null
@@ -0,0 +1,156 @@
+package org.apache.maven.shared.io.logging;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+
+public class DefaultMessageHolder
+    implements MessageHolder
+{
+    
+    private List messages = new ArrayList();
+    private Message currentMessage;
+
+    public MessageHolder addMessage( CharSequence messagePart, Throwable error )
+    {
+        newMessage();
+        append( messagePart );
+        append( error );
+        
+        return this;
+    }
+
+    public MessageHolder addMessage( CharSequence messagePart )
+    {
+        newMessage();
+        append( messagePart );
+        
+        return this;
+    }
+
+    public MessageHolder addMessage( Throwable error )
+    {
+        newMessage();
+        append( error );
+        
+        return this;
+    }
+
+    public MessageHolder append( CharSequence messagePart )
+    {
+        if ( currentMessage == null )
+        {
+            newMessage();
+        }        
+        
+        currentMessage.append( messagePart );
+        
+        return this;
+    }
+
+    public MessageHolder append( Throwable error )
+    {
+        if ( currentMessage == null )
+        {
+            newMessage();
+        }        
+        
+        currentMessage.setError( error );
+        
+        return this;
+    }
+
+    public boolean isEmpty()
+    {
+        return messages.isEmpty();
+    }
+
+    public MessageHolder newMessage()
+    {
+        currentMessage = new Message();
+        messages.add( currentMessage );
+        
+        return this;
+    }
+
+    public String render()
+    {
+        StringBuffer buffer = new StringBuffer();
+        
+        int counter = 1;
+        for ( Iterator it = messages.iterator(); it.hasNext(); )
+        {
+            Message message = (Message) it.next();
+            
+            CharSequence content = message.render();
+            
+            if ( content.length() > 0 )
+            {
+                buffer.append( '[' ).append( counter++ ).append( "] " );
+                buffer.append( content );
+                
+                if ( it.hasNext() )
+                {
+                    buffer.append( "\n\n" );
+                }
+            }
+        }
+        
+        return buffer.toString();
+    }
+
+    public int size()
+    {
+        return messages.size();
+    }
+    
+    private static final class Message
+    {
+        private StringBuffer message = new StringBuffer();
+        private Throwable error;
+        
+        public Message setError( Throwable error )
+        {
+            this.error = error;
+            return this;
+        }
+        
+        public Message append( CharSequence message )
+        {
+            this.message.append( message );
+            return this;
+        }
+        
+        public CharSequence render()
+        {
+            StringBuffer buffer = new StringBuffer();
+            
+            if ( message != null && message.length() > 0 )
+            {
+                buffer.append( message );
+                
+                if ( error != null )
+                {
+                    buffer.append( '\n' );
+                }
+            }
+            
+            if ( error != null )
+            {
+                buffer.append( "Error:\n" );
+                
+                StringWriter sw = new StringWriter();
+                PrintWriter pw = new PrintWriter( sw );
+                error.printStackTrace( pw );
+                
+                buffer.append( sw.toString() );
+            }
+            
+            return buffer;
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/maven/shared/io/logging/MessageHolder.java b/src/main/java/org/apache/maven/shared/io/logging/MessageHolder.java
new file mode 100644 (file)
index 0000000..9e38b9e
--- /dev/null
@@ -0,0 +1,24 @@
+package org.apache.maven.shared.io.logging;
+
+public interface MessageHolder
+{
+    
+    MessageHolder newMessage();
+    
+    MessageHolder append( CharSequence messagePart );
+
+    MessageHolder append( Throwable error );
+    
+    MessageHolder addMessage( CharSequence messagePart, Throwable error );
+    
+    MessageHolder addMessage( CharSequence messagePart );
+
+    MessageHolder addMessage( Throwable error );
+    
+    int size();
+    
+    boolean isEmpty();
+    
+    String render();
+
+}
diff --git a/src/test/java/org/apache/maven/shared/io/LocatorTest.java b/src/test/java/org/apache/maven/shared/io/LocatorTest.java
deleted file mode 100644 (file)
index 849335d..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.apache.maven.shared.io;
-
-import java.io.IOException;
-
-import junit.framework.TestCase;
-
-public class LocatorTest
-    extends TestCase
-{
-    
-    public void testClasspathResource()
-    {
-        String url = getClass().getName().replace( '.', '/' ) + ".class";
-        
-        Locator locator = new Locator();
-        try
-        {
-            locator.resolveLocation( url );
-        }
-        catch ( IOException e )
-        {
-            e.printStackTrace();
-            fail( "Cannot resolve location for this test class." );
-        }
-    }
-
-}
diff --git a/src/test/java/org/apache/maven/shared/io/location/AbstractLocationTest.java b/src/test/java/org/apache/maven/shared/io/location/AbstractLocationTest.java
new file mode 100644 (file)
index 0000000..5a4d5ff
--- /dev/null
@@ -0,0 +1,35 @@
+package org.apache.maven.shared.io.location;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import org.codehaus.plexus.util.IOUtil;
+
+import junit.framework.TestCase;
+
+public class AbstractLocationTest
+    extends TestCase
+{
+
+    public AbstractLocationTest()
+    {
+        super();
+    }
+
+    protected void writeToFile( File file, String testStr )
+        throws IOException
+    {
+        FileWriter fw = null;
+        try
+        {
+            fw = new FileWriter( file );
+            fw.write( testStr );
+        }
+        finally
+        {
+            IOUtil.close( fw );
+        }        
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/maven/shared/io/location/ArtifactLocationTest.java b/src/test/java/org/apache/maven/shared/io/location/ArtifactLocationTest.java
new file mode 100644 (file)
index 0000000..d7b4a2b
--- /dev/null
@@ -0,0 +1,57 @@
+package org.apache.maven.shared.io.location;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.DefaultArtifact;
+import org.apache.maven.artifact.handler.DefaultArtifactHandler;
+import org.apache.maven.artifact.versioning.VersionRange;
+
+public class ArtifactLocationTest
+    extends AbstractLocationTest
+{
+    
+    public void testShouldConstructFromTempFileSpecification()
+        throws IOException
+    {
+        File f = File.createTempFile( "artifact-location.", ".test" );
+
+        Artifact a = new DefaultArtifact( "group", "artifact", VersionRange.createFromVersion( "1" ), null, "jar",
+                                          null, new DefaultArtifactHandler() );
+        
+        a.setFile( f );
+        
+        ArtifactLocation location = new ArtifactLocation( a, f.getAbsolutePath() );
+        
+        assertSame( f, location.getFile() );
+    }
+
+    public void testShouldRead()
+        throws IOException
+    {
+        File f = File.createTempFile( "url-location.", ".test" );
+
+        String testStr = "This is a test";
+
+        writeToFile( f, testStr );
+
+        Artifact a = new DefaultArtifact( "group", "artifact", VersionRange.createFromVersion( "1" ), null, "jar",
+                                          null, new DefaultArtifactHandler() );
+        
+        a.setFile( f );
+        
+        ArtifactLocation location = new ArtifactLocation( a, f.getAbsolutePath() );
+
+        location.open();
+
+        byte[] buffer = new byte[testStr.length()];
+
+        int read = location.read( buffer );
+
+        assertEquals( testStr.length(), read );
+
+        assertEquals( testStr, new String( buffer ) );
+    }
+
+}
diff --git a/src/test/java/org/apache/maven/shared/io/location/FileBasedLocationTest.java b/src/test/java/org/apache/maven/shared/io/location/FileBasedLocationTest.java
new file mode 100644 (file)
index 0000000..a45e4ae
--- /dev/null
@@ -0,0 +1,133 @@
+package org.apache.maven.shared.io.location;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public class FileBasedLocationTest
+    extends AbstractLocationTest
+{
+    
+    public void testShouldConstructWithFileThenRetrieveSameFile() throws IOException
+    {
+        File file = File.createTempFile( "test.", ".file-location" );
+        file.deleteOnExit();
+        
+        FileBasedLocation location = new FileBasedLocation( file, file.getAbsolutePath() );
+        
+        assertSame( file, location.getFile() );
+        assertEquals( file.getAbsolutePath(), location.getSpecification() );
+    }
+    
+    public void testShouldReadFileContentsUsingByteBuffer() throws IOException
+    {
+        File file = File.createTempFile( "test.", ".file-location" );
+        file.deleteOnExit();
+        
+        String testStr = "This is a test";
+        
+        writeToFile( file, testStr );
+        
+        FileBasedLocation location = new FileBasedLocation( file, file.getAbsolutePath() );
+        
+        location.open();
+        
+        ByteBuffer buffer = ByteBuffer.allocate( testStr.length() );
+        location.read( buffer );
+        
+        assertEquals( testStr, new String( buffer.array() ) );
+    }
+    
+    public void testShouldReadFileContentsUsingByteArray() throws IOException
+    {
+        File file = File.createTempFile( "test.", ".file-location" );
+        file.deleteOnExit();
+        
+        String testStr = "This is a test";
+        
+        writeToFile( file, testStr );
+        
+        FileBasedLocation location = new FileBasedLocation( file, file.getAbsolutePath() );
+        
+        location.open();
+        
+        byte[] buffer = new byte[ testStr.length() ];
+        location.read( buffer );
+        
+        assertEquals( testStr, new String( buffer ) );
+    }
+    
+    public void testShouldReadThenClose() throws IOException
+    {
+        File file = File.createTempFile( "test.", ".file-location" );
+        file.deleteOnExit();
+        
+        String testStr = "This is a test";
+        
+        writeToFile( file, testStr );
+        
+        FileBasedLocation location = new FileBasedLocation( file, file.getAbsolutePath() );
+        
+        location.open();
+        
+        byte[] buffer = new byte[ testStr.length() ];
+        location.read( buffer );
+        
+        assertEquals( testStr, new String( buffer ) );
+        
+        location.close();
+    }
+    
+    public void testShouldOpenThenFailToSetFile() throws IOException
+    {
+        File file = File.createTempFile( "test.", ".file-location" );
+        file.deleteOnExit();
+        
+        TestFileLocation location = new TestFileLocation( file.getAbsolutePath() );
+        
+        location.open();
+        
+        try
+        {
+            location.setFile( file );
+            
+            fail( "should not succeed." );
+        }
+        catch( IllegalStateException e )
+        {
+        }
+    }
+    
+    public void testShouldConstructWithoutFileThenSetFileThenOpen() throws IOException
+    {
+        File file = File.createTempFile( "test.", ".file-location" );
+        file.deleteOnExit();
+        
+        TestFileLocation location = new TestFileLocation( file.getAbsolutePath() );
+        
+        location.setFile( file );
+        location.open();
+    }
+    
+    public void testShouldConstructWithLocationThenRetrieveEquivalentFile() throws IOException
+    {
+        File file = File.createTempFile( "test.", ".file-location" );
+        file.deleteOnExit();
+        
+        Location location = new TestFileLocation( file.getAbsolutePath() );
+        
+        assertEquals( file, location.getFile() );
+        assertEquals( file.getAbsolutePath(), location.getSpecification() );
+    }
+    
+    private static final class TestFileLocation extends FileBasedLocation
+    {
+
+        TestFileLocation( String specification )
+        {
+            super( specification );
+        }
+        
+    }
+
+}
diff --git a/src/test/java/org/apache/maven/shared/io/location/FileLocatorStrategyTest.java b/src/test/java/org/apache/maven/shared/io/location/FileLocatorStrategyTest.java
new file mode 100644 (file)
index 0000000..f287695
--- /dev/null
@@ -0,0 +1,57 @@
+package org.apache.maven.shared.io.location;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.maven.shared.io.logging.DefaultMessageHolder;
+import org.apache.maven.shared.io.logging.MessageHolder;
+
+import junit.framework.TestCase;
+
+/*
+ * NOTE: Coverage reporting shows that this strategy is a bit on the low side.
+ * However, looking deeper it becomes apparent that the reason for this is the 
+ * try/catch when trying to canonicalize the file...and I haven't been able to 
+ * find a reliable way to break canonicalization. Either way, it will only change
+ * the message output, not the resulting location's reachability...so this is
+ * non-critical.
+ */
+public class FileLocatorStrategyTest
+    extends TestCase
+{
+    
+    public void testShouldResolveExistingTempFileLocation() throws IOException
+    {
+        File f = File.createTempFile( "file-locator.", ".test" );
+        f.deleteOnExit();
+        
+        FileLocatorStrategy fls = new FileLocatorStrategy();
+        
+        MessageHolder mh = new DefaultMessageHolder();
+        
+        Location location = fls.resolve( f.getAbsolutePath(), mh );
+        
+        assertNotNull( location );
+        
+        assertTrue( mh.isEmpty() );
+        
+        assertEquals( f, location.getFile() );
+    }
+
+    public void testShouldFailToResolveNonExistentFileLocation() throws IOException
+    {
+        File f = File.createTempFile( "file-locator.", ".test" );
+        f.delete();
+        
+        FileLocatorStrategy fls = new FileLocatorStrategy();
+        
+        MessageHolder mh = new DefaultMessageHolder();
+        
+        Location location = fls.resolve( f.getAbsolutePath(), mh );
+        
+        assertNull( location );
+        
+        assertEquals( 1, mh.size() );
+    }
+
+}
diff --git a/src/test/java/org/apache/maven/shared/io/location/LocatorTest.java b/src/test/java/org/apache/maven/shared/io/location/LocatorTest.java
new file mode 100644 (file)
index 0000000..5b2dbeb
--- /dev/null
@@ -0,0 +1,17 @@
+package org.apache.maven.shared.io.location;
+
+import junit.framework.TestCase;
+
+public class LocatorTest
+    extends TestCase
+{
+    
+    public void testClasspathResource()
+    {
+        String url = getClass().getName().replace( '.', '/' ) + ".class";
+        
+        Locator locator = new Locator();
+        locator.resolve( url );
+    }
+
+}
diff --git a/src/test/java/org/apache/maven/shared/io/location/URLLocationTest.java b/src/test/java/org/apache/maven/shared/io/location/URLLocationTest.java
new file mode 100644 (file)
index 0000000..b99ac25
--- /dev/null
@@ -0,0 +1,55 @@
+package org.apache.maven.shared.io.location;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+public class URLLocationTest
+    extends AbstractLocationTest
+{
+    
+    public void testShouldConstructFromUrlAndTempFileSpecifications() throws IOException
+    {
+        File f = File.createTempFile( "url-location.", ".test" );
+        
+        URL url = f.toURL();
+        
+        new URLLocation( url, f.getAbsolutePath(), "prefix.", ".suffix", true );
+    }
+    
+    public void testShouldTransferFromTempFile() throws IOException
+    {
+        File f = File.createTempFile( "url-location.", ".test" );
+        
+        URL url = f.toURL();
+        
+        URLLocation location = new URLLocation( url, f.getAbsolutePath(), "prefix.", ".suffix", true );
+        
+        assertNotNull( location.getFile() );
+        assertFalse( f.equals( location.getFile() ) );
+    }
+
+    public void testShouldTransferFromTempFileThenRead() throws IOException
+    {
+        File f = File.createTempFile( "url-location.", ".test" );
+        
+        String testStr = "This is a test";
+        
+        writeToFile( f, testStr );
+        
+        URL url = f.toURL();
+        
+        URLLocation location = new URLLocation( url, f.getAbsolutePath(), "prefix.", ".suffix", true );
+        
+        location.open();
+        
+        byte[] buffer = new byte[ testStr.length() ];
+        
+        int read = location.read( buffer );
+        
+        assertEquals( testStr.length(), read );
+        
+        assertEquals( testStr, new String( buffer ) );
+    }
+
+}
diff --git a/src/test/java/org/apache/maven/shared/io/logging/DefaultMessageHolderTest.java b/src/test/java/org/apache/maven/shared/io/logging/DefaultMessageHolderTest.java
new file mode 100644 (file)
index 0000000..a460866
--- /dev/null
@@ -0,0 +1,277 @@
+package org.apache.maven.shared.io.logging;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import junit.framework.TestCase;
+
+public class DefaultMessageHolderTest
+    extends TestCase
+{
+
+    // MessageHolder newMessage();
+    // int size();
+    // String render();
+    public void testNewMessageIncrementsSizeWhenEmpty()
+    {
+        MessageHolder mh = new DefaultMessageHolder();
+
+        assertEquals( 0, mh.size() );
+
+        MessageHolder test = mh.newMessage();
+        
+        assertSame( mh, test );
+
+        assertEquals( 1, mh.size() );
+
+        assertEquals( "", mh.render() );
+    }
+
+    // MessageHolder append( CharSequence messagePart );
+    // int size();
+    // String render();
+    public void testAppendCreatesNewMessageIfNoneCurrent()
+    {
+        MessageHolder mh = new DefaultMessageHolder();
+
+        assertEquals( 0, mh.size() );
+
+        MessageHolder test = mh.append( "test" );
+        
+        assertSame( mh, test );
+
+        assertEquals( 1, mh.size() );
+
+        assertEquals( "[1] test", mh.render() );
+    }
+
+    // MessageHolder append( Throwable error );
+    // int size();
+    // String render();
+    public void testAppendErrorCreatesNewMessageIfNoneCurrent()
+    {
+        MessageHolder mh = new DefaultMessageHolder();
+
+        assertEquals( 0, mh.size() );
+
+        NullPointerException npe = new NullPointerException();
+        
+        MessageHolder test = mh.append( npe );
+        
+        assertSame( mh, test );
+        
+        assertEquals( 1, mh.size() );
+        
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter( sw );
+        
+        npe.printStackTrace( pw );
+
+        assertEquals( "[1] Error:\n" + sw.toString(), mh.render() );
+    }
+
+    // MessageHolder newMessage();
+    // MessageHolder append( CharSequence messagePart );
+    // int size();
+    // String render();
+    public void testNewMessageThenAppendOnlyIncrementsSizeByOne()
+    {
+        MessageHolder mh = new DefaultMessageHolder();
+
+        assertEquals( 0, mh.size() );
+
+        MessageHolder test = mh.newMessage();
+        
+        assertSame( mh, test );
+        
+        test = mh.append( "test" );
+        
+        assertSame( mh, test );
+
+        assertEquals( 1, mh.size() );
+
+        assertEquals( "[1] test", mh.render() );
+    }
+
+    // MessageHolder newMessage();
+    // MessageHolder append( CharSequence messagePart );
+    // MessageHolder append( CharSequence messagePart );
+    // int size();
+    // String render();
+    public void testNewMessageThenAppendTwiceOnlyIncrementsSizeByOne()
+    {
+        MessageHolder mh = new DefaultMessageHolder();
+
+        assertEquals( 0, mh.size() );
+
+        MessageHolder test = mh.newMessage();
+        
+        assertSame( mh, test );
+        
+        test = mh.append( "test" );
+        
+        assertSame( mh, test );
+        
+        test = mh.append( " again" );
+        
+        assertSame( mh, test );
+
+        assertEquals( 1, mh.size() );
+
+        assertEquals( "[1] test again", mh.render() );
+    }
+
+    // MessageHolder newMessage();
+    // MessageHolder append( CharSequence messagePart );
+    // MessageHolder append( Throwable error );
+    // int size();
+    // String render();
+    public void testNewMessageThenAppendBothMessageAndErrorOnlyIncrementsSizeByOne()
+    {
+        MessageHolder mh = new DefaultMessageHolder();
+
+        assertEquals( 0, mh.size() );
+
+        MessageHolder test = mh.newMessage();
+        
+        assertSame( mh, test );
+        
+        test = mh.append( "test" );
+        
+        assertSame( mh, test );
+        
+        NullPointerException npe = new NullPointerException();
+        
+        test = mh.append( npe );
+        
+        assertSame( mh, test );
+
+        assertEquals( 1, mh.size() );
+        
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter( sw );
+        
+        npe.printStackTrace( pw );
+
+        assertEquals( "[1] test\nError:\n" + sw.toString(), mh.render() );
+    }
+    
+    // MessageHolder addMessage( CharSequence messagePart );
+    // int size();
+    // String render();
+    public void testAddMessageIncrementsSizeByOne()
+    {
+        MessageHolder mh = new DefaultMessageHolder();
+        MessageHolder check = mh.addMessage( "test" );
+        
+        assertSame( mh, check );
+        
+        assertEquals( 1, mh.size() );
+        assertEquals( "[1] test", mh.render() );
+    }
+    
+    // MessageHolder addMessage( CharSequence messagePart );
+    // int size();
+    // String render();
+    public void testAddMessageTwiceIncrementsSizeByTwo()
+    {
+        MessageHolder mh = new DefaultMessageHolder();
+        MessageHolder check = mh.addMessage( "test" );
+        
+        assertSame( mh, check );
+        
+        check = mh.addMessage( "test2" );
+        
+        assertSame( mh, check );
+        
+        assertEquals( 2, mh.size() );
+        assertEquals( "[1] test\n\n[2] test2", mh.render() );
+    }
+    
+    // MessageHolder addMessage( CharSequence messagePart, Throwable error );
+    // int size();
+    // String render();
+    public void testAddMessageWithErrorIncrementsSizeByOne()
+    {
+        MessageHolder mh = new DefaultMessageHolder();
+        
+        NullPointerException npe = new NullPointerException();
+        
+        MessageHolder check = mh.addMessage( "test", npe );
+        
+        assertSame( mh, check );
+        
+        assertEquals( 1, mh.size() );
+        
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter( sw );
+        
+        npe.printStackTrace( pw );
+
+        assertEquals( "[1] test\nError:\n" + sw.toString(), mh.render() );
+    }
+    
+    // MessageHolder addMessage( CharSequence messagePart, Throwable error );
+    // MessageHolder addMessage( CharSequence messagePart );
+    // int size();
+    // String render();
+    public void testAddMessageWithErrorThenWithJustMessageIncrementsSizeByTwo()
+    {
+        MessageHolder mh = new DefaultMessageHolder();
+        
+        NullPointerException npe = new NullPointerException();
+        
+        MessageHolder check = mh.addMessage( "test", npe );
+        
+        assertSame( mh, check );
+        
+        check = mh.addMessage( "test2" );
+        
+        assertSame( mh, check );
+        
+        assertEquals( 2, mh.size() );
+        
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter( sw );
+        
+        npe.printStackTrace( pw );
+
+        assertEquals( "[1] test\nError:\n" + sw.toString() + "\n\n[2] test2", mh.render() );
+    }
+    
+    // MessageHolder addMessage( Throwable error );
+    // int size();
+    // String render();
+    public void testAddMessageWithJustErrorIncrementsSizeByOne()
+    {
+        MessageHolder mh = new DefaultMessageHolder();
+        
+        NullPointerException npe = new NullPointerException();
+        
+        MessageHolder check = mh.addMessage( npe );
+        
+        assertSame( mh, check );
+        
+        assertEquals( 1, mh.size() );
+        
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter( sw );
+        
+        npe.printStackTrace( pw );
+
+        assertEquals( "[1] Error:\n" + sw.toString(), mh.render() );
+    }
+    
+    // boolean isEmpty();
+    public void testIsEmptyAfterConstruction()
+    {
+        assertTrue( new DefaultMessageHolder().isEmpty() );
+    }
+
+    // boolean isEmpty();
+    public void testIsNotEmptyAfterConstructionAndNewMessageCall()
+    {
+        assertFalse( new DefaultMessageHolder().newMessage().isEmpty() );
+    }
+    
+}