[maven-release-plugin]  copy for tag wagon-1.0-beta-3

git-svn-id: https://svn.apache.org/repos/asf/maven/wagon/tags/wagon-1.0-beta-3@662999 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index 65337db..cb79d34 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,27 +1,4 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one
-  ~ or more contributor license agreements.  See the NOTICE file
-  ~ distributed with this work for additional information
-  ~ regarding copyright ownership.  The ASF licenses this file
-  ~ to you 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
     <artifactId>maven-parent</artifactId>
@@ -33,7 +10,7 @@
   <artifactId>wagon</artifactId>
   <packaging>pom</packaging>
   <name>Maven Wagon</name>
-  <version>1.0-rc1-SNAPSHOT</version>
+  <version>1.0-beta-3</version>
   <description>Tools to manage artifacts and deployment</description>
   <url>http://maven.apache.org/wagon</url>
 
@@ -89,14 +66,18 @@
     </developer>
   </developers>
   <scm>
-    <connection>scm:svn:http://svn.apache.org/repos/asf/maven/wagon/trunk</connection>
-    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/maven/wagon/trunk</developerConnection>
-    <url>http://svn.apache.org/viewvc/maven/wagon/trunk</url>
+    <connection>scm:svn:http://svn.apache.org/repos/asf/maven/wagon/tags/wagon-1.0-beta-3</connection>
+    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/maven/wagon/tags/wagon-1.0-beta-3</developerConnection>
+    <url>http://svn.apache.org/viewvc/maven/wagon/tags/wagon-1.0-beta-3</url>
   </scm>
   <build>
     <pluginManagement>
       <plugins>
         <plugin>
+          <artifactId>maven-javadoc-plugin</artifactId>
+          <version>2.4</version>
+        </plugin>
+        <plugin>
           <artifactId>maven-release-plugin</artifactId>
           <configuration>
             <tagBase>https://svn.apache.org/repos/asf/maven/wagon/tags</tagBase>
@@ -152,22 +133,22 @@
       <dependency>
         <groupId>org.apache.maven.wagon</groupId>
         <artifactId>wagon-provider-api</artifactId>
-        <version>1.0-rc1-SNAPSHOT</version>
+        <version>1.0-beta-3</version>
       </dependency>
       <dependency>
         <groupId>org.apache.maven.wagon</groupId>
         <artifactId>wagon-provider-test</artifactId>
-        <version>1.0-rc1-SNAPSHOT</version>
+        <version>1.0-beta-3</version>
       </dependency>
       <dependency>
         <groupId>org.apache.maven.wagon</groupId>
         <artifactId>wagon-ssh-common-test</artifactId>
-        <version>1.0-rc1-SNAPSHOT</version>
+        <version>1.0-beta-3</version>
       </dependency>
       <dependency>
         <groupId>org.apache.maven.wagon</groupId>
         <artifactId>wagon-ssh-common</artifactId>
-        <version>1.0-rc1-SNAPSHOT</version>
+        <version>1.0-beta-3</version>
       </dependency>
       <dependency>
         <groupId>junit</groupId>
@@ -268,4 +249,4 @@
       <name>Kohsuke Kawaguchi</name>
     </contributor>
   </contributors>
-</project>
+</project>
\ No newline at end of file
diff --git a/wagon-provider-api/pom.xml b/wagon-provider-api/pom.xml
index 59fa1af..ca58034 100644
--- a/wagon-provider-api/pom.xml
+++ b/wagon-provider-api/pom.xml
@@ -1,31 +1,8 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one
-  ~ or more contributor license agreements.  See the NOTICE file
-  ~ distributed with this work for additional information
-  ~ regarding copyright ownership.  The ASF licenses this file
-  ~ to you 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <artifactId>wagon</artifactId>
     <groupId>org.apache.maven.wagon</groupId>
-    <version>1.0-rc1-SNAPSHOT</version>
+    <version>1.0-beta-3</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>wagon-provider-api</artifactId>
@@ -36,5 +13,11 @@
       <groupId>org.codehaus.plexus</groupId>
       <artifactId>plexus-utils</artifactId>
     </dependency>
+    <dependency>
+      <groupId>easymock</groupId>
+      <artifactId>easymock</artifactId>
+      <version>1.2_Java1.3</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
-</project>
+</project>
\ No newline at end of file
diff --git a/wagon-provider-api/src/main/java/org/apache/maven/wagon/AbstractWagon.java b/wagon-provider-api/src/main/java/org/apache/maven/wagon/AbstractWagon.java
index 738154a..61dd1c3 100644
--- a/wagon-provider-api/src/main/java/org/apache/maven/wagon/AbstractWagon.java
+++ b/wagon-provider-api/src/main/java/org/apache/maven/wagon/AbstractWagon.java
@@ -19,6 +19,14 @@
  * under the License.
  */
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+
 import org.apache.maven.wagon.authentication.AuthenticationException;
 import org.apache.maven.wagon.authentication.AuthenticationInfo;
 import org.apache.maven.wagon.authorization.AuthorizationException;
@@ -36,17 +44,6 @@
 import org.apache.maven.wagon.resource.Resource;
 import org.codehaus.plexus.util.IOUtil;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.List;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
-
 /**
  * Implementation of common facilties for Wagon providers.
  *
@@ -72,6 +69,9 @@
     
     private ProxyInfoProvider proxyInfoProvider;
     
+    /** @deprecated */
+    protected ProxyInfo proxyInfo;
+    
     private RepositoryPermissions permissionsOverride;
 
     // ----------------------------------------------------------------------
@@ -160,6 +160,7 @@
                 }
             }
         } );
+        this.proxyInfo = proxyInfo;
     }
     
     public void connect( Repository repository, AuthenticationInfo authenticationInfo,
@@ -201,22 +202,7 @@
         
         fireSessionOpening();
 
-        try
-        {
-            openConnectionInternal();
-        }
-        catch ( ConnectionException e )
-        {
-            fireSessionConnectionRefused();
-            
-            throw e;
-        }
-        catch ( AuthenticationException e )
-        {
-            fireSessionConnectionRefused();
-            
-            throw e;
-        }
+        openConnection();
 
         fireSessionOpened();
     }
@@ -288,6 +274,12 @@
         getTransfer( resource, destination, input, true, Integer.MAX_VALUE );
     }
 
+    protected void getTransfer( Resource resource, OutputStream output, InputStream input )
+        throws TransferFailedException
+    {
+        getTransfer( resource, output, input, true, Integer.MAX_VALUE );
+    }
+
     protected void getTransfer( Resource resource, File destination, InputStream input, boolean closeInput,
                                 int maxSize )
         throws TransferFailedException
@@ -296,18 +288,16 @@
         fireTransferDebug( "attempting to create parent directories for destination: " + destination.getName() );
         createParentDirectories( destination );
 
-        fireGetStarted( resource, destination );
-
         OutputStream output = new LazyFileOutputStream( destination );
 
+        fireGetStarted( resource, destination );
+
         try
         {
-            transfer( resource, input, output, TransferEvent.REQUEST_GET, maxSize );
+            getTransfer( resource, output, input, closeInput, maxSize );
         }
-        catch ( IOException e )
+        catch ( TransferFailedException e )
         {
-            fireTransferError( resource, e, TransferEvent.REQUEST_GET );
-
             if ( destination.exists() )
             {
                 boolean deleted = destination.delete();
@@ -317,11 +307,32 @@
                     destination.deleteOnExit();
                 }
             }
+            throw e;
+        }
+        finally
+        {
+            IOUtil.close( output );
+        }
+
+        fireGetCompleted( resource, destination );
+    }
+
+    protected void getTransfer( Resource resource, OutputStream output, InputStream input, boolean closeInput, int maxSize )
+        throws TransferFailedException
+    {
+        try
+        {
+            transfer( resource, input, output, TransferEvent.REQUEST_GET, maxSize );
+            
+            finishGetTransfer( resource, input, output );
+        }
+        catch ( IOException e )
+        {
+            fireTransferError( resource, e, TransferEvent.REQUEST_GET );
 
             String msg = "GET request of: " + resource.getName() + " from " + repository.getName() + " failed";
 
             throw new TransferFailedException( msg, e );
-
         }
         finally
         {
@@ -330,19 +341,22 @@
                 IOUtil.close( input );
             }
 
-            IOUtil.close( output );
+            cleanupGetTransfer( resource );
         }
+    }
 
-        fireGetCompleted( resource, destination );
+    protected void finishGetTransfer( Resource resource, InputStream input, OutputStream output )
+        throws TransferFailedException
+    {
+    }
+
+    protected void cleanupGetTransfer( Resource resource )
+    {
     }
 
     protected void putTransfer( Resource resource, File source, OutputStream output, boolean closeOutput )
-        throws TransferFailedException
+        throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
     {
-        resource.setContentLength( source.length() );
-
-        resource.setLastModified( source.lastModified() );
-
         firePutStarted( resource, source );
 
         transfer( resource, source, output, closeOutput );
@@ -360,9 +374,11 @@
      * @param output output stream
      * @param closeOutput whether the output stream should be closed or not
      * @throws TransferFailedException
+     * @throws ResourceDoesNotExistException 
+     * @throws AuthorizationException 
      */
     protected void transfer( Resource resource, File source, OutputStream output, boolean closeOutput )
-        throws TransferFailedException
+        throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
     {
         InputStream input = null;
 
@@ -370,7 +386,7 @@
         {
             input = new FileInputStream( source );
 
-            transfer( resource, input, output, TransferEvent.REQUEST_PUT );
+            putTransfer( resource, input, output, closeOutput );
         }
         catch ( FileNotFoundException e )
         {
@@ -378,25 +394,49 @@
 
             throw new TransferFailedException( "Specified source file does not exist: " + source, e );
         }
+        finally
+        {
+            IOUtil.close( input );
+        }
+    }
+
+    protected void putTransfer( Resource resource, InputStream input, OutputStream output, boolean closeOutput )
+        throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
+    {
+        try
+        {
+            transfer( resource, input, output, TransferEvent.REQUEST_PUT );
+            
+            finishPutTransfer( resource, input, output );
+        }
         catch ( IOException e )
         {
             fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
 
-            String msg = "PUT request for: " + resource + " to " + source.getName() + "failed";
+            String msg = "PUT request to: " + resource.getName() + " in " + repository.getName() + " failed";
 
             throw new TransferFailedException( msg, e );
         }
         finally
         {
-            IOUtil.close( input );
-
             if ( closeOutput )
             {
                 IOUtil.close( output );
             }
+            
+            cleanupPutTransfer( resource );
         }
     }
 
+    protected void cleanupPutTransfer( Resource resource )
+    {
+    }
+
+    protected void finishPutTransfer( Resource resource, InputStream input, OutputStream output )
+        throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
+    {
+    }
+
     /**
      * Write from {@link InputStream} to {@link OutputStream}.
      * Equivalent to {@link #transfer(Resource, InputStream, OutputStream, int, int)} with a maxSize equal to {@link Integer#MAX_VALUE}
@@ -448,6 +488,7 @@
 
             remaining -= n;
         }
+        output.flush();
     }
 
     // ----------------------------------------------------------------------
@@ -713,9 +754,10 @@
         transferEvent.setTimestamp( System.currentTimeMillis() );
         transferEvent.setLocalFile( source );
 
+        InputStream input = null;
         try
         {
-            InputStream input = new FileInputStream( source );
+            input = new FileInputStream( source );
 
             while ( true )
             {
@@ -735,6 +777,10 @@
             
             throw new TransferFailedException( "Failed to post-process the source file", e );
         }
+        finally
+        {
+            IOUtil.close( input );
+        }
     }
 
     public void putDirectory( File sourceDirectory, String destinationDirectory )
@@ -748,54 +794,6 @@
         return false;
     }
 
-    public void createZip( List files, File zipName, File basedir )
-        throws IOException
-    {
-        ZipOutputStream zos = new ZipOutputStream( new FileOutputStream( zipName ) );
-
-        try
-        {
-            for ( int i = 0; i < files.size(); i++ )
-            {
-                String file = (String) files.get( i );
-
-                file = file.replace( '\\', '/' );
-
-                writeZipEntry( zos, new File( basedir, file ), file );
-            }
-        }
-        finally
-        {
-            IOUtil.close( zos );
-        }
-    }
-
-    private void writeZipEntry( ZipOutputStream jar, File source, String entryName )
-        throws IOException
-    {
-        byte[] buffer = new byte[1024];
-
-        int bytesRead;
-
-        FileInputStream is = new FileInputStream( source );
-
-        try
-        {
-            ZipEntry entry = new ZipEntry( entryName );
-
-            jar.putNextEntry( entry );
-
-            while ( ( bytesRead = is.read( buffer ) ) != -1 )
-            {
-                jar.write( buffer, 0, bytesRead );
-            }
-        }
-        finally
-        {
-            is.close();
-        }
-    }
-
     protected static String getPath( String basedir, String dir )
     {
         String path;
diff --git a/wagon-provider-api/src/main/java/org/apache/maven/wagon/StreamWagon.java b/wagon-provider-api/src/main/java/org/apache/maven/wagon/StreamWagon.java
index 77e1237..09b4f06 100644
--- a/wagon-provider-api/src/main/java/org/apache/maven/wagon/StreamWagon.java
+++ b/wagon-provider-api/src/main/java/org/apache/maven/wagon/StreamWagon.java
@@ -36,13 +36,14 @@
  */
 public abstract class StreamWagon
     extends AbstractWagon
+    implements StreamingWagon
 {
     // ----------------------------------------------------------------------
     //
     // ----------------------------------------------------------------------
 
     public abstract void fillInputData( InputData inputData )
-        throws TransferFailedException, ResourceDoesNotExistException;
+        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException;
 
     public abstract void fillOutputData( OutputData outputData )
         throws TransferFailedException;
@@ -57,31 +58,12 @@
     public void get( String resourceName, File destination )
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
     {
-        InputData inputData = new InputData();
+        getIfNewer( resourceName, destination, 0 );
+    }
 
-        Resource resource = new Resource( resourceName );
-
-        fireGetInitiated( resource, destination );
-
-        inputData.setResource( resource );
-
-        try
-        {
-            fillInputData( inputData );
-        }
-        catch ( TransferFailedException e )
-        {
-            fireTransferError( resource, e, TransferEvent.REQUEST_GET );
-            throw e;
-        }
-        catch ( ResourceDoesNotExistException e )
-        {
-            fireTransferError( resource, e, TransferEvent.REQUEST_GET );
-            throw e;
-        }
-
-        InputStream is = inputData.getInputStream();
-
+    protected void checkInputStream( InputStream is, Resource resource )
+        throws TransferFailedException
+    {
         if ( is == null )
         {
             TransferFailedException e =
@@ -90,10 +72,6 @@
             fireTransferError( resource, e, TransferEvent.REQUEST_GET );
             throw e;
         }
-
-        createParentDirectories( destination );
-
-        getTransfer( inputData.getResource(), destination, is );
     }
 
     public boolean getIfNewer( String resourceName, File destination, long timestamp )
@@ -101,46 +79,22 @@
     {
         boolean retValue = false;
 
-        InputData inputData = new InputData();
-
         Resource resource = new Resource( resourceName );
 
-        inputData.setResource( resource );
+        fireGetInitiated( resource, destination );
 
-        try
-        {
-            fillInputData( inputData );
-        }
-        catch ( TransferFailedException e )
-        {
-            fireTransferError( resource, e, TransferEvent.REQUEST_GET );
-            throw e;
-        }
-        catch ( ResourceDoesNotExistException e )
-        {
-            fireTransferError( resource, e, TransferEvent.REQUEST_GET );
-            throw e;
-        }
-
-        InputStream is = inputData.getInputStream();
+        resource.setLastModified( timestamp );
+        
+        InputStream is = getInputStream( resource );
 
         // always get if timestamp is 0 (ie, target doesn't exist), otherwise only if older than the remote file
         if ( timestamp == 0 || timestamp < resource.getLastModified() )
         {
             retValue = true;
 
-            if ( is == null )
-            {
-                TransferFailedException e =
-                    new TransferFailedException( getRepository().getUrl()
-                        + " - Could not open input stream for resource: '" + resource + "'" );
-                fireTransferError( resource, e, TransferEvent.REQUEST_GET );
-                throw e;
-            }
+            checkInputStream( is, resource );
 
-            createParentDirectories( destination );
-
-            getTransfer( inputData.getResource(), destination, is );
+            getTransfer( resource, destination, is );
         }
         else
         {
@@ -150,16 +104,80 @@
         return retValue;
     }
 
+    protected InputStream getInputStream( Resource resource )
+        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
+    {
+        InputData inputData = new InputData();
+
+        inputData.setResource( resource );
+
+        try
+        {
+            fillInputData( inputData );
+        }
+        catch ( TransferFailedException e )
+        {
+            fireTransferError( resource, e, TransferEvent.REQUEST_GET );
+            throw e;
+        }
+        catch ( ResourceDoesNotExistException e )
+        {
+            fireTransferError( resource, e, TransferEvent.REQUEST_GET );
+            throw e;
+        }
+        catch ( AuthorizationException e )
+        {
+            fireTransferError( resource, e, TransferEvent.REQUEST_GET );
+            throw e;
+        }
+        finally
+        {
+            if ( inputData.getInputStream() == null )
+            {
+                cleanupGetTransfer( resource );
+            }
+        }
+
+        return inputData.getInputStream();
+    }
+
     // source doesn't exist exception
     public void put( File source, String resourceName )
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
     {
-        OutputData outputData = new OutputData();
-
         Resource resource = new Resource( resourceName );
 
         firePutInitiated( resource, source );
 
+        resource.setContentLength( source.length() );
+
+        resource.setLastModified( source.lastModified() );
+
+        OutputStream os = getOutputStream( resource );
+
+        checkOutputStream( resource, os );
+
+        putTransfer( resource, source, os, true );
+    }
+
+    protected void checkOutputStream( Resource resource, OutputStream os )
+        throws TransferFailedException
+    {
+        if ( os == null )
+        {
+            TransferFailedException e =
+                new TransferFailedException( getRepository().getUrl()
+                    + " - Could not open output stream for resource: '" + resource + "'" );
+            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
+            throw e;
+        }
+    }
+
+    protected OutputStream getOutputStream( Resource resource )
+        throws TransferFailedException
+    {
+        OutputData outputData = new OutputData();
+
         outputData.setResource( resource );
 
         try
@@ -172,18 +190,90 @@
 
             throw e;
         }
-
-        OutputStream os = outputData.getOutputStream();
-
-        if ( os == null )
+        finally
         {
-            TransferFailedException e =
-                new TransferFailedException( getRepository().getUrl()
-                    + " - Could not open output stream for resource: '" + resource + "'" );
-            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
-            throw e;
+            if ( outputData.getOutputStream() == null )
+            {
+                cleanupPutTransfer( resource );
+            }
         }
 
-        putTransfer( outputData.getResource(), source, os, true );
+        return outputData.getOutputStream();
+    }
+
+    public boolean getIfNewerToStream( String resourceName, OutputStream stream, long timestamp )
+        throws ResourceDoesNotExistException, TransferFailedException, AuthorizationException
+    {
+        boolean retValue = false;
+
+        Resource resource = new Resource( resourceName );
+
+        fireGetInitiated( resource, null );
+
+        InputStream is = getInputStream( resource );
+
+        // always get if timestamp is 0 (ie, target doesn't exist), otherwise only if older than the remote file
+        if ( timestamp == 0 || timestamp < resource.getLastModified() )
+        {
+            retValue = true;
+
+            checkInputStream( is, resource );
+
+            fireGetStarted( resource, null );
+
+            getTransfer( resource, stream, is, true, Integer.MAX_VALUE );
+
+            fireGetCompleted( resource, null );
+        }
+        else
+        {
+            IOUtil.close( is );
+        }
+        
+        return retValue;
+    }
+
+    public void getToStream( String resourceName, OutputStream stream )
+        throws ResourceDoesNotExistException, TransferFailedException, AuthorizationException
+    {
+        getIfNewerToStream( resourceName, stream, 0 );
+    }
+
+    public void putFromStream( InputStream stream, String destination )
+        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
+    {
+        Resource resource = new Resource( destination );
+
+        firePutInitiated( resource, null );
+
+        putFromStream( stream, resource );
+    }
+
+    public void putFromStream( InputStream stream, String destination, long contentLength, long lastModified )
+        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
+    {
+        Resource resource = new Resource( destination );
+
+        firePutInitiated( resource, null );
+
+        resource.setContentLength( contentLength );
+
+        resource.setLastModified( lastModified );
+
+        putFromStream( stream, resource );
+    }
+
+    private void putFromStream( InputStream stream, Resource resource )
+        throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
+    {
+        OutputStream os = getOutputStream( resource );
+
+        checkOutputStream( resource, os );
+
+        firePutStarted( resource, null );
+
+        putTransfer( resource, stream, os, true );
+
+        firePutCompleted( resource, null );
     }
 }
diff --git a/wagon-provider-api/src/main/java/org/apache/maven/wagon/StreamingWagon.java b/wagon-provider-api/src/main/java/org/apache/maven/wagon/StreamingWagon.java
new file mode 100644
index 0000000..68827d7
--- /dev/null
+++ b/wagon-provider-api/src/main/java/org/apache/maven/wagon/StreamingWagon.java
@@ -0,0 +1,83 @@
+package org.apache.maven.wagon;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.InputStream;
+import java.io.OutputStream;
+
+import org.apache.maven.wagon.authorization.AuthorizationException;
+
+public interface StreamingWagon
+    extends Wagon
+{
+    /**
+     * Downloads specified resource from the repository to given output stream.
+     * 
+     * @param resourceName
+     * @param destination
+     * @throws TransferFailedException
+     * @throws ResourceDoesNotExistException
+     * @throws AuthorizationException 
+     * @throws AuthorizationException
+     */
+    void getToStream( String resourceName, OutputStream stream )
+        throws ResourceDoesNotExistException, TransferFailedException, AuthorizationException;
+
+    /**
+     * Downloads specified resource from the repository if it was modified since specified date. The date is measured in
+     * milliseconds, between the current time and midnight, January 1, 1970 UTC and aligned to GMT timezone.
+     * 
+     * @param resourceName
+     * @param destination
+     * @param timestamp
+     * @return <code>true</code> if newer resource has been downloaded, <code>false</code> if resource in the
+     *         repository is older or has the same age.
+     * @throws TransferFailedException
+     * @throws ResourceDoesNotExistException
+     * @throws AuthorizationException 
+     * @throws AuthorizationException
+     */
+    boolean getIfNewerToStream( String resourceName, OutputStream stream, long timestamp )
+        throws ResourceDoesNotExistException, TransferFailedException, AuthorizationException;
+
+    /**
+     * Copy from a local input stream to remote.
+     * 
+     * @param source the local file
+     * @param destination the remote destination
+     * @throws TransferFailedException
+     * @throws ResourceDoesNotExistException
+     * @throws AuthorizationException
+     */
+    void putFromStream( InputStream stream, String destination )
+        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException;
+
+    /**
+     * Copy from a local input stream to remote.
+     * 
+     * @param source the local file
+     * @param destination the remote destination
+     * @throws TransferFailedException
+     * @throws ResourceDoesNotExistException
+     * @throws AuthorizationException
+     */
+    void putFromStream( InputStream stream, String destination, long contentLength, long lastModified )
+        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException;
+}
diff --git a/wagon-provider-api/src/main/java/org/apache/maven/wagon/Wagon.java b/wagon-provider-api/src/main/java/org/apache/maven/wagon/Wagon.java
index 0cf4c8c..387600b 100644
--- a/wagon-provider-api/src/main/java/org/apache/maven/wagon/Wagon.java
+++ b/wagon-provider-api/src/main/java/org/apache/maven/wagon/Wagon.java
@@ -65,8 +65,6 @@
      * @throws TransferFailedException
      * @throws ResourceDoesNotExistException
      * @throws AuthorizationException
-     * @todo michal: I have to learn more about timezones!
-     * Specifically how to convert time for UTC to time for GMT and if such conversioin is needed.
      */
     boolean getIfNewer( String resourceName, File destination, long timestamp )
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException;
diff --git a/wagon-provider-api/src/main/java/org/apache/maven/wagon/WagonUtils.java b/wagon-provider-api/src/main/java/org/apache/maven/wagon/WagonUtils.java
index e2ca976..0bbbcd9 100644
--- a/wagon-provider-api/src/main/java/org/apache/maven/wagon/WagonUtils.java
+++ b/wagon-provider-api/src/main/java/org/apache/maven/wagon/WagonUtils.java
@@ -19,14 +19,13 @@
  * under the License.
  */
 
-import org.apache.maven.wagon.authentication.AuthenticationInfo;
-import org.apache.maven.wagon.authorization.AuthorizationException;
-import org.codehaus.plexus.util.FileUtils;
-
 import java.io.File;
 import java.io.IOException;
 import java.util.LinkedList;
 
+import org.apache.maven.wagon.authorization.AuthorizationException;
+import org.codehaus.plexus.util.FileUtils;
+
 /**
  * @author <a href="mailto:mmaczka@interia.pl">Michal Maczka</a>
  * @version $Id$
diff --git a/wagon-provider-api/src/test/java/org/apache/maven/wagon/AbstractWagonTest.java b/wagon-provider-api/src/test/java/org/apache/maven/wagon/AbstractWagonTest.java
index 05e2d05..9621e7c 100644
--- a/wagon-provider-api/src/test/java/org/apache/maven/wagon/AbstractWagonTest.java
+++ b/wagon-provider-api/src/test/java/org/apache/maven/wagon/AbstractWagonTest.java
@@ -19,18 +19,20 @@
  * under the License.
  */
 
-import junit.framework.TestCase;
-import org.apache.maven.wagon.authentication.AuthenticationException;
-import org.apache.maven.wagon.authorization.AuthorizationException;
-import org.apache.maven.wagon.events.SessionListenerMock;
-import org.apache.maven.wagon.events.TransferListenerMock;
-import org.apache.maven.wagon.repository.Repository;
-import org.codehaus.plexus.util.IOUtil;
-
 import java.io.File;
 import java.io.InputStream;
 import java.io.OutputStream;
 
+import junit.framework.TestCase;
+
+import org.apache.maven.wagon.authentication.AuthenticationException;
+import org.apache.maven.wagon.authorization.AuthorizationException;
+import org.apache.maven.wagon.events.SessionListener;
+import org.apache.maven.wagon.events.TransferListener;
+import org.apache.maven.wagon.repository.Repository;
+import org.codehaus.plexus.util.IOUtil;
+import org.easymock.MockControl;
+
 /**
  * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
  * @version $Id$
@@ -48,9 +50,13 @@
 
     private String artifact;
 
-    private SessionListenerMock sessionListener = null;
+    private SessionListener sessionListener = null;
 
-    private TransferListenerMock transferListener = null;
+    private TransferListener transferListener = null;
+
+    private MockControl transferListenerControl;
+
+    private MockControl sessionListenerControl;
 
     protected void setUp()
         throws Exception
@@ -65,11 +71,13 @@
 
         wagon = new WagonMock();
 
-        sessionListener = new SessionListenerMock();
+        sessionListenerControl = MockControl.createControl( SessionListener.class ); 
+        sessionListener = (SessionListener) sessionListenerControl.getMock();
 
         wagon.addSessionListener( sessionListener );
 
-        transferListener = new TransferListenerMock();
+        transferListenerControl = MockControl.createControl( TransferListener.class ); 
+        transferListener = (TransferListener) transferListenerControl.getMock();
 
         wagon.addTransferListener( transferListener );
 
@@ -98,64 +106,66 @@
     {
         Repository repository = new Repository();
 
+        sessionListenerControl.setDefaultMatcher( MockControl.ALWAYS_MATCHER );
+        sessionListener.sessionOpening( null );
+        sessionListener.sessionOpened( null );
+        sessionListenerControl.replay();
+        
         wagon.connect( repository );
+        
+        sessionListenerControl.verify();
 
         assertEquals( repository, wagon.getRepository() );
-
-        assertTrue( sessionListener.isSessionOpenningCalled() );
-
-        assertTrue( sessionListener.isSessionOpenedCalled() );
-
-        //!!
-        //assertTrue( sessionListener.isSessionLoggedInCalled() );
-
-        //!!
-        //assertTrue( sessionListener.isSessionRefusedCalled() );
-
     }
 
     public void testSessionCloseEvents()
         throws Exception
     {
+        sessionListenerControl.setDefaultMatcher( MockControl.ALWAYS_MATCHER );
+        sessionListener.sessionDisconnecting( null );
+        sessionListener.sessionDisconnected( null );
+        sessionListenerControl.replay();
+        
         wagon.disconnect();
-
-        //!!
-        //assertTrue( sessionListener.isSessionLoggedOffCalled() );
-
-        assertTrue( sessionListener.isSessionDisconnectingCalled() );
-
-        assertTrue( sessionListener.isSessionDisconnectedCalled() );
+        
+        sessionListenerControl.verify();
     }
 
     public void testGetTransferEvents()
         throws Exception
     {
+        transferListener.debug( "fetch debug message" );
+        transferListenerControl.setDefaultMatcher( MockControl.ALWAYS_MATCHER );
+        transferListener.transferInitiated( null );
+        transferListener.transferStarted( null );
+        transferListener.debug( null );
+        transferListenerControl.setVoidCallable( MockControl.ZERO_OR_MORE );
+        transferListener.transferProgress( null, null, 0 );
+        transferListenerControl.setVoidCallable( 5 );
+        transferListener.transferCompleted( null );
+        transferListenerControl.replay();
+        
         wagon.fireTransferDebug( "fetch debug message" );
 
         Repository repository = new Repository();
-
         wagon.connect( repository );
 
-        assertEquals( "fetch debug message", transferListener.getDebugMessage() );
-
-        assertTrue( transferListener.isDebugCalled() );
-
         wagon.get( artifact, destination );
-
-        assertTrue( transferListener.isTransferStartedCalled() );
-
-        assertTrue( transferListener.isTransferCompletedCalled() );
-
-        assertTrue( transferListener.isTransferProgressCalled() );
-
-        assertEquals( 5, transferListener.getNumberOfProgressCalls() );
+        
+        transferListenerControl.verify();
     }
 
     public void testGetError()
         throws Exception
     {
-        TransferListenerMock transferListener = new TransferListenerMock();
-
+        transferListenerControl.setDefaultMatcher( MockControl.ALWAYS_MATCHER );
+        transferListener.transferInitiated( null );
+        transferListener.transferStarted( null );        
+        transferListener.debug( null );
+        transferListenerControl.setVoidCallable( MockControl.ZERO_OR_MORE );
+        transferListener.transferError( null );
+        transferListenerControl.replay();
+        
         try
         {
             Repository repository = new Repository();
@@ -175,17 +185,21 @@
             assertTrue( true );
         }
 
-        assertTrue( transferListener.isTransferStartedCalled() );
-
-        assertTrue( transferListener.isTransferErrorCalled() );
-
-        assertFalse( transferListener.isTransferCompletedCalled() );
+        transferListenerControl.verify();
     }
 
     public void testPutTransferEvents()
         throws ConnectionException, AuthenticationException, ResourceDoesNotExistException, TransferFailedException,
         AuthorizationException
     {
+        transferListener.debug( "deploy debug message" );
+        transferListenerControl.setDefaultMatcher( MockControl.ALWAYS_MATCHER );
+        transferListener.transferInitiated( null );
+        transferListener.transferStarted( null );
+        transferListener.transferProgress( null, null, 0 );
+        transferListener.transferCompleted( null );
+        transferListenerControl.replay();
+        
         wagon.fireTransferDebug( "deploy debug message" );
 
         Repository repository = new Repository();
@@ -194,18 +208,7 @@
 
         wagon.put( source, artifact );
 
-        assertTrue( transferListener.isTransferStartedCalled() );
-
-        assertTrue( transferListener.isTransferCompletedCalled() );
-
-        assertTrue( transferListener.isDebugCalled() );
-
-        assertTrue( transferListener.isTransferProgressCalled() );
-
-        assertEquals( "deploy debug message", transferListener.getDebugMessage() );
-
-        //!!
-        //assertEquals( 5, transferListener.getNumberOfProgressCalls() );
+        transferListenerControl.verify();
     }
 
     /*
diff --git a/wagon-provider-api/src/test/java/org/apache/maven/wagon/PermissionModeUtilsTest.java b/wagon-provider-api/src/test/java/org/apache/maven/wagon/PermissionModeUtilsTest.java
index 6365e64..b42298c 100644
--- a/wagon-provider-api/src/test/java/org/apache/maven/wagon/PermissionModeUtilsTest.java
+++ b/wagon-provider-api/src/test/java/org/apache/maven/wagon/PermissionModeUtilsTest.java
@@ -20,7 +20,6 @@
  */
 
 import junit.framework.TestCase;
-import org.apache.maven.wagon.PermissionModeUtils;
 
 /**
  * Unit test for PermissionModeUtils class
diff --git a/wagon-provider-api/src/test/java/org/apache/maven/wagon/WagonMock.java b/wagon-provider-api/src/test/java/org/apache/maven/wagon/WagonMock.java
index f9a4d9b..3e609b3 100644
--- a/wagon-provider-api/src/test/java/org/apache/maven/wagon/WagonMock.java
+++ b/wagon-provider-api/src/test/java/org/apache/maven/wagon/WagonMock.java
@@ -21,7 +21,6 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-import java.io.File;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.Collections;
@@ -98,12 +97,6 @@
 
     }
 
-    public boolean getIfNewer( String resourceName, File destination, long timestamp )
-        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-    {
-        return false;
-    }
-
     public void openConnectionInternal()
     {
     }
diff --git a/wagon-provider-api/src/test/java/org/apache/maven/wagon/events/SessionEventSupportTest.java b/wagon-provider-api/src/test/java/org/apache/maven/wagon/events/SessionEventSupportTest.java
index 45f6452..c61ce64 100644
--- a/wagon-provider-api/src/test/java/org/apache/maven/wagon/events/SessionEventSupportTest.java
+++ b/wagon-provider-api/src/test/java/org/apache/maven/wagon/events/SessionEventSupportTest.java
@@ -20,8 +20,9 @@
  */
 
 import junit.framework.TestCase;
-import org.apache.maven.wagon.WagonMock;
+
 import org.apache.maven.wagon.Wagon;
+import org.easymock.MockControl;
 
 /**
  * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
@@ -33,6 +34,9 @@
 
     private SessionEventSupport eventSupport;
 
+    private MockControl wagonMockControl;
+    
+    private Wagon wagon;
 
     /**
      * @see junit.framework.TestCase#setUp()
@@ -43,6 +47,11 @@
         super.setUp();
 
         eventSupport = new SessionEventSupport();
+        
+        // TODO: actually test it gets called?
+        wagonMockControl = MockControl.createControl( Wagon.class );
+        
+        wagon = (Wagon) wagonMockControl.getMock();
     }
 
     public void testSessionListenerRegistration()
@@ -85,8 +94,6 @@
 
         eventSupport.addSessionListener( mock2 );
 
-        final Wagon wagon = new WagonMock();
-
         final SessionEvent event = new SessionEvent( wagon, 1 );
 
         eventSupport.fireSessionDisconnected( event );
@@ -111,8 +118,6 @@
 
         eventSupport.addSessionListener( mock2 );
 
-        final Wagon wagon = new WagonMock();
-
         final SessionEvent event = new SessionEvent( wagon, 1 );
 
         eventSupport.fireSessionDisconnecting( event );
@@ -136,8 +141,6 @@
 
         eventSupport.addSessionListener( mock2 );
 
-        final Wagon wagon = new WagonMock();
-
         final SessionEvent event = new SessionEvent( wagon, 1 );
 
         eventSupport.fireSessionLoggedIn( event );
@@ -162,8 +165,6 @@
 
         eventSupport.addSessionListener( mock2 );
 
-        final Wagon wagon = new WagonMock();
-
         final SessionEvent event = new SessionEvent( wagon, 1 );
 
         eventSupport.fireSessionLoggedOff( event );
@@ -188,8 +189,6 @@
 
         eventSupport.addSessionListener( mock2 );
 
-        final Wagon wagon = new WagonMock();
-
         final SessionEvent event = new SessionEvent( wagon, 1 );
 
         eventSupport.fireSessionOpened( event );
@@ -215,8 +214,6 @@
 
         eventSupport.addSessionListener( mock2 );
 
-        final Wagon wagon = new WagonMock();
-
         final SessionEvent event = new SessionEvent( wagon, 1 );
 
         eventSupport.fireSessionOpening( event );
@@ -241,8 +238,6 @@
 
         eventSupport.addSessionListener( mock2 );
 
-        final Wagon wagon = new WagonMock();
-
         final SessionEvent event = new SessionEvent( wagon, 1 );
 
         eventSupport.fireSessionConnectionRefused( event );
@@ -277,5 +272,5 @@
         assertEquals( "mm", mock2.getDebugMessage() );
 
     }
-
+    
 }
diff --git a/wagon-provider-api/src/test/java/org/apache/maven/wagon/events/SessionEventTest.java b/wagon-provider-api/src/test/java/org/apache/maven/wagon/events/SessionEventTest.java
index f740f49..a1bc846 100644
--- a/wagon-provider-api/src/test/java/org/apache/maven/wagon/events/SessionEventTest.java
+++ b/wagon-provider-api/src/test/java/org/apache/maven/wagon/events/SessionEventTest.java
@@ -21,10 +21,10 @@
 
 import junit.framework.TestCase;
 import org.apache.maven.wagon.ConnectionException;
-import org.apache.maven.wagon.WagonMock;
 import org.apache.maven.wagon.Wagon;
 import org.apache.maven.wagon.authentication.AuthenticationException;
 import org.apache.maven.wagon.repository.Repository;
+import org.easymock.MockControl;
 
 /**
  * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
@@ -33,26 +33,6 @@
 public class SessionEventTest
     extends TestCase
 {
-
-    /*
-	 * @see TestCase#setUp()
-	 */
-    protected void setUp()
-        throws Exception
-    {
-        super.setUp();
-    }
-
-    /**
-     * Constructor for SESSIONEventTest.
-     *
-     * @param arg0
-     */
-    public SessionEventTest( final String arg0 )
-    {
-        super( arg0 );
-    }
-
     /*
 	 * Class to test for void SESSIONEvent(Wagon, Repository, String, int,
 	 * int)
@@ -61,7 +41,7 @@
         throws ConnectionException, AuthenticationException
     {
 
-        final Wagon wagon = new WagonMock();
+        final Wagon wagon = (Wagon) MockControl.createControl( Wagon.class ).getMock();
         final Repository repo = new Repository();
 
         wagon.connect( repo );
@@ -72,14 +52,12 @@
         SessionEvent event = new SessionEvent( wagon, SessionEvent.SESSION_CLOSED );
 
         assertEquals( wagon, event.getWagon() );
-        assertEquals( repo, event.getWagon().getRepository() );
 
         assertEquals( SessionEvent.SESSION_CLOSED, event.getEventType() );
 
         event = new SessionEvent( wagon, exception );
 
         assertEquals( wagon, event.getWagon() );
-        assertEquals( repo, event.getWagon().getRepository() );
         assertEquals( SessionEvent.SESSION_ERROR_OCCURRED, event.getEventType() );
         assertEquals( exception, event.getException() );
 
diff --git a/wagon-provider-api/src/test/java/org/apache/maven/wagon/events/TransferEventSupportTest.java b/wagon-provider-api/src/test/java/org/apache/maven/wagon/events/TransferEventSupportTest.java
index 9b86416..c9cca3b 100644
--- a/wagon-provider-api/src/test/java/org/apache/maven/wagon/events/TransferEventSupportTest.java
+++ b/wagon-provider-api/src/test/java/org/apache/maven/wagon/events/TransferEventSupportTest.java
@@ -20,8 +20,8 @@
  */
 
 import junit.framework.TestCase;
-import org.apache.maven.wagon.WagonMock;
 import org.apache.maven.wagon.Wagon;
+import org.easymock.MockControl;
 
 /**
  * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
@@ -31,19 +31,26 @@
 public class TransferEventSupportTest
     extends TestCase
 {
-
     private TransferEventSupport eventSupport;
 
+    private MockControl wagonMockControl;
+    
+    private Wagon wagon;
+
     /**
      * @see junit.framework.TestCase#setUp()
      */
     protected void setUp()
         throws Exception
     {
-
         super.setUp();
+        
         eventSupport = new TransferEventSupport();
-
+        
+        // TODO: actually test it gets called?
+        wagonMockControl = MockControl.createControl( Wagon.class );
+        
+        wagon = (Wagon) wagonMockControl.getMock();
     }
 
     public void testTransferListenerRegistration()
@@ -78,7 +85,6 @@
         final TransferListenerMock mock2 = new TransferListenerMock();
         eventSupport.addTransferListener( mock2 );
 
-        final Wagon wagon = new WagonMock();
         final TransferEvent event = getEvent( wagon );
 
         eventSupport.fireTransferStarted( event );
@@ -100,8 +106,6 @@
 
         eventSupport.addTransferListener( mock2 );
 
-        final Wagon wagon = new WagonMock();
-
         final TransferEvent event = getEvent( wagon );
 
         // TODO: should be testing the buffer
@@ -126,8 +130,6 @@
 
         eventSupport.addTransferListener( mock2 );
 
-        final Wagon wagon = new WagonMock();
-
         final TransferEvent event = getEvent( wagon );
 
         eventSupport.fireTransferCompleted( event );
@@ -152,8 +154,6 @@
 
         eventSupport.addTransferListener( mock2 );
 
-        final Wagon wagon = new WagonMock();
-
         final TransferEvent event = getEvent( wagon );
 
         eventSupport.fireTransferError( event );
diff --git a/wagon-provider-api/src/test/java/org/apache/maven/wagon/events/TransferEventTest.java b/wagon-provider-api/src/test/java/org/apache/maven/wagon/events/TransferEventTest.java
index 2075374..b7289fc 100644
--- a/wagon-provider-api/src/test/java/org/apache/maven/wagon/events/TransferEventTest.java
+++ b/wagon-provider-api/src/test/java/org/apache/maven/wagon/events/TransferEventTest.java
@@ -21,11 +21,11 @@
 
 import junit.framework.TestCase;
 import org.apache.maven.wagon.ConnectionException;
-import org.apache.maven.wagon.WagonMock;
 import org.apache.maven.wagon.Wagon;
 import org.apache.maven.wagon.authentication.AuthenticationException;
 import org.apache.maven.wagon.repository.Repository;
 import org.apache.maven.wagon.resource.Resource;
+import org.easymock.MockControl;
 
 /**
  * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
@@ -34,22 +34,6 @@
 public class TransferEventTest
     extends TestCase
 {
-
-    public TransferEventTest( final String name )
-    {
-        super( name );
-    }
-
-    /*
-     * @see TestCase#setUp()
-     */
-    protected void setUp()
-        throws Exception
-    {
-        super.setUp();
-    }
-
-
     /*
      * Class to test for void TransferEvent(Wagon, Repository, String, int,
      * int)
@@ -57,8 +41,7 @@
     public void testTransferEventProperties()
         throws ConnectionException, AuthenticationException
     {
-
-        final Wagon wagon = new WagonMock();
+        final Wagon wagon = (Wagon) MockControl.createControl( Wagon.class ).getMock();
 
         final Repository repo = new Repository();
 
@@ -77,8 +60,6 @@
 
         assertEquals( wagon, event.getWagon() );
 
-        assertEquals( repo, event.getWagon().getRepository() );
-
         assertEquals( "mm", event.getResource().getName() );
 
         assertEquals( TransferEvent.TRANSFER_COMPLETED, event.getEventType() );
@@ -93,8 +74,6 @@
 
         assertEquals( wagon, event.getWagon() );
 
-        assertEquals( repo, event.getWagon().getRepository() );
-
         assertEquals( "mm", event.getResource().getName() );
 
         assertEquals( TransferEvent.TRANSFER_ERROR, event.getEventType() );
diff --git a/wagon-provider-test/pom.xml b/wagon-provider-test/pom.xml
index 23e503a..79a507d 100644
--- a/wagon-provider-test/pom.xml
+++ b/wagon-provider-test/pom.xml
@@ -1,31 +1,8 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one
-  ~ or more contributor license agreements.  See the NOTICE file
-  ~ distributed with this work for additional information
-  ~ regarding copyright ownership.  The ASF licenses this file
-  ~ to you 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <artifactId>wagon</artifactId>
     <groupId>org.apache.maven.wagon</groupId>
-    <version>1.0-rc1-SNAPSHOT</version>
+    <version>1.0-beta-3</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>wagon-provider-test</artifactId>
@@ -51,4 +28,4 @@
       <version>1.2_Java1.3</version>
     </dependency>
   </dependencies>
-</project>
+</project>
\ No newline at end of file
diff --git a/wagon-provider-test/src/main/java/org/apache/maven/wagon/StreamingWagonTestCase.java b/wagon-provider-test/src/main/java/org/apache/maven/wagon/StreamingWagonTestCase.java
new file mode 100644
index 0000000..3366cb2
--- /dev/null
+++ b/wagon-provider-test/src/main/java/org/apache/maven/wagon/StreamingWagonTestCase.java
@@ -0,0 +1,301 @@
+package org.apache.maven.wagon;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
+
+import org.apache.maven.wagon.authentication.AuthenticationException;
+import org.apache.maven.wagon.authorization.AuthorizationException;
+import org.apache.maven.wagon.observers.ChecksumObserver;
+import org.apache.maven.wagon.resource.Resource;
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.IOUtil;
+
+/**
+ * @author <a href="mailto:brett@apache.org">Brett Porter</a>
+ * @version $Id: WagonTestCase.java 660462 2008-05-27 10:52:46Z brett $
+ */
+public abstract class StreamingWagonTestCase
+    extends WagonTestCase
+{
+    public void testStreamingWagon()
+        throws Exception
+    {
+        setupRepositories();
+
+        setupWagonTestingFixtures();
+
+        streamRoundTripTesting();
+
+        tearDownWagonTestingFixtures();
+    }
+
+    public void testFailedGetToStream()
+        throws Exception
+    {
+        setupRepositories();
+
+        setupWagonTestingFixtures();
+
+        message( "Getting test artifact from test repository " + testRepository );
+
+        StreamingWagon wagon = (StreamingWagon) getWagon();
+
+        wagon.addTransferListener( checksumObserver );
+
+        wagon.connect( testRepository, getAuthInfo() );
+
+        destFile = FileTestUtils.createUniqueFile( getName(), getName() );
+
+        destFile.deleteOnExit();
+
+        OutputStream stream = null;
+
+        try
+        {
+            stream = new FileOutputStream( destFile );
+            wagon.getToStream( "fubar.txt", stream );
+            fail( "File was found when it shouldn't have been" );
+        }
+        catch ( ResourceDoesNotExistException e )
+        {
+            // expected
+            assertTrue( true );
+        }
+        finally
+        {
+            wagon.removeTransferListener( checksumObserver );
+
+            wagon.disconnect();
+
+            IOUtil.close( stream );
+
+            tearDownWagonTestingFixtures();
+        }
+    }
+
+    public void testWagonGetIfNewerToStreamIsNewer()
+        throws Exception
+    {
+        if ( supportsGetIfNewer() )
+        {
+            setupRepositories();
+            setupWagonTestingFixtures();
+            int expectedSize = putFile();
+            getIfNewerToStream( getExpectedLastModifiedOnGet( testRepository, new Resource( resource ) ) + 30000,
+                                false, expectedSize );
+        }
+    }
+
+    public void testWagonGetIfNewerToStreamIsOlder()
+        throws Exception
+    {
+        if ( supportsGetIfNewer() )
+        {
+            setupRepositories();
+            setupWagonTestingFixtures();
+            int expectedSize = putFile();
+            getIfNewerToStream( new SimpleDateFormat( "yyyy-MM-dd" ).parse( "2006-01-01" ).getTime(), true,
+                                expectedSize );
+        }
+    }
+
+    public void testWagonGetIfNewerToStreamIsSame()
+        throws Exception
+    {
+        if ( supportsGetIfNewer() )
+        {
+            setupRepositories();
+            setupWagonTestingFixtures();
+            int expectedSize = putFile();
+            getIfNewerToStream( getExpectedLastModifiedOnGet( testRepository, new Resource( resource ) ), false,
+                                expectedSize );
+        }
+    }
+
+    private void getIfNewerToStream( long timestamp, boolean expectedResult, int expectedSize )
+        throws Exception, NoSuchAlgorithmException, IOException, ConnectionException, AuthenticationException,
+        TransferFailedException, ResourceDoesNotExistException, AuthorizationException
+    {
+        StreamingWagon wagon = (StreamingWagon) getWagon();
+
+        ProgressArgumentMatcher progressArgumentMatcher = setupGetIfNewerTest( wagon, expectedResult, expectedSize );
+
+        connectWagon( wagon );
+
+        OutputStream stream = new LazyFileOutputStream( destFile );
+        
+        try
+        {
+            boolean result = wagon.getIfNewerToStream( this.resource, stream, timestamp );
+            assertEquals( expectedResult, result );
+        }
+        finally
+        {
+            IOUtil.close( stream );
+        }
+        
+        disconnectWagon( wagon );
+
+        assertGetIfNewerTest( progressArgumentMatcher, expectedResult, expectedSize );
+
+        tearDownWagonTestingFixtures();
+    }
+
+    public void testFailedGetIfNewerToStream()
+        throws Exception
+    {
+        if ( supportsGetIfNewer() )
+        {
+            setupRepositories();
+            setupWagonTestingFixtures();
+            message( "Getting test artifact from test repository " + testRepository );
+            StreamingWagon wagon = (StreamingWagon) getWagon();
+            wagon.addTransferListener( checksumObserver );
+            wagon.connect( testRepository, getAuthInfo() );
+            destFile = FileTestUtils.createUniqueFile( getName(), getName() );
+            destFile.deleteOnExit();
+            OutputStream stream = null;
+            try
+            {
+                stream = new FileOutputStream( destFile );
+                wagon.getIfNewerToStream( "fubar.txt", stream, 0 );
+                fail( "File was found when it shouldn't have been" );
+            }
+            catch ( ResourceDoesNotExistException e )
+            {
+                // expected
+                assertTrue( true );
+            }
+            finally
+            {
+                wagon.removeTransferListener( checksumObserver );
+
+                wagon.disconnect();
+
+                IOUtil.close( stream );
+
+                tearDownWagonTestingFixtures();
+            }
+        }
+    }
+
+    protected void streamRoundTripTesting()
+        throws Exception
+    {
+        message( "Stream round trip testing ..." );
+
+        int expectedSize = putStream();
+
+        assertNotNull( "check checksum is not null", checksumObserver.getActualChecksum() );
+
+        assertEquals( "compare checksums", "6b144b7285ffd6b0bc8300da162120b9", checksumObserver.getActualChecksum() );
+
+        checksumObserver = new ChecksumObserver();
+
+        getStream( expectedSize );
+
+        assertNotNull( "check checksum is not null", checksumObserver.getActualChecksum() );
+
+        assertEquals( "compare checksums", "6b144b7285ffd6b0bc8300da162120b9", checksumObserver.getActualChecksum() );
+
+        // Now compare the conents of the artifact that was placed in
+        // the repository with the contents of the artifact that was
+        // retrieved from the repository.
+
+        String sourceContent = FileUtils.fileRead( sourceFile );
+
+        String destContent = FileUtils.fileRead( destFile );
+
+        assertEquals( sourceContent, destContent );
+    }
+
+    private int putStream()
+        throws Exception
+    {
+        String content = "test-resource.txt\n";
+        sourceFile = new File( FileTestUtils.getTestOutputDir(), "test-resource" );
+        sourceFile.getParentFile().mkdirs();
+        FileUtils.fileWrite( sourceFile.getAbsolutePath(), content );
+
+        StreamingWagon wagon = (StreamingWagon) getWagon();
+
+        ProgressArgumentMatcher progressArgumentMatcher = replayMockForPut( resource, content, wagon );
+
+        message( "Putting test artifact: " + resource + " into test repository " + testRepository );
+
+        connectWagon( wagon );
+
+        InputStream stream = null;
+
+        try
+        {
+            stream = new FileInputStream( sourceFile );
+            wagon.putFromStream( stream, resource, sourceFile.length(), sourceFile.lastModified() );
+        }
+        finally
+        {
+            IOUtil.close( stream );
+        }
+
+        disconnectWagon( wagon );
+
+        verifyMock( progressArgumentMatcher, content.length() );
+        return content.length();
+    }
+
+    private void getStream( int expectedSize )
+        throws Exception
+    {
+        destFile = FileTestUtils.createUniqueFile( getName(), getName() );
+        destFile.deleteOnExit();
+
+        StreamingWagon wagon = (StreamingWagon) getWagon();
+
+        ProgressArgumentMatcher progressArgumentMatcher = replaceMockForGet( wagon, expectedSize );
+
+        message( "Getting test artifact from test repository " + testRepository );
+
+        connectWagon( wagon );
+
+        OutputStream stream = null;
+
+        try
+        {
+            stream = new FileOutputStream( destFile );
+            wagon.getToStream( this.resource, stream );
+        }
+        finally
+        {
+            IOUtil.close( stream );
+        }
+
+        disconnectWagon( wagon );
+
+        verifyMock( progressArgumentMatcher, expectedSize );
+    }
+}
diff --git a/wagon-provider-test/src/main/java/org/apache/maven/wagon/WagonTestCase.java b/wagon-provider-test/src/main/java/org/apache/maven/wagon/WagonTestCase.java
index 1cc2a3b..c709886 100644
--- a/wagon-provider-test/src/main/java/org/apache/maven/wagon/WagonTestCase.java
+++ b/wagon-provider-test/src/main/java/org/apache/maven/wagon/WagonTestCase.java
@@ -21,10 +21,13 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
+import org.apache.maven.wagon.authentication.AuthenticationException;
 import org.apache.maven.wagon.authentication.AuthenticationInfo;
 import org.apache.maven.wagon.authorization.AuthorizationException;
 import org.apache.maven.wagon.events.TransferEvent;
@@ -46,7 +49,7 @@
 public abstract class WagonTestCase
     extends PlexusTestCase
 {
-    private static final class ProgressArgumentMatcher
+    static final class ProgressArgumentMatcher
         extends AbstractMatcher
     {
         private int size;
@@ -215,7 +218,7 @@
         return wagon;
     }
 
-    private void message( String message )
+    protected void message( String message )
     {
         System.out.println( message );
     }
@@ -236,6 +239,141 @@
         tearDownWagonTestingFixtures();
     }
 
+    public void testWagonGetIfNewerIsNewer()
+        throws Exception
+    {
+        if ( supportsGetIfNewer() )
+        {
+            setupRepositories();
+            setupWagonTestingFixtures();
+            int expectedSize = putFile();
+            getIfNewer( getExpectedLastModifiedOnGet( testRepository, new Resource( resource ) ) + 30000, false,
+                        expectedSize );
+        }
+    }
+
+    protected boolean supportsGetIfNewer()
+    {
+        return true;
+    }
+
+    public void testWagonGetIfNewerIsOlder()
+        throws Exception
+    {
+        if ( supportsGetIfNewer() )
+        {
+            setupRepositories();
+            setupWagonTestingFixtures();
+            int expectedSize = putFile();
+            getIfNewer( new SimpleDateFormat( "yyyy-MM-dd" ).parse( "2006-01-01" ).getTime(), true, expectedSize );
+        }
+    }
+
+    public void testWagonGetIfNewerIsSame()
+        throws Exception
+    {
+        if ( supportsGetIfNewer() )
+        {
+            setupRepositories();
+            setupWagonTestingFixtures();
+            int expectedSize = putFile();
+            getIfNewer( getExpectedLastModifiedOnGet( testRepository, new Resource( resource ) ), false, expectedSize );
+        }
+    }
+
+    private void getIfNewer( long timestamp, boolean expectedResult, int expectedSize )
+        throws Exception, NoSuchAlgorithmException, IOException, ConnectionException, AuthenticationException,
+        TransferFailedException, ResourceDoesNotExistException, AuthorizationException
+    {
+        Wagon wagon = getWagon();
+        
+        ProgressArgumentMatcher progressArgumentMatcher = setupGetIfNewerTest( wagon, expectedResult, expectedSize );
+        
+        connectWagon( wagon );
+        
+        boolean result = wagon.getIfNewer( this.resource, destFile, timestamp );
+        assertEquals( expectedResult, result );
+        
+        disconnectWagon( wagon );
+        
+        assertGetIfNewerTest( progressArgumentMatcher, expectedResult, expectedSize );
+        
+        tearDownWagonTestingFixtures();
+    }
+
+    protected void assertGetIfNewerTest( ProgressArgumentMatcher progressArgumentMatcher, boolean expectedResult,
+                                       int expectedSize )
+        throws IOException
+    {
+        if ( expectedResult == true )
+        {
+            verifyMock( progressArgumentMatcher, expectedSize );
+            
+            assertNotNull( "check checksum is not null", checksumObserver.getActualChecksum() );
+            
+            assertEquals( "compare checksums", "6b144b7285ffd6b0bc8300da162120b9", checksumObserver.getActualChecksum() );
+            
+            // Now compare the contents of the artifact that was placed in
+            // the repository with the contents of the artifact that was
+            // retrieved from the repository.
+            
+            String sourceContent = FileUtils.fileRead( sourceFile );
+            String destContent = FileUtils.fileRead( destFile );
+            assertEquals( sourceContent, destContent );
+        }
+        else
+        {
+            mockTransferListenerControl.verify();
+            
+            mockTransferListenerControl.reset();
+            
+            assertNull( "check checksum is null", checksumObserver.getActualChecksum() );
+            
+            assertFalse( destFile.exists() );
+        }
+    }
+
+    protected ProgressArgumentMatcher setupGetIfNewerTest( Wagon wagon, boolean expectedResult, int expectedSize )
+        throws NoSuchAlgorithmException, IOException
+    {
+        checksumObserver = new ChecksumObserver();
+        
+        destFile = FileTestUtils.createUniqueFile( getName(), getName() );
+        destFile.delete();
+        assertFalse( destFile.exists() );
+        destFile.deleteOnExit();
+        
+        ProgressArgumentMatcher progressArgumentMatcher = null;
+        if ( expectedResult == true )
+        {
+            progressArgumentMatcher = replaceMockForGet( wagon, expectedSize );
+        }
+        else
+        {
+            replaceMockForSkippedGetIfNewer( wagon, expectedSize );
+        }
+        return progressArgumentMatcher;
+    }
+
+    private void replaceMockForSkippedGetIfNewer( Wagon wagon, int expectedSize )
+    {
+        Resource resource = new Resource( this.resource );
+        mockTransferListener.transferInitiated( createTransferEvent( wagon, resource, TransferEvent.TRANSFER_INITIATED,
+                                                                     TransferEvent.REQUEST_GET, destFile ) );
+        resource = new Resource( this.resource );
+        resource.setContentLength( getExpectedContentLengthOnGet( expectedSize ) );
+        resource.setLastModified( getExpectedLastModifiedOnGet( testRepository, resource ) );
+        // TODO: transfer skipped event?
+//        mockTransferListener.transferSkipped( createTransferEvent( wagon, resource, TransferEvent.TRANSFER_STARTED,
+//                                                                   TransferEvent.REQUEST_GET, destFile ) );
+        
+        mockTransferListener.debug( null );
+        mockTransferListenerControl.setMatcher( MockControl.ALWAYS_MATCHER );
+        mockTransferListenerControl.setVoidCallable( MockControl.ZERO_OR_MORE );
+        
+        mockTransferListenerControl.replay();
+    }
+
     public void testWagonPutDirectory()
         throws Exception
     {
@@ -521,7 +659,7 @@
         try
         {
             wagon.get( "fubar.txt", destFile );
-            fail( "File was found when it sohuldn't have been" );
+            fail( "File was found when it shouldn't have been" );
         }
         catch ( ResourceDoesNotExistException e )
         {
@@ -538,6 +676,40 @@
         }
     }
 
+    public void testFailedGetIfNewer()
+        throws Exception
+    {
+        if ( supportsGetIfNewer() )
+        {
+            setupRepositories();
+            setupWagonTestingFixtures();
+            message( "Getting test artifact from test repository " + testRepository );
+            Wagon wagon = getWagon();
+            wagon.addTransferListener( checksumObserver );
+            wagon.connect( testRepository, getAuthInfo() );
+            destFile = FileTestUtils.createUniqueFile( getName(), getName() );
+            destFile.deleteOnExit();
+            try
+            {
+                wagon.getIfNewer( "fubar.txt", destFile, 0 );
+                fail( "File was found when it shouldn't have been" );
+            }
+            catch ( ResourceDoesNotExistException e )
+            {
+                // expected
+                assertTrue( true );
+            }
+            finally
+            {
+                wagon.removeTransferListener( checksumObserver );
+
+                wagon.disconnect();
+
+                tearDownWagonTestingFixtures();
+            }
+        }
+    }
+
     /**
      * Test {@link Wagon#getFileList(String)}.
      *
@@ -682,6 +854,21 @@
 
         Wagon wagon = getWagon();
 
+        ProgressArgumentMatcher progressArgumentMatcher = replayMockForPut( resourceName, content, wagon );
+        
+        message( "Putting test artifact: " + resourceName + " into test repository " + testRepository );
+
+        connectWagon( wagon );
+
+        wagon.put( sourceFile, resourceName );
+
+        disconnectWagon( wagon );
+        
+        verifyMock( progressArgumentMatcher, content.length() );
+    }
+
+    protected ProgressArgumentMatcher replayMockForPut( String resourceName, String content, Wagon wagon )
+    {
         Resource resource = new Resource( resourceName );
         mockTransferListener.transferInitiated( createTransferEvent( wagon, resource, TransferEvent.TRANSFER_INITIATED,
                                                                      TransferEvent.REQUEST_PUT, sourceFile ) );
@@ -704,31 +891,10 @@
                                                                      TransferEvent.REQUEST_PUT, sourceFile ) );
         
         mockTransferListenerControl.replay();
-        
-        message( "Putting test artifact: " + resourceName + " into test repository " + testRepository );
-
-        wagon.addTransferListener( checksumObserver );
-
-        wagon.addTransferListener( mockTransferListener );
-
-        wagon.connect( testRepository, getAuthInfo() );
-
-        wagon.put( sourceFile, resourceName );
-
-        wagon.removeTransferListener( mockTransferListener );
-        
-        wagon.removeTransferListener( checksumObserver );
-
-        wagon.disconnect();
-        
-        mockTransferListenerControl.verify();
-        
-        assertEquals( content.length(), progressArgumentMatcher.getSize() );
-        
-        mockTransferListenerControl.reset();
+        return progressArgumentMatcher;
     }
 
-    private TransferEvent createTransferEvent( Wagon wagon, Resource resource, int eventType, int requestType,
+    protected TransferEvent createTransferEvent( Wagon wagon, Resource resource, int eventType, int requestType,
                                                File file )
     {
         TransferEvent transferEvent = new TransferEvent( wagon, resource, eventType, requestType );
@@ -752,6 +918,50 @@
         
         Wagon wagon = getWagon();
 
+        ProgressArgumentMatcher progressArgumentMatcher = replaceMockForGet( wagon, expectedSize );
+        
+        message( "Getting test artifact from test repository " + testRepository );
+
+        connectWagon( wagon );
+
+        wagon.get( this.resource, destFile );
+
+        disconnectWagon( wagon );
+        
+        verifyMock( progressArgumentMatcher, expectedSize );
+    }
+
+    protected void verifyMock( ProgressArgumentMatcher progressArgumentMatcher, int length )
+    {
+        mockTransferListenerControl.verify();
+        
+        assertEquals( length, progressArgumentMatcher.getSize() );
+        
+        mockTransferListenerControl.reset();
+    }
+
+    protected void disconnectWagon( Wagon wagon )
+        throws ConnectionException
+    {
+        wagon.removeTransferListener( mockTransferListener );
+        
+        wagon.removeTransferListener( checksumObserver );
+
+        wagon.disconnect();
+    }
+
+    protected void connectWagon( Wagon wagon )
+        throws ConnectionException, AuthenticationException
+    {
+        wagon.addTransferListener( checksumObserver );
+        
+        wagon.addTransferListener( mockTransferListener );
+
+        wagon.connect( testRepository, getAuthInfo() );
+    }
+
+    protected ProgressArgumentMatcher replaceMockForGet( Wagon wagon, int expectedSize )
+    {
         Resource resource = new Resource( this.resource );
         mockTransferListener.transferInitiated( createTransferEvent( wagon, resource, TransferEvent.TRANSFER_INITIATED,
                                                                      TransferEvent.REQUEST_GET, destFile ) );
@@ -773,28 +983,7 @@
                                                                      TransferEvent.REQUEST_GET, destFile ) );
         
         mockTransferListenerControl.replay();
-        
-        message( "Getting test artifact from test repository " + testRepository );
-
-        wagon.addTransferListener( checksumObserver );
-        
-        wagon.addTransferListener( mockTransferListener );
-
-        wagon.connect( testRepository, getAuthInfo() );
-
-        wagon.get( this.resource, destFile );
-
-        wagon.removeTransferListener( mockTransferListener );
-        
-        wagon.removeTransferListener( checksumObserver );
-
-        wagon.disconnect();
-        
-        mockTransferListenerControl.verify();
-        
-        assertEquals( expectedSize, progressArgumentMatcher.getSize() );
-        
-        mockTransferListenerControl.reset();
+        return progressArgumentMatcher;
     }
 
     protected int getExpectedContentLengthOnGet( int expectedSize )
diff --git a/wagon-providers/pom.xml b/wagon-providers/pom.xml
index f33a282..097df35 100644
--- a/wagon-providers/pom.xml
+++ b/wagon-providers/pom.xml
@@ -1,31 +1,8 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one
-  ~ or more contributor license agreements.  See the NOTICE file
-  ~ distributed with this work for additional information
-  ~ regarding copyright ownership.  The ASF licenses this file
-  ~ to you 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <artifactId>wagon</artifactId>
     <groupId>org.apache.maven.wagon</groupId>
-    <version>1.0-rc1-SNAPSHOT</version>
+    <version>1.0-beta-3</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>wagon-providers</artifactId>
@@ -55,4 +32,4 @@
       <scope>test</scope>
     </dependency>
   </dependencies>
-</project>
+</project>
\ No newline at end of file
diff --git a/wagon-providers/wagon-file/pom.xml b/wagon-providers/wagon-file/pom.xml
index dbed3ed..b9792d0 100644
--- a/wagon-providers/wagon-file/pom.xml
+++ b/wagon-providers/wagon-file/pom.xml
@@ -1,31 +1,8 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one
-  ~ or more contributor license agreements.  See the NOTICE file
-  ~ distributed with this work for additional information
-  ~ regarding copyright ownership.  The ASF licenses this file
-  ~ to you 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <artifactId>wagon-providers</artifactId>
     <groupId>org.apache.maven.wagon</groupId>
-    <version>1.0-rc1-SNAPSHOT</version>
+    <version>1.0-beta-3</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>wagon-file</artifactId>
@@ -33,4 +10,4 @@
   <description>
     Wagon that gets and puts artifacts using file system protocol
   </description>
-</project>
+</project>
\ No newline at end of file
diff --git a/wagon-providers/wagon-file/src/main/java/org/apache/maven/wagon/providers/file/FileWagon.java b/wagon-providers/wagon-file/src/main/java/org/apache/maven/wagon/providers/file/FileWagon.java
index b8a32b0..42e2f2e 100644
--- a/wagon-providers/wagon-file/src/main/java/org/apache/maven/wagon/providers/file/FileWagon.java
+++ b/wagon-providers/wagon-file/src/main/java/org/apache/maven/wagon/providers/file/FileWagon.java
@@ -19,6 +19,17 @@
  * under the License.
  */
 
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.List;
+
 import org.apache.maven.wagon.ConnectionException;
 import org.apache.maven.wagon.InputData;
 import org.apache.maven.wagon.LazyFileOutputStream;
@@ -28,29 +39,15 @@
 import org.apache.maven.wagon.TransferFailedException;
 import org.apache.maven.wagon.authorization.AuthorizationException;
 import org.apache.maven.wagon.resource.Resource;
-import org.codehaus.plexus.util.StringUtils;
 import org.codehaus.plexus.util.FileUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.IOException;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.util.Arrays;
-import java.util.List;
+import org.codehaus.plexus.util.StringUtils;
 
 /**
  * Wagon Provider for Local File System
- *
+ * 
  * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
  * @version $Id$
- * 
- * @plexus.component role="org.apache.maven.wagon.Wagon" 
- *   role-hint="file"
- *   instantiation-strategy="per-lookup"
+ * @plexus.component role="org.apache.maven.wagon.Wagon" role-hint="file" instantiation-strategy="per-lookup"
  */
 public class FileWagon
     extends StreamWagon
@@ -74,7 +71,7 @@
 
         try
         {
-            InputStream in = new BufferedInputStream(new FileInputStream( file ));
+            InputStream in = new BufferedInputStream( new FileInputStream( file ) );
 
             inputData.setInputStream( in );
 
@@ -95,14 +92,14 @@
         {
             throw new TransferFailedException( "Unable to operate with a null basedir." );
         }
-        
+
         Resource resource = outputData.getResource();
 
         File file = new File( getRepository().getBasedir(), resource.getName() );
 
         createParentDirectories( file );
 
-        OutputStream outputStream = new BufferedOutputStream(new LazyFileOutputStream( file ));
+        OutputStream outputStream = new BufferedOutputStream( new LazyFileOutputStream( file ) );
 
         outputData.setOutputStream( outputStream );
     }
@@ -122,7 +119,7 @@
             return;
         }
 
-        // Check the File repository exists 
+        // Check the File repository exists
         File basedir = new File( getRepository().getBasedir() );
         if ( !basedir.exists() )
         {
@@ -160,16 +157,13 @@
 
         try
         {
-            /* Done to address issue found in HP-UX with regards to "." directory references.
-             * Details found in ..
-             * WAGON-30 - wagon-file failed when used by maven-site-plugin
-             * WAGON-33 - FileWagon#putDirectory() fails in HP-UX if destinationDirectory is "."
-             * http://www.nabble.com/With-maven-2.0.2-site%3Adeploy-doesn%27t-work-t934716.html for details.
-             * 
-             * Using path.getCanonicalFile() ensures that the path is fully 
-             * resolved before an attempt to create it.
-             * 
-             * TODO: consider moving this to FileUtils.mkdirs()
+            /*
+             * Done to address issue found in HP-UX with regards to "." directory references. Details found in ..
+             * WAGON-30 - wagon-file failed when used by maven-site-plugin WAGON-33 - FileWagon#putDirectory() fails in
+             * HP-UX if destinationDirectory is "."
+             * http://www.nabble.com/With-maven-2.0.2-site%3Adeploy-doesn%27t-work-t934716.html for details. Using
+             * path.getCanonicalFile() ensures that the path is fully resolved before an attempt to create it. TODO:
+             * consider moving this to FileUtils.mkdirs()
              */
             File realFile = path.getCanonicalFile();
             realFile.mkdirs();
@@ -225,13 +219,13 @@
     }
 
     public List getFileList( String destinationDirectory )
-        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException 
+        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
     {
         if ( getRepository().getBasedir() == null )
         {
             throw new TransferFailedException( "Unable to getFileList() with a null basedir." );
         }
-        
+
         File path = resolveDestinationPath( destinationDirectory );
 
         if ( !path.exists() )
@@ -256,7 +250,7 @@
         {
             throw new TransferFailedException( "Unable to getFileList() with a null basedir." );
         }
-        
+
         File file = resolveDestinationPath( resourceName );
 
         return file.exists();
diff --git a/wagon-providers/wagon-file/src/test/java/org/apache/maven/wagon/providers/file/FileWagonTest.java b/wagon-providers/wagon-file/src/test/java/org/apache/maven/wagon/providers/file/FileWagonTest.java
index e238b6c..e284da8 100644
--- a/wagon-providers/wagon-file/src/test/java/org/apache/maven/wagon/providers/file/FileWagonTest.java
+++ b/wagon-providers/wagon-file/src/test/java/org/apache/maven/wagon/providers/file/FileWagonTest.java
@@ -19,23 +19,23 @@
  * under the License.
  */
 
+import java.io.File;
+import java.io.IOException;
+
 import org.apache.maven.wagon.ConnectionException;
 import org.apache.maven.wagon.FileTestUtils;
+import org.apache.maven.wagon.StreamingWagonTestCase;
 import org.apache.maven.wagon.Wagon;
-import org.apache.maven.wagon.WagonTestCase;
 import org.apache.maven.wagon.authentication.AuthenticationException;
 import org.apache.maven.wagon.repository.Repository;
 import org.apache.maven.wagon.resource.Resource;
 
-import java.io.File;
-import java.io.IOException;
-
 /**
  * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
  * @version $Id$
  */
 public class FileWagonTest
-    extends WagonTestCase
+    extends StreamingWagonTestCase
 {
     protected String getProtocol()
     {
diff --git a/wagon-providers/wagon-ftp/pom.xml b/wagon-providers/wagon-ftp/pom.xml
index 0b546c2..ae206f6 100644
--- a/wagon-providers/wagon-ftp/pom.xml
+++ b/wagon-providers/wagon-ftp/pom.xml
@@ -1,31 +1,8 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one
-  ~ or more contributor license agreements.  See the NOTICE file
-  ~ distributed with this work for additional information
-  ~ regarding copyright ownership.  The ASF licenses this file
-  ~ to you 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <artifactId>wagon-providers</artifactId>
     <groupId>org.apache.maven.wagon</groupId>
-    <version>1.0-rc1-SNAPSHOT</version>
+    <version>1.0-beta-3</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>wagon-ftp</artifactId>
@@ -53,4 +30,4 @@
       </exclusions>
     </dependency>
   </dependencies>
-</project>
+</project>
\ No newline at end of file
diff --git a/wagon-providers/wagon-ftp/src/test/java/org/apache/maven/wagon/providers/ftp/FtpWagonTest.java b/wagon-providers/wagon-ftp/src/test/java/org/apache/maven/wagon/providers/ftp/FtpWagonTest.java
index 9856e55..2324060 100644
--- a/wagon-providers/wagon-ftp/src/test/java/org/apache/maven/wagon/providers/ftp/FtpWagonTest.java
+++ b/wagon-providers/wagon-ftp/src/test/java/org/apache/maven/wagon/providers/ftp/FtpWagonTest.java
@@ -22,8 +22,8 @@
 import java.io.File;
 
 import org.apache.ftpserver.interfaces.FtpServerInterface;
+import org.apache.maven.wagon.StreamingWagonTestCase;
 import org.apache.maven.wagon.Wagon;
-import org.apache.maven.wagon.WagonTestCase;
 import org.apache.maven.wagon.authentication.AuthenticationInfo;
 import org.apache.maven.wagon.repository.Repository;
 import org.apache.maven.wagon.resource.Resource;
@@ -33,7 +33,7 @@
  * @version $Id$
  */
 public class FtpWagonTest
-    extends WagonTestCase
+    extends StreamingWagonTestCase
 {
     private FtpServerInterface server;
 
diff --git a/wagon-providers/wagon-http-lightweight/pom.xml b/wagon-providers/wagon-http-lightweight/pom.xml
index 5684423..3f5a11d 100644
--- a/wagon-providers/wagon-http-lightweight/pom.xml
+++ b/wagon-providers/wagon-http-lightweight/pom.xml
@@ -1,31 +1,8 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one
-  ~ or more contributor license agreements.  See the NOTICE file
-  ~ distributed with this work for additional information
-  ~ regarding copyright ownership.  The ASF licenses this file
-  ~ to you 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <artifactId>wagon-providers</artifactId>
     <groupId>org.apache.maven.wagon</groupId>
-    <version>1.0-rc1-SNAPSHOT</version>
+    <version>1.0-beta-3</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
@@ -60,4 +37,4 @@
       </exclusions>
     </dependency>
   </dependencies>
-</project>
+</project>
\ No newline at end of file
diff --git a/wagon-providers/wagon-http-lightweight/src/main/java/org/apache/maven/wagon/providers/http/LightweightHttpWagon.java b/wagon-providers/wagon-http-lightweight/src/main/java/org/apache/maven/wagon/providers/http/LightweightHttpWagon.java
index 5d20c90..5a8e6af 100644
--- a/wagon-providers/wagon-http-lightweight/src/main/java/org/apache/maven/wagon/providers/http/LightweightHttpWagon.java
+++ b/wagon-providers/wagon-http-lightweight/src/main/java/org/apache/maven/wagon/providers/http/LightweightHttpWagon.java
@@ -19,10 +19,10 @@
  * under the License.
  */
 
-import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.net.Authenticator;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
@@ -170,14 +170,11 @@
         }
     }
 
-    public void put( File source, String resourceName )
-        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
+    protected void finishPutTransfer( Resource resource, InputStream input, OutputStream output )
+        throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
     {
-        super.put( source, resourceName );
-
         try
         {
-            String url = buildUrl( resourceName );
             int statusCode = putConnection.getResponseCode();
 
             switch ( statusCode )
@@ -190,20 +187,20 @@
                     break;
 
                 case HttpURLConnection.HTTP_FORBIDDEN:
-                    throw new AuthorizationException( "Access denied to: " + url );
+                    throw new AuthorizationException( "Access denied to: " + buildUrl( resource.getName() ) );
 
                 case HttpURLConnection.HTTP_NOT_FOUND:
-                    throw new ResourceDoesNotExistException( "File: " + url + " does not exist" );
+                    throw new ResourceDoesNotExistException( "File: " + buildUrl( resource.getName() ) + " does not exist" );
 
                 //add more entries here
                 default :
                     throw new TransferFailedException(
-                        "Failed to transfer file: " + url + ". Return code is: " + statusCode );
+                        "Failed to transfer file: " + buildUrl( resource.getName() ) + ". Return code is: " + statusCode );
             }
         }
         catch ( IOException e )
         {
-            fireTransferError( new Resource( resourceName ), e, TransferEvent.REQUEST_PUT );
+            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
             
             throw new TransferFailedException( "Error transferring file", e );
         }
diff --git a/wagon-providers/wagon-http-lightweight/src/test/java/org/apache/maven/wagon/providers/http/LightweightHttpWagonTest.java b/wagon-providers/wagon-http-lightweight/src/test/java/org/apache/maven/wagon/providers/http/LightweightHttpWagonTest.java
index c2eade5..e7d36e7 100644
--- a/wagon-providers/wagon-http-lightweight/src/test/java/org/apache/maven/wagon/providers/http/LightweightHttpWagonTest.java
+++ b/wagon-providers/wagon-http-lightweight/src/test/java/org/apache/maven/wagon/providers/http/LightweightHttpWagonTest.java
@@ -19,20 +19,20 @@
  * under the License.
  */
 
+import java.io.File;
+
 import org.apache.maven.wagon.FileTestUtils;
-import org.apache.maven.wagon.WagonTestCase;
+import org.apache.maven.wagon.StreamingWagonTestCase;
 import org.apache.maven.wagon.repository.Repository;
 import org.apache.maven.wagon.resource.Resource;
 import org.codehaus.plexus.jetty.Httpd;
 
-import java.io.File;
-
 /**
  * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
  * @version $Id$
  */
 public class LightweightHttpWagonTest
-    extends WagonTestCase
+    extends StreamingWagonTestCase
 {
     private Httpd httpd;
 
diff --git a/wagon-providers/wagon-http-shared/pom.xml b/wagon-providers/wagon-http-shared/pom.xml
index 068bdf8..60bfc45 100644
--- a/wagon-providers/wagon-http-shared/pom.xml
+++ b/wagon-providers/wagon-http-shared/pom.xml
@@ -1,31 +1,8 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one
-  ~ or more contributor license agreements.  See the NOTICE file
-  ~ distributed with this work for additional information
-  ~ regarding copyright ownership.  The ASF licenses this file
-  ~ to you 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.
--->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <artifactId>wagon-providers</artifactId>
     <groupId>org.apache.maven.wagon</groupId>
-    <version>1.0-rc1-SNAPSHOT</version>
+    <version>1.0-beta-3</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>wagon-http-shared</artifactId>
@@ -38,8 +15,19 @@
   <dependencies>
     <dependency>
       <groupId>nekohtml</groupId>
+      <artifactId>xercesMinimal</artifactId>
+      <version>1.9.6.2</version>
+    </dependency>
+    <dependency>
+      <groupId>nekohtml</groupId>
       <artifactId>nekohtml</artifactId>
-      <version>1.9.6.1</version>
+      <version>1.9.6.2</version>
+      <exclusions>
+        <exclusion>
+          <groupId>xerces</groupId>
+          <artifactId>xercesImpl</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
     <dependency>
       <groupId>commons-httpclient</groupId>
@@ -47,4 +35,4 @@
       <version>3.1</version>
     </dependency>
   </dependencies>
-</project>
+</project>
\ No newline at end of file
diff --git a/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/AbstractHttpClientWagon.java b/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/AbstractHttpClientWagon.java
index 3992111..1ffcb3d 100644
--- a/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/AbstractHttpClientWagon.java
+++ b/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/AbstractHttpClientWagon.java
@@ -20,9 +20,11 @@
  */
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.net.URLEncoder;
 import java.text.SimpleDateFormat;
 import java.util.Date;
@@ -46,11 +48,16 @@
 import org.apache.commons.httpclient.methods.GetMethod;
 import org.apache.commons.httpclient.methods.HeadMethod;
 import org.apache.commons.httpclient.methods.PutMethod;
+import org.apache.commons.httpclient.methods.RequestEntity;
 import org.apache.commons.httpclient.util.DateParseException;
 import org.apache.commons.httpclient.util.DateParser;
-import org.apache.maven.wagon.AbstractWagon;
+import org.apache.maven.wagon.InputData;
+import org.apache.maven.wagon.OutputData;
+import org.apache.maven.wagon.PathUtils;
 import org.apache.maven.wagon.ResourceDoesNotExistException;
+import org.apache.maven.wagon.StreamWagon;
 import org.apache.maven.wagon.TransferFailedException;
+import org.apache.maven.wagon.Wagon;
 import org.apache.maven.wagon.authorization.AuthorizationException;
 import org.apache.maven.wagon.events.TransferEvent;
 import org.apache.maven.wagon.proxy.ProxyInfo;
@@ -63,8 +70,71 @@
  * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
  * @author <a href="mailto:james@atlassian.com">James William Dumay</a>
  */
-public abstract class AbstractHttpClientWagon extends AbstractWagon
+public abstract class AbstractHttpClientWagon
+    extends StreamWagon
 {
+    private final class RequestEntityImplementation
+        implements RequestEntity
+    {
+        private final InputStream stream;
+        
+        private final Resource resource;
+
+        private final Wagon wagon;
+
+        private RequestEntityImplementation( InputStream stream, Resource resource, Wagon wagon )
+        {
+            this.stream = stream;
+            
+            this.resource = resource;
+            
+            this.wagon = wagon;
+        }
+
+        public long getContentLength()
+        {
+            return resource.getContentLength();
+        }
+
+        public String getContentType()
+        {
+            return null;
+        }
+
+        public boolean isRepeatable()
+        {
+            return false;
+        }
+
+        public void writeRequest( OutputStream output )
+            throws IOException
+        {
+            byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
+            
+            TransferEvent transferEvent =
+                new TransferEvent( wagon, resource, TransferEvent.TRANSFER_PROGRESS, TransferEvent.REQUEST_PUT );
+            transferEvent.setTimestamp( System.currentTimeMillis() );
+            
+            int remaining = Integer.MAX_VALUE;
+            while ( remaining > 0 )
+            {
+                int n = stream.read( buffer, 0, Math.min( buffer.length, remaining ) );
+            
+                if ( n == -1 )
+                {
+                    break;
+                }
+            
+                fireTransferProgress( transferEvent, buffer, n );
+            
+                output.write( buffer, 0, n );
+            
+                remaining -= n;
+            }
+            output.flush();
+        }
+    }
+
     protected static final int SC_NULL = -1;
 
     protected static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone( "GMT" );
@@ -75,6 +145,8 @@
 
     private Properties httpHeaders;
 
+    private HttpMethod getMethod;
+
     public void openConnectionInternal()
     {
         repository.setUrl( getURL( repository ) );
@@ -142,218 +214,52 @@
     {
     }
 
-    public void get( String resourceName, File destination )
-        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-    {
-        get( resourceName, destination, 0 );
-    }
-
-    public boolean getIfNewer( String resourceName, File destination, long timestamp )
-        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-    {
-        return get( resourceName, destination, timestamp );
-    }
-
-    /**
-     * @param resourceName
-     * @param destination
-     * @param timestamp the timestamp to check against, only downloading if newer. If <code>0</code>, always download
-     * @return <code>true</code> if newer version was downloaded, <code>false</code> otherwise.
-     * @throws TransferFailedException
-     * @throws ResourceDoesNotExistException
-     * @throws AuthorizationException
-     */
-    public boolean get( String resourceName, File destination, long timestamp )
-        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-    {
-        Resource resource = new Resource( resourceName );
-        fireGetInitiated( resource, destination );
-        boolean retValue = false;
-        String url = getRepository().getUrl() + "/" + resourceName;
-        GetMethod getMethod = new GetMethod( url );;
-
-        try
-        {
-            if ( timestamp > 0 )
-            {
-                SimpleDateFormat fmt = new SimpleDateFormat( "EEE, dd-MMM-yy HH:mm:ss zzz", Locale.US );
-                fmt.setTimeZone( GMT_TIME_ZONE );
-                Header hdr = new Header( "If-Modified-Since", fmt.format( new Date( timestamp ) ) );
-                fireTransferDebug( "sending ==> " + hdr + "(" + timestamp + ")" );
-                getMethod.addRequestHeader( hdr );
-            }
-
-            int statusCode;
-            try
-            {
-                statusCode = execute( getMethod );
-            }
-            catch ( IOException e )
-            {
-                fireTransferError( resource, e, TransferEvent.REQUEST_GET );
-
-                throw new TransferFailedException( e.getMessage(), e );
-            }
-
-            fireTransferDebug( url + " - Status code: " + statusCode );
-
-            // TODO [BP]: according to httpclient docs, really should swallow the output on error. verify if that is required
-            switch ( statusCode )
-            {
-                case HttpStatus.SC_OK:
-                    break;
-
-                case HttpStatus.SC_NOT_MODIFIED:
-                    return false;
-
-                case SC_NULL:
-                {
-                    TransferFailedException e = new TransferFailedException( "Failed to transfer file: " + url );
-                    fireTransferError( resource, e, TransferEvent.REQUEST_GET );
-                    throw e;
-                }
-
-                case HttpStatus.SC_FORBIDDEN:
-                    fireSessionConnectionRefused();
-                    throw new AuthorizationException( "Access denied to: " + url );
-
-                case HttpStatus.SC_UNAUTHORIZED:
-                    fireSessionConnectionRefused();
-                    throw new AuthorizationException( "Not authorized." );
-
-                case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
-                    fireSessionConnectionRefused();
-                    throw new AuthorizationException( "Not authorized by proxy." );
-
-                case HttpStatus.SC_NOT_FOUND:
-                    throw new ResourceDoesNotExistException( "File: " + url + " does not exist" );
-
-                //add more entries here
-                default :
-                {
-                    TransferFailedException e =
-                            new TransferFailedException( "Failed to transfer file: " + url + ". Return code is: "
-                                + statusCode );
-                    fireTransferError( resource, e, TransferEvent.REQUEST_GET );
-                    throw e;
-                }
-            }
-
-            InputStream is = null;
-
-            Header contentLengthHeader = getMethod.getResponseHeader( "Content-Length" );
-
-            if ( contentLengthHeader != null )
-            {
-                try
-                {
-                    long contentLength = Integer.valueOf( contentLengthHeader.getValue() ).intValue();
-
-                    resource.setContentLength( contentLength );
-                }
-                catch ( NumberFormatException e )
-                {
-                    fireTransferDebug(
-                        "error parsing content length header '" + contentLengthHeader.getValue() + "' " + e );
-                }
-            }
-
-            Header lastModifiedHeader = getMethod.getResponseHeader( "Last-Modified" );
-
-            long lastModified = 0;
-
-            if ( lastModifiedHeader != null )
-            {
-                try
-                {
-                    lastModified = DateParser.parseDate( lastModifiedHeader.getValue() ).getTime();
-                    
-                    resource.setLastModified( lastModified );
-                }
-                catch ( DateParseException e )
-                {
-                    fireTransferDebug( "Unable to parse last modified header" );
-                }
-
-                fireTransferDebug( "last-modified = " + lastModifiedHeader.getValue() + " (" + lastModified + ")" );
-            }
-
-            // always get if timestamp is 0 (ie, target doesn't exist), otherwise only if older than the remote file
-            if ( timestamp == 0 || timestamp < lastModified )
-            {
-                retValue = true;
-
-                Header contentEncoding = getMethod.getResponseHeader( "Content-Encoding" );
-                boolean isGZipped = contentEncoding == null ? false : "gzip".equalsIgnoreCase(contentEncoding.getValue());
-
-                try
-                {
-                    is = getMethod.getResponseBodyAsStream();
-                    if (isGZipped)
-                    {
-                        is = new GZIPInputStream( is );
-                    }
-
-                    getTransfer( resource, destination, is );
-                }
-                catch ( IOException e )
-                {
-                    fireTransferError( resource, e, TransferEvent.REQUEST_GET );
-
-                    if ( destination.exists() )
-                    {
-                        boolean deleted = destination.delete();
-
-                        if ( !deleted )
-                        {
-                            destination.deleteOnExit();
-                        }
-                    }
-
-                    String msg = "Error occurred while deploying to remote repository:" + getRepository();
-
-                    throw new TransferFailedException( msg, e );
-                }
-                finally
-                {
-                    IOUtil.close( is );
-                }
-
-                if ( lastModified > 0 )
-                {
-                    resource.setLastModified( lastModified );
-                }
-            }
-            else
-            {
-                fireTransferDebug( "Local file is newer: not downloaded" );
-            }
-
-            return retValue;
-        }
-        finally
-        {
-            getMethod.releaseConnection();
-        }
-    }    
-
     public void put( File source, String resourceName )
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
     {
         Resource resource = new Resource( resourceName );
         
         firePutInitiated( resource, source );
-
-        put(source, resource);
-    }
-
-    protected void put( File source, Resource resource )
-        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-    {
+        
         resource.setContentLength( source.length() );
         
         resource.setLastModified( source.lastModified() );
+
+        InputStream stream = null;
+        try
+        {
+            stream = new FileInputStream( source );
+            put( stream, resource, source );
+        }
+        catch ( FileNotFoundException e )
+        {
+            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
+
+            throw new TransferFailedException( "Specified source file does not exist: " + source, e );
+        }
+        finally
+        {
+            IOUtil.close( stream );
+        }
+    }
+    
+    public void putFromStream( final InputStream stream, String destination, long contentLength, long lastModified )
+        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
+    {
+        Resource resource = new Resource( destination );
         
+        firePutInitiated( resource, null );
+        
+        resource.setContentLength( contentLength );
+        
+        resource.setLastModified( lastModified );
+        
+        put( stream, resource, null );
+    }
+
+    private void put( final InputStream stream, Resource resource, File source )
+        throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
+    {
         String url = getRepository().getUrl();
         String[] parts = StringUtils.split( resource.getName(), "/" );
         for ( int i = 0; i < parts.length; i++ )
@@ -361,25 +267,24 @@
             url += "/" + URLEncoder.encode( parts[i] );
         }
 
+        //Parent directories need to be created before posting
+        try
+        {
+            mkdirs( PathUtils.dirname( resource.getName() ) );
+        }
+        catch ( IOException e )
+        {
+            fireTransferError( resource, e, TransferEvent.REQUEST_GET );
+        }
+
         PutMethod putMethod = new PutMethod( url );
-        InputStream is = null;
+
+        putMethod.setRequestEntity( new RequestEntityImplementation( stream, resource, this ) );
+
+        firePutStarted( resource, source );
+                
         try
         {
-            is = new PutInputStream( source, resource, this, getTransferEventSupport() );
-        }
-        catch ( FileNotFoundException e )
-        {
-            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
-
-            throw new ResourceDoesNotExistException( "Source file does not exist: " + source, e );
-        }
-
-        putMethod.setRequestBody( is );
-
-        try
-        {
-            firePutStarted( resource, source );
-            
             int statusCode;
             try
             {
@@ -433,11 +338,13 @@
         }
         finally
         {
-            IOUtil.close(is);
-            
             putMethod.releaseConnection();
         }
     }
+    
+    protected void mkdirs( String dirname ) throws HttpException, IOException
+    {
+    }
 
     public boolean resourceExists( String resourceName )
         throws TransferFailedException, AuthorizationException
@@ -546,4 +453,158 @@
     {
         this.httpHeaders = httpHeaders;
     }
+
+    public void fillInputData( InputData inputData )
+        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
+    {
+        Resource resource = inputData.getResource();
+        
+        String url = getRepository().getUrl() + "/" + resource.getName();
+        getMethod = new GetMethod( url );
+        long timestamp = resource.getLastModified();
+        if ( timestamp > 0 )
+        {
+            SimpleDateFormat fmt = new SimpleDateFormat( "EEE, dd-MMM-yy HH:mm:ss zzz", Locale.US );
+            fmt.setTimeZone( GMT_TIME_ZONE );
+            Header hdr = new Header( "If-Modified-Since", fmt.format( new Date( timestamp ) ) );
+            fireTransferDebug( "sending ==> " + hdr + "(" + timestamp + ")" );
+            getMethod.addRequestHeader( hdr );
+        }
+
+        int statusCode;
+        try
+        {
+            statusCode = execute( getMethod );
+        }
+        catch ( IOException e )
+        {
+            fireTransferError( resource, e, TransferEvent.REQUEST_GET );
+
+            throw new TransferFailedException( e.getMessage(), e );
+        }
+
+        fireTransferDebug( url + " - Status code: " + statusCode );
+
+        // TODO [BP]: according to httpclient docs, really should swallow the output on error. verify if that is
+        // required
+        switch ( statusCode )
+        {
+            case HttpStatus.SC_OK:
+                break;
+
+            case HttpStatus.SC_NOT_MODIFIED:
+                // return, leaving last modified set to original value so getIfNewer should return unmodified
+                return;
+
+            case SC_NULL:
+            {
+                TransferFailedException e = new TransferFailedException( "Failed to transfer file: " + url );
+                fireTransferError( resource, e, TransferEvent.REQUEST_GET );
+                throw e;
+            }
+
+            case HttpStatus.SC_FORBIDDEN:
+                fireSessionConnectionRefused();
+                throw new AuthorizationException( "Access denied to: " + url );
+
+            case HttpStatus.SC_UNAUTHORIZED:
+                fireSessionConnectionRefused();
+                throw new AuthorizationException( "Not authorized." );
+
+            case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
+                fireSessionConnectionRefused();
+                throw new AuthorizationException( "Not authorized by proxy." );
+
+            case HttpStatus.SC_NOT_FOUND:
+                throw new ResourceDoesNotExistException( "File: " + url + " does not exist" );
+
+                // add more entries here
+            default:
+            {
+                cleanupGetTransfer( resource );
+                TransferFailedException e =
+                    new TransferFailedException( "Failed to transfer file: " + url + ". Return code is: "
+                        + statusCode );
+                fireTransferError( resource, e, TransferEvent.REQUEST_GET );
+                throw e;
+            }
+        }
+
+        InputStream is = null;
+
+        Header contentLengthHeader = getMethod.getResponseHeader( "Content-Length" );
+
+        if ( contentLengthHeader != null )
+        {
+            try
+            {
+                long contentLength = Integer.valueOf( contentLengthHeader.getValue() ).intValue();
+
+                resource.setContentLength( contentLength );
+            }
+            catch ( NumberFormatException e )
+            {
+                fireTransferDebug( "error parsing content length header '" + contentLengthHeader.getValue() + "' "
+                    + e );
+            }
+        }
+
+        Header lastModifiedHeader = getMethod.getResponseHeader( "Last-Modified" );
+
+        long lastModified = 0;
+
+        if ( lastModifiedHeader != null )
+        {
+            try
+            {
+                lastModified = DateParser.parseDate( lastModifiedHeader.getValue() ).getTime();
+
+                resource.setLastModified( lastModified );
+            }
+            catch ( DateParseException e )
+            {
+                fireTransferDebug( "Unable to parse last modified header" );
+            }
+
+            fireTransferDebug( "last-modified = " + lastModifiedHeader.getValue() + " (" + lastModified + ")" );
+        }
+
+        Header contentEncoding = getMethod.getResponseHeader( "Content-Encoding" );
+        boolean isGZipped =
+            contentEncoding == null ? false : "gzip".equalsIgnoreCase( contentEncoding.getValue() );
+
+        try
+        {
+            is = getMethod.getResponseBodyAsStream();
+            if ( isGZipped )
+            {
+                is = new GZIPInputStream( is );
+            }
+        }
+        catch ( IOException e )
+        {
+            fireTransferError( resource, e, TransferEvent.REQUEST_GET );
+
+            String msg =
+                "Error occurred while retrieving from remote repository:" + getRepository() + ": " + e.getMessage();
+
+            throw new TransferFailedException( msg, e );
+        }
+        
+        inputData.setInputStream( is );
+    }
+
+    protected void cleanupGetTransfer( Resource resource )
+    {
+        if ( getMethod != null )
+        {
+            getMethod.releaseConnection();
+        }
+    }
+
+    public void fillOutputData( OutputData outputData )
+        throws TransferFailedException
+    {
+        throw new IllegalStateException( "Should not be using the streaming wagon for HTTP PUT" );        
+    }
 }
diff --git a/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/HtmlFileListParser.java b/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/HtmlFileListParser.java
index bfb906d..0ba6652 100644
--- a/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/HtmlFileListParser.java
+++ b/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/HtmlFileListParser.java
@@ -27,44 +27,25 @@
 import java.net.URLDecoder;
 import java.util.ArrayList;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 import java.util.regex.Pattern;
 
 import org.apache.maven.wagon.TransferFailedException;
+import org.apache.xerces.xni.Augmentations;
+import org.apache.xerces.xni.QName;
+import org.apache.xerces.xni.XMLAttributes;
+import org.apache.xerces.xni.parser.XMLInputSource;
+import org.apache.xerces.xni.parser.XMLParserConfiguration;
 import org.codehaus.plexus.util.StringUtils;
-import org.cyberneko.html.parsers.DOMParser;
-import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXNotRecognizedException;
-import org.xml.sax.SAXNotSupportedException;
+import org.cyberneko.html.HTMLConfiguration;
+import org.cyberneko.html.filters.DefaultFilter;
 
 /**
  * Html File List Parser.
  */
 public class HtmlFileListParser
 {
-    private static final Set/*<Pattern>*/skips = new HashSet/*<Pattern>*/();
-
-    static
-    {
-        // Apache Fancy Index Sort Headers
-        skips.add( Pattern.compile( "\\?[CDMNS]=.*" ) );
-
-        // URLs with excessive paths.
-        skips.add( Pattern.compile( "/[^/]*/" ) );
-
-        // URLs that to a parent directory.
-        skips.add( Pattern.compile( "\\.\\./" ) );
-
-        // mailto urls
-        skips.add( Pattern.compile( "mailto:.*" ) );
-    }
-
     /**
      * Fetches a raw HTML from a provided InputStream, parses it, and returns the file list.
      * 
@@ -72,7 +53,7 @@
      * @return the file list.
      * @throws TransferFailedException if there was a problem fetching the raw html.
      */
-    public static List/*<String>*/parseFileList( String baseurl, InputStream stream )
+    public static List/* <String> */parseFileList( String baseurl, InputStream stream )
         throws TransferFailedException
     {
         try
@@ -80,55 +61,68 @@
             // Use URI object to get benefits of proper absolute and relative path resolution for free
             URI baseURI = new URI( baseurl );
 
-            DOMParser parser = new DOMParser();
+            Parser handler = new Parser( baseURI );
+
+            XMLParserConfiguration parser = new HTMLConfiguration();
+            parser.setDocumentHandler( handler );
             parser.setFeature( "http://cyberneko.org/html/features/augmentations", true );
             parser.setProperty( "http://cyberneko.org/html/properties/names/elems", "upper" );
             parser.setProperty( "http://cyberneko.org/html/properties/names/attrs", "upper" );
-            parser.parse( new InputSource( stream ) );
+            parser.parse( new XMLInputSource( null, baseurl, baseURI.toString(), stream, "UTF-8" ) );
 
-            Set/*<String>*/links = new HashSet/*<String>*/();
-
-            recursiveLinkCollector( parser.getDocument(), baseURI, links );
-
-            return new ArrayList( links );
+            return new ArrayList( handler.getLinks() );
 
         }
         catch ( URISyntaxException e )
         {
             throw new TransferFailedException( "Unable to parse as URI: " + baseurl );
         }
-        catch ( SAXNotRecognizedException e )
-        {
-            throw new TransferFailedException( "Unable to setup XML/SAX: " + e.getMessage(), e );
-        }
-        catch ( SAXNotSupportedException e )
-        {
-            throw new TransferFailedException( "XML/SAX not supported?: " + e.getMessage(), e );
-        }
-        catch ( SAXException e )
-        {
-            throw new TransferFailedException( "XML/SAX error: " + e.getMessage(), e );
-        }
         catch ( IOException e )
         {
             throw new TransferFailedException( "I/O error: " + e.getMessage(), e );
         }
     }
 
-    private static void recursiveLinkCollector( Node node, URI baseURI, Set/*<String>*/links )
+    private static class Parser
+        extends DefaultFilter
     {
-        if ( node.getNodeType() == Node.ELEMENT_NODE )
+        // Apache Fancy Index Sort Headers
+        private static final Pattern APACHE_INDEX_SKIP = Pattern.compile( "\\?[CDMNS]=.*" );
+
+        // URLs with excessive paths.
+        private static final Pattern URLS_WITH_PATHS = Pattern.compile( "/[^/]*/" );
+
+        // URLs that to a parent directory.
+        private static final Pattern URLS_TO_PARENT = Pattern.compile( "\\.\\./" );
+
+        // mailto urls
+        private static final Pattern MAILTO_URLS = Pattern.compile( "mailto:.*" );
+
+        private static final Pattern[] SKIPS =
+            new Pattern[] { APACHE_INDEX_SKIP, URLS_WITH_PATHS, URLS_TO_PARENT, MAILTO_URLS };
+        
+        private Set links = new HashSet();
+
+        private URI baseURI;
+
+        public Parser( URI baseURI )
         {
-            //            System.out.println("Element <" + node.getNodeName() + dumpAttributes((Element) node) + ">");
-            if ( "A".equals( node.getNodeName() ) )
+            this.baseURI = baseURI;
+        }
+
+        public Set getLinks()
+        {
+            return links;
+        }
+
+        public void startElement( QName element, XMLAttributes attrs, Augmentations augs )
+        {
+            if ( "A".equals( element.rawname ) )
             {
-                Element anchor = (Element) node;
-                NamedNodeMap nodemap = anchor.getAttributes();
-                Node href = nodemap.getNamedItem( "HREF" );
+                String href = attrs.getValue( "HREF" );
                 if ( href != null )
                 {
-                    String link = cleanLink( baseURI, href.getNodeValue() );
-                    //                    System.out.println("HREF (" + href.getNodeValue() + " => " + link + ")");
+                    String link = cleanLink( baseURI, href );
                     if ( isAcceptableLink( link ) )
                     {
                         links.add( link );
@@ -137,76 +131,53 @@
             }
         }
 
-        Node child = node.getFirstChild();
-        while ( child != null )
+        private static String cleanLink( URI baseURI, String link )
         {
-            recursiveLinkCollector( child, baseURI, links );
-            child = child.getNextSibling();
-        }
-    }
-
-    //    private String dumpAttributes(Element elem) {
-    //        StringBuffer buf = new StringBuffer();
-    //        NamedNodeMap nodemap = elem.getAttributes();
-    //        int len = nodemap.getLength();
-    //        for (int i = 0; i < len; i++) {
-    //            Node att = nodemap.item(i);
-    //            buf.append(" ");
-    //            buf.append(att.getNodeName()).append("=\"");
-    //            buf.append(att.getNodeValue()).append("\"");
-    //        }
-    //        return buf.toString();
-    //    }
-
-    private static String cleanLink( URI baseURI, String link )
-    {
-        if ( StringUtils.isEmpty( link ) )
-        {
-            return "";
-        }
-
-        String ret = link;
-
-        try
-        {
-            URI linkuri = new URI( ret );
-            URI relativeURI = baseURI.relativize( linkuri ).normalize();
-            ret = relativeURI.toASCIIString();
-            if ( ret.startsWith( baseURI.getPath() ) )
+            if ( StringUtils.isEmpty( link ) )
             {
-                ret = ret.substring( baseURI.getPath().length() );
+                return "";
             }
-            
-            ret = URLDecoder.decode( ret, "UTF-8" );
-        }
-        catch ( URISyntaxException e )
-        {
-        }
-        catch ( UnsupportedEncodingException e )
-        {
+
+            String ret = link;
+
+            try
+            {
+                URI linkuri = new URI( ret );
+                URI relativeURI = baseURI.relativize( linkuri ).normalize();
+                ret = relativeURI.toASCIIString();
+                if ( ret.startsWith( baseURI.getPath() ) )
+                {
+                    ret = ret.substring( baseURI.getPath().length() );
+                }
+
+                ret = URLDecoder.decode( ret, "UTF-8" );
+            }
+            catch ( URISyntaxException e )
+            {
+            }
+            catch ( UnsupportedEncodingException e )
+            {
+            }
+
+            return ret;
         }
 
-        return ret;
-    }
-    
-    
-
-    private static boolean isAcceptableLink( String link )
-    {
-        if ( StringUtils.isEmpty( link ) )
+        private static boolean isAcceptableLink( String link )
         {
-            return false;
-        }
-
-        for ( Iterator it = skips.iterator(); it.hasNext(); )
-        {
-            Pattern skipPat = (Pattern) it.next();
-            if ( skipPat.matcher( link ).find() )
+            if ( StringUtils.isEmpty( link ) )
             {
                 return false;
             }
-        }
 
-        return true;
+            for ( int i = 0; i < SKIPS.length; i++ )
+            {
+                if ( SKIPS[i].matcher( link ).find() )
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
     }
 }
diff --git a/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/PutInputStream.java b/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/PutInputStream.java
deleted file mode 100644
index 5c0cf30..0000000
--- a/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/PutInputStream.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package org.apache.maven.wagon.shared.http;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-import org.apache.maven.wagon.Wagon;
-import org.apache.maven.wagon.events.TransferEvent;
-import org.apache.maven.wagon.events.TransferEventSupport;
-import org.apache.maven.wagon.resource.Resource;
-
-/**
- * Put Input Stream is borrowed from wagon-http, and is used to properly
- * notify the listeners of transfer events on a put request.
- * 
- * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
- * @todo move to wagon-providers-api
- */
-public class PutInputStream
-    extends FileInputStream
-{
-    private TransferEventSupport eventSupport;
-
-    private TransferEvent event;
-
-    public PutInputStream( File file, Resource resource, Wagon wagon, TransferEventSupport eventSupport )
-        throws FileNotFoundException
-    {
-        super( file );
-
-        this.eventSupport = eventSupport;
-
-        event = new TransferEvent( wagon, resource, TransferEvent.TRANSFER_PROGRESS, TransferEvent.REQUEST_PUT );
-
-        event.setLocalFile( file );
-    }
-
-    public int read( byte buffer[] )
-        throws IOException
-    {
-        return read( buffer, 0, buffer.length );
-    }
-
-    public int read()
-        throws IOException
-    {
-        byte buffer[] = new byte[1];
-
-        return read( buffer );
-    }
-
-    public int read( byte buffer[], int offset, int length )
-        throws IOException
-    {
-        int retValue = super.read( buffer, offset, length );
-
-        if ( retValue > 0 )
-        {
-            event.setTimestamp( System.currentTimeMillis() );
-
-            eventSupport.fireTransferProgress( event, buffer, retValue );
-        }
-        return retValue;
-    }
-}
diff --git a/wagon-providers/wagon-http-shared/src/test/java/org/apache/maven/wagon/shared/http/PutInputStreamTest.java b/wagon-providers/wagon-http-shared/src/test/java/org/apache/maven/wagon/shared/http/PutInputStreamTest.java
deleted file mode 100644
index 3182f0f..0000000
--- a/wagon-providers/wagon-http-shared/src/test/java/org/apache/maven/wagon/shared/http/PutInputStreamTest.java
+++ /dev/null
@@ -1,160 +0,0 @@
-package org.apache.maven.wagon.shared.http;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Collections;
-import java.util.List;
-
-import junit.framework.TestCase;
-
-import org.apache.maven.wagon.AbstractWagon;
-import org.apache.maven.wagon.ConnectionException;
-import org.apache.maven.wagon.FileTestUtils;
-import org.apache.maven.wagon.ResourceDoesNotExistException;
-import org.apache.maven.wagon.TransferFailedException;
-import org.apache.maven.wagon.authentication.AuthenticationException;
-import org.apache.maven.wagon.authorization.AuthorizationException;
-import org.apache.maven.wagon.resource.Resource;
-import org.codehaus.plexus.util.FileUtils;
-import org.codehaus.plexus.util.IOUtil;
-
-/**
- * PutInputStream Test
- * 
- * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
- */
-public class PutInputStreamTest
-    extends TestCase
-{
-    private static final int BUFFER_SIZE = 8096;
-    
-    public void testStream() throws IOException, NoSuchAlgorithmException
-    {
-        File sourceFile = new File( FileTestUtils.getTestOutputDir(), "test-source" );
-        FileUtils.fileWrite( sourceFile.getAbsolutePath(), "test-source.txt\n" );
-        
-        File destFile = new File( FileTestUtils.getTestOutputDir(), "test-dest" );
-        
-        Resource resource = new Resource();
-        AbstractWagon wagon = new NullWagon();
-        
-        InputStream input = new PutInputStream(sourceFile, resource, wagon, wagon.getTransferEventSupport() );
-        
-        FileOutputStream output = new FileOutputStream(destFile);
-        byte buf[] = new byte[BUFFER_SIZE];
-        int n = -1;
-        while( (n = input.read(buf)) != (-1))
-        {
-            output.write(buf, 0, n);
-        }
-        IOUtil.close(output);
-        IOUtil.close(input);
-        
-        String sourceHash = generateHash(sourceFile);
-        String destHash = generateHash(destFile);
-        
-        assertEquals("Hash comparision", sourceHash, destHash);
-    }
-    
-    private String generateHash(File file) throws NoSuchAlgorithmException, IOException
-    {
-        MessageDigest md = MessageDigest.getInstance( "SHA1" );
-        
-        byte buffer[] = new byte[BUFFER_SIZE];
-        FileInputStream fis = new FileInputStream(file);
-        md.reset();
-        
-        int size = fis.read( buffer, 0, BUFFER_SIZE );
-        while ( size >= 0 )
-        {
-            md.update( buffer, 0, size );
-            size = fis.read( buffer, 0, BUFFER_SIZE );
-        }
-        
-        return toHex(md.digest());
-    }
-    
-    private String toHex( byte buf[] )
-    {
-        final char pseudo[] = new char[] {
-            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
-
-        char[] hash = new char[buf.length * 2];
-        for ( int i = 0; i < buf.length; i++ )
-        {
-            hash[i * 2] = ( pseudo[( buf[i] & 0xF0 ) >> 4] );
-            hash[( i * 2 ) + 1] = ( pseudo[( buf[i] & 0x0F )] );
-        }
-        return new String( hash );
-    }
-    
-    
-    public class NullWagon extends AbstractWagon
-    {
-        protected void closeConnection()
-            throws ConnectionException
-        {
-                        
-        }
-
-        public void get( String resourceName, File destination )
-            throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-        {
-            
-        }
-
-        public boolean getIfNewer( String resourceName, File destination, long timestamp )
-            throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-        {
-            return false;
-        }
-
-        public void put( File source, String destination )
-            throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-        {
-            
-        }
-
-        public void openConnectionInternal()
-            throws ConnectionException, AuthenticationException
-        {
-
-        }
-
-        public List getFileList( String destinationDirectory )
-            throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-        {
-            return Collections.EMPTY_LIST;
-        }
-
-        public boolean resourceExists( String resourceName )
-            throws TransferFailedException, AuthorizationException
-        {
-            return false;
-        }
-    }
-}
diff --git a/wagon-providers/wagon-http/pom.xml b/wagon-providers/wagon-http/pom.xml
index 82ed781..70101e3 100644
--- a/wagon-providers/wagon-http/pom.xml
+++ b/wagon-providers/wagon-http/pom.xml
@@ -1,31 +1,8 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one
-  ~ or more contributor license agreements.  See the NOTICE file
-  ~ distributed with this work for additional information
-  ~ regarding copyright ownership.  The ASF licenses this file
-  ~ to you 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <artifactId>wagon-providers</artifactId>
     <groupId>org.apache.maven.wagon</groupId>
-    <version>1.0-rc1-SNAPSHOT</version>
+    <version>1.0-beta-3</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>wagon-http</artifactId>
@@ -38,7 +15,7 @@
     <dependency>
       <groupId>${pom.groupId}</groupId>
       <artifactId>wagon-http-shared</artifactId>
-      <version>1.0-rc1-SNAPSHOT</version>
+      <version>1.0-beta-3</version>
     </dependency>
     <dependency>
       <groupId>plexus</groupId>
@@ -109,4 +86,4 @@
     </plugins>
   </build>
     -->
-</project>
+</project>
\ No newline at end of file
diff --git a/wagon-providers/wagon-http/src/test/java/org/apache/maven/wagon/providers/http/HttpWagonTest.java b/wagon-providers/wagon-http/src/test/java/org/apache/maven/wagon/providers/http/HttpWagonTest.java
index 7358cc0..8bdd08a 100644
--- a/wagon-providers/wagon-http/src/test/java/org/apache/maven/wagon/providers/http/HttpWagonTest.java
+++ b/wagon-providers/wagon-http/src/test/java/org/apache/maven/wagon/providers/http/HttpWagonTest.java
@@ -19,20 +19,20 @@
  * under the License.
  */
 
+import java.io.File;
+
 import org.apache.maven.wagon.FileTestUtils;
-import org.apache.maven.wagon.WagonTestCase;
+import org.apache.maven.wagon.StreamingWagonTestCase;
 import org.apache.maven.wagon.repository.Repository;
 import org.apache.maven.wagon.resource.Resource;
 import org.codehaus.plexus.jetty.Httpd;
 
-import java.io.File;
-
 /**
  * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
  * @version $Id$
  */
 public class HttpWagonTest
-    extends WagonTestCase
+    extends StreamingWagonTestCase
 {
     private Httpd httpd;
 
diff --git a/wagon-providers/wagon-scm/pom.xml b/wagon-providers/wagon-scm/pom.xml
index d36bcb8..0e2c5e9 100644
--- a/wagon-providers/wagon-scm/pom.xml
+++ b/wagon-providers/wagon-scm/pom.xml
@@ -1,12 +1,8 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project 
-  xmlns="http://maven.apache.org/POM/4.0.0" 
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <artifactId>wagon-providers</artifactId>
     <groupId>org.apache.maven.wagon</groupId>
-    <version>1.0-rc1-SNAPSHOT</version>
+    <version>1.0-beta-3</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>wagon-scm</artifactId>
@@ -60,4 +56,4 @@
       </plugin>
     </plugins>
   </build>
-</project>
+</project>
\ No newline at end of file
diff --git a/wagon-providers/wagon-scm/src/test/java/org/apache/maven/wagon/providers/scm/AbstractScmSvnWagonTest.java b/wagon-providers/wagon-scm/src/test/java/org/apache/maven/wagon/providers/scm/AbstractScmSvnWagonTest.java
index d2942f5..3708f7b 100644
--- a/wagon-providers/wagon-scm/src/test/java/org/apache/maven/wagon/providers/scm/AbstractScmSvnWagonTest.java
+++ b/wagon-providers/wagon-scm/src/test/java/org/apache/maven/wagon/providers/scm/AbstractScmSvnWagonTest.java
@@ -71,4 +71,9 @@
     {
         return repository;
     }
+
+    protected boolean supportsGetIfNewer()
+    {
+        return false;
+    }
 }
diff --git a/wagon-providers/wagon-ssh-common-test/pom.xml b/wagon-providers/wagon-ssh-common-test/pom.xml
index 089fa71..f6151eb 100644
--- a/wagon-providers/wagon-ssh-common-test/pom.xml
+++ b/wagon-providers/wagon-ssh-common-test/pom.xml
@@ -1,31 +1,8 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one
-  ~ or more contributor license agreements.  See the NOTICE file
-  ~ distributed with this work for additional information
-  ~ regarding copyright ownership.  The ASF licenses this file
-  ~ to you 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <artifactId>wagon-providers</artifactId>
     <groupId>org.apache.maven.wagon</groupId>
-    <version>1.0-rc1-SNAPSHOT</version>
+    <version>1.0-beta-3</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>wagon-ssh-common-test</artifactId>
@@ -52,4 +29,4 @@
       <scope>compile</scope>
     </dependency>
   </dependencies>
-</project>
+</project>
\ No newline at end of file
diff --git a/wagon-providers/wagon-ssh-common/pom.xml b/wagon-providers/wagon-ssh-common/pom.xml
index 89d0e35..c67445a 100644
--- a/wagon-providers/wagon-ssh-common/pom.xml
+++ b/wagon-providers/wagon-ssh-common/pom.xml
@@ -1,31 +1,8 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one
-  ~ or more contributor license agreements.  See the NOTICE file
-  ~ distributed with this work for additional information
-  ~ regarding copyright ownership.  The ASF licenses this file
-  ~ to you 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <artifactId>wagon-providers</artifactId>
     <groupId>org.apache.maven.wagon</groupId>
-    <version>1.0-rc1-SNAPSHOT</version>
+    <version>1.0-beta-3</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>wagon-ssh-common</artifactId>
@@ -39,6 +16,12 @@
     <dependency>
       <groupId>org.codehaus.plexus</groupId>
       <artifactId>plexus-interactivity-api</artifactId>
+      <exclusions>
+        <exclusion>
+          <groupId>org.codehaus.plexus</groupId>
+          <artifactId>plexus-component-api</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
   </dependencies>
-</project>
+</project>
\ No newline at end of file
diff --git a/wagon-providers/wagon-ssh-common/src/main/java/org/apache/maven/wagon/providers/ssh/AbstractSshWagon.java b/wagon-providers/wagon-ssh-common/src/main/java/org/apache/maven/wagon/providers/ssh/AbstractSshWagon.java
deleted file mode 100644
index b90a8f2..0000000
--- a/wagon-providers/wagon-ssh-common/src/main/java/org/apache/maven/wagon/providers/ssh/AbstractSshWagon.java
+++ /dev/null
@@ -1,380 +0,0 @@
-package org.apache.maven.wagon.providers.ssh;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.apache.maven.wagon.AbstractWagon;
-import org.apache.maven.wagon.CommandExecutionException;
-import org.apache.maven.wagon.CommandExecutor;
-import org.apache.maven.wagon.PathUtils;
-import org.apache.maven.wagon.PermissionModeUtils;
-import org.apache.maven.wagon.ResourceDoesNotExistException;
-import org.apache.maven.wagon.Streams;
-import org.apache.maven.wagon.TransferFailedException;
-import org.apache.maven.wagon.WagonConstants;
-import org.apache.maven.wagon.authentication.AuthenticationException;
-import org.apache.maven.wagon.authentication.AuthenticationInfo;
-import org.apache.maven.wagon.authorization.AuthorizationException;
-import org.apache.maven.wagon.events.TransferEvent;
-import org.apache.maven.wagon.repository.RepositoryPermissions;
-import org.apache.maven.wagon.resource.Resource;
-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.util.List;
-
-/**
- * Common SSH operations.
- *
- * @author <a href="mailto:brett@apache.org">Brett Porter</a>
- * @version $Id$
- * @todo cache pass[words|phases]
- * @todo move permissions tools to repositorypermissionsutils
- */
-public abstract class AbstractSshWagon
-    extends AbstractWagon
-    implements CommandExecutor
-{
-    protected static final char PATH_SEPARATOR = '/';
-
-    protected static final int DEFAULT_SSH_PORT = 22;
-
-    public boolean getIfNewer( String resourceName, File destination, long timestamp )
-        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-    {
-        fireSessionDebug( "getIfNewer in SCP wagon is not supported - performing an unconditional get" );
-        get( resourceName, destination );
-        return true;
-    }
-
-    protected String getOctalMode( RepositoryPermissions permissions )
-    {
-        String mode = null;
-        if ( permissions != null && permissions.getFileMode() != null )
-        {
-            if ( permissions.getFileMode().matches( "[0-9]{3,4}" ) )
-            {
-                mode = permissions.getFileMode();
-
-                if ( mode.length() == 3 )
-                {
-                    mode = "0" + mode;
-                }
-            }
-            else
-            {
-                // TODO: calculate?
-                // TODO: as warning
-                fireSessionDebug( "Not using non-octal permissions: " + mode );
-            }
-        }
-        return mode;
-    }
-
-    /**
-     * @param permissions repository's permissions
-     * @return the directory mode for the repository or <code>-1</code> if it
-     *         wasn't set
-     */
-    protected int getDirectoryMode( RepositoryPermissions permissions )
-    {
-        int ret = -1;
-
-        if ( permissions != null )
-        {
-            ret = getOctalMode( permissions.getDirectoryMode() );
-        }
-
-        return ret;
-    }
-
-    protected int getOctalMode( String mode )
-    {
-        int ret;
-        try
-        {
-            ret = Integer.valueOf( mode, 8 ).intValue();
-        }
-        catch ( NumberFormatException e )
-        {
-            // TODO: warning level
-            fireTransferDebug( "the file mode must be a numerical mode for SFTP" );
-            ret = -1;
-        }
-        return ret;
-    }
-
-    protected static String getResourceDirectory( String resourceName )
-    {
-        String dir = PathUtils.dirname( resourceName );
-        dir = StringUtils.replace( dir, "\\", "/" );
-        return dir;
-    }
-
-    protected static String getResourceFilename( String r )
-    {
-        String filename;
-        if ( r.lastIndexOf( PATH_SEPARATOR ) > 0 )
-        {
-            filename = r.substring( r.lastIndexOf( PATH_SEPARATOR ) + 1 );
-        }
-        else
-        {
-            filename = r;
-        }
-        return filename;
-    }
-
-    protected static Resource getResource( String resourceName )
-    {
-        String r = StringUtils.replace( resourceName, "\\", "/" );
-        return new Resource( r );
-    }
-
-    protected void openConnectionInternal()
-        throws AuthenticationException
-    {
-        if ( authenticationInfo == null )
-        {
-            authenticationInfo = new AuthenticationInfo();
-        }
-    }
-
-    public void closeConnection()
-    {
-        // nothing to disconnect
-    }
-
-    protected File getPrivateKey() throws FileNotFoundException
-    {
-        // If user don't define a password, he want to use a private key
-        File privateKey = null;
-        if ( authenticationInfo.getPassword() == null )
-        {
-
-            if ( authenticationInfo.getPrivateKey() != null )
-            {
-                privateKey = new File( authenticationInfo.getPrivateKey() );
-                if ( !privateKey.exists() )
-                {
-                    throw new FileNotFoundException( "Private key '" + privateKey + "' not found" );
-                }
-            }
-            else
-            {
-                privateKey = findPrivateKey();
-            }
-
-            if ( privateKey != null && privateKey.exists() )
-            {
-                if ( authenticationInfo.getPassphrase() == null )
-                {
-                    authenticationInfo.setPassphrase( "" );
-                }
-
-                fireSessionDebug( "Using private key: " + privateKey );
-            }
-        }
-        return privateKey;
-    }
-
-    protected int getPort()
-    {
-        int port = getRepository().getPort();
-
-        if ( port == WagonConstants.UNKNOWN_PORT )
-        {
-            port = DEFAULT_SSH_PORT;
-        }
-        return port;
-    }
-
-    private File findPrivateKey()
-    {
-        String privateKeyDirectory = System.getProperty( "wagon.privateKeyDirectory" );
-
-        if ( privateKeyDirectory == null )
-        {
-            privateKeyDirectory = System.getProperty( "user.home" );
-        }
-
-        File privateKey = new File( privateKeyDirectory, ".ssh/id_dsa" );
-
-        if ( !privateKey.exists() )
-        {
-            privateKey = new File( privateKeyDirectory, ".ssh/id_rsa" );
-            if ( !privateKey.exists() )
-            {
-                privateKey = null;
-            }
-        }
-
-        return privateKey;
-    }
-
-    public void executeCommand( String command )
-        throws CommandExecutionException
-    {
-        fireTransferDebug( "Executing command: " + command );
-
-        executeCommand( command, false );
-    }
-    
-    public void putDirectory( File sourceDirectory, String destinationDirectory )
-        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-    {
-        String basedir = getRepository().getBasedir();
-
-        String destDir = StringUtils.replace( destinationDirectory, "\\", "/" );
-
-        String path = getPath( basedir, destDir );
-        try
-        {
-            if ( getRepository().getPermissions() != null )
-            {
-                String dirPerms = getRepository().getPermissions().getDirectoryMode();
-
-                if ( dirPerms != null )
-                {
-                    String umaskCmd = "umask " + PermissionModeUtils.getUserMaskFor( dirPerms );
-                    executeCommand( umaskCmd );
-                }
-            }
-
-            String mkdirCmd = "mkdir -p " + path;
-
-            executeCommand( mkdirCmd );
-        }
-        catch ( CommandExecutionException e )
-        {
-            throw new TransferFailedException( "Error performing commands for file transfer", e );
-        }
-
-        File zipFile;
-        try
-        {
-            zipFile = File.createTempFile( "wagon", ".zip" );
-            zipFile.deleteOnExit();
-
-            List files = FileUtils.getFileNames( sourceDirectory, "**/**", "", false );
-
-            createZip( files, zipFile, sourceDirectory );
-        }
-        catch ( IOException e )
-        {
-            throw new TransferFailedException( "Unable to create ZIP archive of directory", e );
-        }
-
-        put( zipFile, getPath( destDir, zipFile.getName() ) );
-
-        try
-        {
-            executeCommand( "cd " + path + "; unzip -q -o " + zipFile.getName() + "; rm -f " + zipFile.getName() );
-
-            zipFile.delete();
-
-            RepositoryPermissions permissions = getRepository().getPermissions();
-
-            if ( permissions != null && permissions.getGroup() != null )
-            {
-                executeCommand( "chgrp -Rf " + permissions.getGroup() + " " + path );
-            }
-
-            if ( permissions != null && permissions.getFileMode() != null )
-            {
-                executeCommand( "chmod -Rf " + permissions.getFileMode() + " " + path );
-            }
-        }
-        catch ( CommandExecutionException e )
-        {
-            throw new TransferFailedException( "Error performing commands for file transfer", e );
-        }
-    }
-
-    public boolean supportsDirectoryCopy()
-    {
-        return true;
-    }
-
-    public List getFileList( String destinationDirectory )
-        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-    {
-        try
-        {
-            String path = getPath( getRepository().getBasedir(), destinationDirectory );
-            Streams streams = executeCommand( "ls -la " + path, false );
-            
-            return new LSParser().parseFiles( streams.getOut() );
-        }
-        catch ( CommandExecutionException e )
-        {
-            if ( e.getMessage().trim().endsWith( "No such file or directory" ) )
-            {
-                throw new ResourceDoesNotExistException( e.getMessage().trim() );
-            }
-            else
-            {
-                throw new TransferFailedException( "Error performing file listing.", e );
-            }
-        }
-    }
-
-    public boolean resourceExists( String resourceName )
-        throws TransferFailedException, AuthorizationException
-    {
-        try
-        {
-            String path = getPath( getRepository().getBasedir(), resourceName );
-            executeCommand( "ls " + path );
-
-            // Parsing of output not really needed.  As a failed ls results in a
-            // CommandExectionException on the 'ls' command.
-
-            return true;
-        }
-        catch ( CommandExecutionException e )
-        {
-            // Error?  Then the 'ls' command failed.  No such file found.
-            return false;
-        }
-    }
-
-    protected void handleGetException( Resource resource, Exception e, File destination )
-        throws TransferFailedException, ResourceDoesNotExistException
-    {
-        fireTransferError( resource, e, TransferEvent.REQUEST_GET );
-
-        if ( destination.exists() )
-        {
-            boolean deleted = destination.delete();
-
-            if ( !deleted )
-            {
-                destination.deleteOnExit();
-            }
-        }
-
-        String msg = "Error occured while downloading '" + resource + "' from the remote repository:" + getRepository() + ": " + e.getMessage();
-
-        throw new TransferFailedException( msg, e );
-    }
-}
diff --git a/wagon-providers/wagon-ssh-common/src/main/java/org/apache/maven/wagon/providers/ssh/LSParser.java b/wagon-providers/wagon-ssh-common/src/main/java/org/apache/maven/wagon/providers/ssh/LSParser.java
index f2386be..ab9ce26 100644
--- a/wagon-providers/wagon-ssh-common/src/main/java/org/apache/maven/wagon/providers/ssh/LSParser.java
+++ b/wagon-providers/wagon-ssh-common/src/main/java/org/apache/maven/wagon/providers/ssh/LSParser.java
@@ -19,9 +19,6 @@
  * under the License.
  */
 
-import org.apache.maven.wagon.TransferFailedException;
-import org.codehaus.plexus.util.StringUtils;
-
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.StringReader;
@@ -30,6 +27,8 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.apache.maven.wagon.TransferFailedException;
+
 /**
  * LSParser 
  *
diff --git a/wagon-providers/wagon-ssh-common/src/main/java/org/apache/maven/wagon/providers/ssh/ScpHelper.java b/wagon-providers/wagon-ssh-common/src/main/java/org/apache/maven/wagon/providers/ssh/ScpHelper.java
index 965cfdc..16067e5 100644
--- a/wagon-providers/wagon-ssh-common/src/main/java/org/apache/maven/wagon/providers/ssh/ScpHelper.java
+++ b/wagon-providers/wagon-ssh-common/src/main/java/org/apache/maven/wagon/providers/ssh/ScpHelper.java
@@ -19,19 +19,304 @@
  * under the License.
  */
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
 import org.apache.maven.wagon.CommandExecutionException;
 import org.apache.maven.wagon.CommandExecutor;
+import org.apache.maven.wagon.PathUtils;
 import org.apache.maven.wagon.PermissionModeUtils;
+import org.apache.maven.wagon.ResourceDoesNotExistException;
+import org.apache.maven.wagon.Streams;
+import org.apache.maven.wagon.TransferFailedException;
+import org.apache.maven.wagon.Wagon;
+import org.apache.maven.wagon.authentication.AuthenticationInfo;
+import org.apache.maven.wagon.authorization.AuthorizationException;
+import org.apache.maven.wagon.repository.Repository;
 import org.apache.maven.wagon.repository.RepositoryPermissions;
+import org.apache.maven.wagon.resource.Resource;
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.StringUtils;
 
 public class ScpHelper
 {
-    private ScpHelper()
+    public static final char PATH_SEPARATOR = '/';
+
+    public static final int DEFAULT_SSH_PORT = 22;
+    
+    private final CommandExecutor executor;
+    
+    public ScpHelper( CommandExecutor executor )
     {
+        this.executor = executor;
     }
 
-    public static void createRemoteDirectories( String path, RepositoryPermissions permissions,
-                                                CommandExecutor commandExecutor )
+    public static String getResourceDirectory( String resourceName )
+    {
+        String dir = PathUtils.dirname( resourceName );
+        dir = StringUtils.replace( dir, "\\", "/" );
+        return dir;
+    }
+
+    public static String getResourceFilename( String r )
+    {
+        String filename;
+        if ( r.lastIndexOf( PATH_SEPARATOR ) > 0 )
+        {
+            filename = r.substring( r.lastIndexOf( PATH_SEPARATOR ) + 1 );
+        }
+        else
+        {
+            filename = r;
+        }
+        return filename;
+    }
+
+    public static Resource getResource( String resourceName )
+    {
+        String r = StringUtils.replace( resourceName, "\\", "/" );
+        return new Resource( r );
+    }
+
+    public static File getPrivateKey( AuthenticationInfo authenticationInfo )
+        throws FileNotFoundException
+    {
+        // If user don't define a password, he want to use a private key
+        File privateKey = null;
+        if ( authenticationInfo.getPassword() == null )
+        {
+
+            if ( authenticationInfo.getPrivateKey() != null )
+            {
+                privateKey = new File( authenticationInfo.getPrivateKey() );
+                if ( !privateKey.exists() )
+                {
+                    throw new FileNotFoundException( "Private key '" + privateKey + "' not found" );
+                }
+            }
+            else
+            {
+                privateKey = findPrivateKey();
+            }
+
+            if ( privateKey != null && privateKey.exists() )
+            {
+                if ( authenticationInfo.getPassphrase() == null )
+                {
+                    authenticationInfo.setPassphrase( "" );
+                }
+            }
+        }
+        return privateKey;
+    }
+
+    private static File findPrivateKey()
+    {
+        String privateKeyDirectory = System.getProperty( "wagon.privateKeyDirectory" );
+
+        if ( privateKeyDirectory == null )
+        {
+            privateKeyDirectory = System.getProperty( "user.home" );
+        }
+
+        File privateKey = new File( privateKeyDirectory, ".ssh/id_dsa" );
+
+        if ( !privateKey.exists() )
+        {
+            privateKey = new File( privateKeyDirectory, ".ssh/id_rsa" );
+            if ( !privateKey.exists() )
+            {
+                privateKey = null;
+            }
+        }
+
+        return privateKey;
+    }
+    
+    public static void createZip( List files, File zipName, File basedir )
+        throws IOException
+    {
+        ZipOutputStream zos = new ZipOutputStream( new FileOutputStream( zipName ) );
+
+        try
+        {
+            for ( int i = 0; i < files.size(); i++ )
+            {
+                String file = (String) files.get( i );
+
+                file = file.replace( '\\', '/' );
+
+                writeZipEntry( zos, new File( basedir, file ), file );
+            }
+        }
+        finally
+        {
+            IOUtil.close( zos );
+        }
+    }
+
+    private static void writeZipEntry( ZipOutputStream jar, File source, String entryName )
+        throws IOException
+    {
+        byte[] buffer = new byte[1024];
+
+        int bytesRead;
+
+        FileInputStream is = new FileInputStream( source );
+
+        try
+        {
+            ZipEntry entry = new ZipEntry( entryName );
+
+            jar.putNextEntry( entry );
+
+            while ( ( bytesRead = is.read( buffer ) ) != -1 )
+            {
+                jar.write( buffer, 0, bytesRead );
+            }
+        }
+        finally
+        {
+            is.close();
+        }
+    }
+
+    protected static String getPath( String basedir, String dir )
+    {
+        String path;
+        path = basedir;
+        if ( !basedir.endsWith( "/" ) && !dir.startsWith( "/" ) )
+        {
+            path += "/";
+        }
+        path += dir;
+        return path;
+    }
+
+    public void putDirectory( Wagon wagon, File sourceDirectory, String destinationDirectory )
+        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
+    {
+        Repository repository = wagon.getRepository();
+        
+        String basedir = repository.getBasedir();
+
+        String destDir = StringUtils.replace( destinationDirectory, "\\", "/" );
+
+        String path = getPath( basedir, destDir );
+        try
+        {
+            if ( repository.getPermissions() != null )
+            {
+                String dirPerms = repository.getPermissions().getDirectoryMode();
+
+                if ( dirPerms != null )
+                {
+                    String umaskCmd = "umask " + PermissionModeUtils.getUserMaskFor( dirPerms );
+                    executor.executeCommand( umaskCmd );
+                }
+            }
+
+            String mkdirCmd = "mkdir -p " + path;
+
+            executor.executeCommand( mkdirCmd );
+        }
+        catch ( CommandExecutionException e )
+        {
+            throw new TransferFailedException( "Error performing commands for file transfer", e );
+        }
+
+        File zipFile;
+        try
+        {
+            zipFile = File.createTempFile( "wagon", ".zip" );
+            zipFile.deleteOnExit();
+
+            List files = FileUtils.getFileNames( sourceDirectory, "**/**", "", false );
+
+            createZip( files, zipFile, sourceDirectory );
+        }
+        catch ( IOException e )
+        {
+            throw new TransferFailedException( "Unable to create ZIP archive of directory", e );
+        }
+
+        wagon.put( zipFile, getPath( destDir, zipFile.getName() ) );
+
+        try
+        {
+            executor.executeCommand( "cd " + path + "; unzip -q -o " + zipFile.getName() + "; rm -f " + zipFile.getName() );
+
+            zipFile.delete();
+
+            RepositoryPermissions permissions = repository.getPermissions();
+
+            if ( permissions != null && permissions.getGroup() != null )
+            {
+                executor.executeCommand( "chgrp -Rf " + permissions.getGroup() + " " + path );
+            }
+
+            if ( permissions != null && permissions.getFileMode() != null )
+            {
+                executor.executeCommand( "chmod -Rf " + permissions.getFileMode() + " " + path );
+            }
+        }
+        catch ( CommandExecutionException e )
+        {
+            throw new TransferFailedException( "Error performing commands for file transfer", e );
+        }
+    }
+
+    public List getFileList( String destinationDirectory, Repository repository )
+        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
+    {
+        try
+        {
+            String path = getPath( repository.getBasedir(), destinationDirectory );
+            Streams streams = executor.executeCommand( "ls -la " + path, false );
+            
+            return new LSParser().parseFiles( streams.getOut() );
+        }
+        catch ( CommandExecutionException e )
+        {
+            if ( e.getMessage().trim().endsWith( "No such file or directory" ) )
+            {
+                throw new ResourceDoesNotExistException( e.getMessage().trim() );
+            }
+            else
+            {
+                throw new TransferFailedException( "Error performing file listing.", e );
+            }
+        }
+    }
+
+    public boolean resourceExists( String resourceName, Repository repository )
+        throws TransferFailedException, AuthorizationException
+    {
+        try
+        {
+            String path = getPath( repository.getBasedir(), resourceName );
+            executor.executeCommand( "ls " + path );
+
+            // Parsing of output not really needed.  As a failed ls results in a
+            // CommandExectionException on the 'ls' command.
+
+            return true;
+        }
+        catch ( CommandExecutionException e )
+        {
+            // Error?  Then the 'ls' command failed.  No such file found.
+            return false;
+        }
+    }
+
+    public void createRemoteDirectories( String path, RepositoryPermissions permissions )
         throws CommandExecutionException
     {
         String umaskCmd = null;
@@ -52,6 +337,6 @@
             mkdirCmd = umaskCmd + "; " + mkdirCmd;
         }
 
-        commandExecutor.executeCommand( mkdirCmd );
+        executor.executeCommand( mkdirCmd );
     }
 }
diff --git a/wagon-providers/wagon-ssh-external/pom.xml b/wagon-providers/wagon-ssh-external/pom.xml
index a161440..d61d1ce 100644
--- a/wagon-providers/wagon-ssh-external/pom.xml
+++ b/wagon-providers/wagon-ssh-external/pom.xml
@@ -1,31 +1,8 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one
-  ~ or more contributor license agreements.  See the NOTICE file
-  ~ distributed with this work for additional information
-  ~ regarding copyright ownership.  The ASF licenses this file
-  ~ to you 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <artifactId>wagon-providers</artifactId>
     <groupId>org.apache.maven.wagon</groupId>
-    <version>1.0-rc1-SNAPSHOT</version>
+    <version>1.0-beta-3</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>wagon-ssh-external</artifactId>
@@ -34,19 +11,30 @@
     Wagon that gets and puts artifacts using SSH protocol with a preinstalled SSH client
   </description>
 
-  <build>
-    <plugins>
-      <plugin>
-        <artifactId>maven-surefire-plugin</artifactId>
-        <configuration>
-          <excludes>
-            <exclude>**/Scp*Test.*</exclude>
-            <exclude>**/SshCommandExecutorTest.*</exclude>
-          </excludes>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
+  <profiles>
+    <profile>
+      <id>no-ssh-tests</id>
+      <activation>
+        <property>
+          <name>!ssh-tests</name>
+        </property>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <configuration>
+              <!-- Tests that require an ssh server on localhost -->
+              <excludes>
+                <exclude>**/SshCommandExecutorTest.*</exclude>
+                <exclude>**/Scp*Test.*</exclude>
+              </excludes>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
   <dependencies>
     <dependency>
       <groupId>org.codehaus.plexus</groupId>
@@ -62,4 +50,4 @@
       <scope>test</scope>
     </dependency>
   </dependencies>
-</project>
+</project>
\ No newline at end of file
diff --git a/wagon-providers/wagon-ssh-external/src/main/java/org/apache/maven/wagon/providers/ssh/external/ScpExternalWagon.java b/wagon-providers/wagon-ssh-external/src/main/java/org/apache/maven/wagon/providers/ssh/external/ScpExternalWagon.java
index 28e363d..fbd144a 100644
--- a/wagon-providers/wagon-ssh-external/src/main/java/org/apache/maven/wagon/providers/ssh/external/ScpExternalWagon.java
+++ b/wagon-providers/wagon-ssh-external/src/main/java/org/apache/maven/wagon/providers/ssh/external/ScpExternalWagon.java
@@ -21,21 +21,24 @@
 
 import java.io.File;
 import java.io.FileNotFoundException;
-import java.io.IOException;
 import java.util.List;
 
+import org.apache.maven.wagon.AbstractWagon;
 import org.apache.maven.wagon.CommandExecutionException;
+import org.apache.maven.wagon.CommandExecutor;
 import org.apache.maven.wagon.PathUtils;
 import org.apache.maven.wagon.PermissionModeUtils;
 import org.apache.maven.wagon.ResourceDoesNotExistException;
 import org.apache.maven.wagon.Streams;
 import org.apache.maven.wagon.TransferFailedException;
+import org.apache.maven.wagon.WagonConstants;
+import org.apache.maven.wagon.authentication.AuthenticationException;
+import org.apache.maven.wagon.authentication.AuthenticationInfo;
 import org.apache.maven.wagon.authorization.AuthorizationException;
 import org.apache.maven.wagon.events.TransferEvent;
-import org.apache.maven.wagon.providers.ssh.AbstractSshWagon;
+import org.apache.maven.wagon.providers.ssh.ScpHelper;
 import org.apache.maven.wagon.repository.RepositoryPermissions;
 import org.apache.maven.wagon.resource.Resource;
-import org.codehaus.plexus.util.FileUtils;
 import org.codehaus.plexus.util.StringUtils;
 import org.codehaus.plexus.util.cli.CommandLineException;
 import org.codehaus.plexus.util.cli.CommandLineUtils;
@@ -54,7 +57,8 @@
  *   instantiation-strategy="per-lookup"
  */
 public class ScpExternalWagon
-    extends AbstractSshWagon
+    extends AbstractWagon
+    implements CommandExecutor
 {
     /**
      * The external SCP command to use - default is <code>scp</code>.
@@ -84,12 +88,36 @@
      */
     private String sshArgs;
 
+    private ScpHelper sshTool = new ScpHelper( this );
+
     private static final int SSH_FATAL_EXIT_CODE = 255;
 
     // ----------------------------------------------------------------------
     //
     // ----------------------------------------------------------------------
 
+    protected void openConnectionInternal()
+        throws AuthenticationException
+    {
+        if ( authenticationInfo == null )
+        {
+            authenticationInfo = new AuthenticationInfo();
+        }
+    }
+
+    public void closeConnection()
+    {
+        // nothing to disconnect
+    }
+
+    public boolean getIfNewer( String resourceName, File destination, long timestamp )
+        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
+    {
+        fireSessionDebug( "getIfNewer in SCP wagon is not supported - performing an unconditional get" );
+        get( resourceName, destination );
+        return true;
+    }
+
     /**
      * @return The hostname of the remote server prefixed with the username, which comes either from the repository URL
      *         or from the authenticationInfo.
@@ -112,6 +140,14 @@
         }
     }
     
+    public void executeCommand( String command )
+        throws CommandExecutionException
+    {
+        fireTransferDebug( "Executing command: " + command );
+
+        executeCommand( command, false );
+    }
+
     public Streams executeCommand( String command, boolean ignoreFailures )
         throws CommandExecutionException
     {
@@ -120,7 +156,7 @@
         File privateKey;
         try
         {
-            privateKey = getPrivateKey();
+            privateKey = ScpHelper.getPrivateKey( authenticationInfo );
         }
         catch ( FileNotFoundException e )
         {
@@ -128,8 +164,9 @@
         }
         Commandline cl = createBaseCommandLine( putty, sshExecutable, privateKey );
 
-        int port = getPort();
-        if ( port != DEFAULT_SSH_PORT )
+        int port =
+            repository.getPort() == WagonConstants.UNKNOWN_PORT ? ScpHelper.DEFAULT_SSH_PORT : repository.getPort();
+        if ( port != ScpHelper.DEFAULT_SSH_PORT )
         {
             if ( putty )
             {
@@ -220,7 +257,7 @@
         File privateKey;
         try
         {
-            privateKey = getPrivateKey();
+            privateKey = ScpHelper.getPrivateKey( authenticationInfo );
         }
         catch ( FileNotFoundException e )
         {
@@ -232,8 +269,9 @@
 
         cl.setWorkingDirectory( localFile.getParentFile().getAbsolutePath() );
 
-        int port = getPort();
-        if ( port != DEFAULT_SSH_PORT )
+        int port =
+            repository.getPort() == WagonConstants.UNKNOWN_PORT ? ScpHelper.DEFAULT_SSH_PORT : repository.getPort();
+        if ( port != ScpHelper.DEFAULT_SSH_PORT )
         {
             cl.createArgument().setLine( "-P " + port );
         }
@@ -246,6 +284,8 @@
         String resourceName = normalizeResource( resource );
         String remoteFile = getRepository().getBasedir() + "/" + resourceName;
         
+        remoteFile = StringUtils.replace( remoteFile, " ", "\\ " );
+        
         String qualifiedRemoteFile = this.buildRemoteHost() + ":" + remoteFile;
         if ( put )
         {
@@ -377,12 +417,6 @@
         firePutCompleted( resource, source );
     }
 
-    public void executeCommand( String command )
-        throws CommandExecutionException
-    {
-        executeCommand( command, false );
-    }
-
     public void get( String resourceName, File destination )
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
     {
@@ -410,6 +444,29 @@
     // method on a Wagon.
     //
 
+    public List getFileList( String destinationDirectory )
+        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
+    {
+        return sshTool.getFileList( destinationDirectory, repository );
+    }
+
+    public void putDirectory( File sourceDirectory, String destinationDirectory )
+        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
+    {
+        sshTool.putDirectory( this, sourceDirectory, destinationDirectory );
+    }
+
+    public boolean resourceExists( String resourceName )
+        throws TransferFailedException, AuthorizationException
+    {
+        return sshTool.resourceExists( resourceName, repository );
+    }
+
+    public boolean supportsDirectoryCopy()
+    {
+        return true;
+    }
+
     public String getScpExecutable()
     {
         return scpExecutable;
@@ -449,75 +506,4 @@
     {
         this.sshArgs = sshArgs;
     }
-
-    public void putDirectory( File sourceDirectory, String destinationDirectory )
-        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-    {
-        String basedir = getRepository().getBasedir();
-
-        String dir = StringUtils.replace( destinationDirectory, "\\", "/" );
-
-        String path = getPath( basedir, dir );
-        try
-        {
-            if ( getRepository().getPermissions() != null )
-            {
-                String dirPerms = getRepository().getPermissions().getDirectoryMode();
-
-                if ( dirPerms != null )
-                {
-                    String umaskCmd = "umask " + PermissionModeUtils.getUserMaskFor( dirPerms );
-                    executeCommand( umaskCmd );
-                }
-            }
-
-            String mkdirCmd = "mkdir -p " + path;
-
-            executeCommand( mkdirCmd );
-        }
-        catch ( CommandExecutionException e )
-        {
-            throw new TransferFailedException( "Error performing commands for file transfer", e );
-        }
-
-        File zipFile;
-        try
-        {
-            zipFile = File.createTempFile( "wagon", ".zip" );
-            zipFile.deleteOnExit();
-
-            List files = FileUtils.getFileNames( sourceDirectory, "**/**", "", false );
-
-            createZip( files, zipFile, sourceDirectory );
-        }
-        catch ( IOException e )
-        {
-            throw new TransferFailedException( "Unable to create ZIP archive of directory", e );
-        }
-
-        put( zipFile, getPath( dir, zipFile.getName() ) );
-
-        try
-        {
-            executeCommand( "cd " + path + "; unzip -o " + zipFile.getName() + "; rm -f " + zipFile.getName() );
-
-            zipFile.delete();
-
-            RepositoryPermissions permissions = getRepository().getPermissions();
-
-            if ( permissions != null && permissions.getGroup() != null )
-            {
-                executeCommand( "chgrp -Rf " + permissions.getGroup() + " " + path );
-            }
-
-            if ( permissions != null && permissions.getFileMode() != null )
-            {
-                executeCommand( "chmod -Rf " + permissions.getFileMode() + " " + path );
-            }
-        }
-        catch ( CommandExecutionException e )
-        {
-            throw new TransferFailedException( "Error performing commands for file transfer", e );
-        }
-    }
 }
diff --git a/wagon-providers/wagon-ssh-external/src/test/java/org/apache/maven/wagon/providers/ssh/external/ScpExternalWagonTest.java b/wagon-providers/wagon-ssh-external/src/test/java/org/apache/maven/wagon/providers/ssh/external/ScpExternalWagonTest.java
index 5a5ddbb..1f59c67 100644
--- a/wagon-providers/wagon-ssh-external/src/test/java/org/apache/maven/wagon/providers/ssh/external/ScpExternalWagonTest.java
+++ b/wagon-providers/wagon-ssh-external/src/test/java/org/apache/maven/wagon/providers/ssh/external/ScpExternalWagonTest.java
@@ -40,6 +40,11 @@
         return WagonConstants.UNKNOWN_LENGTH;
     }
 
+    protected boolean supportsGetIfNewer()
+    {
+        return false;
+    }
+
     protected long getExpectedLastModifiedOnGet( Repository repository, Resource resource )
     {
         return 0;
diff --git a/wagon-providers/wagon-ssh-external/src/test/java/org/apache/maven/wagon/providers/ssh/external/ScpWagonWithSshPrivateKeySearchTest.java b/wagon-providers/wagon-ssh-external/src/test/java/org/apache/maven/wagon/providers/ssh/external/ScpWagonWithSshPrivateKeySearchTest.java
index 0262f1d..de87f2e 100644
--- a/wagon-providers/wagon-ssh-external/src/test/java/org/apache/maven/wagon/providers/ssh/external/ScpWagonWithSshPrivateKeySearchTest.java
+++ b/wagon-providers/wagon-ssh-external/src/test/java/org/apache/maven/wagon/providers/ssh/external/ScpWagonWithSshPrivateKeySearchTest.java
@@ -33,6 +33,11 @@
 public class ScpWagonWithSshPrivateKeySearchTest
     extends WagonTestCase
 {
+    protected boolean supportsGetIfNewer()
+    {
+        return false;
+    }
+
     protected int getExpectedContentLengthOnGet( int expectedSize )
     {
         return WagonConstants.UNKNOWN_LENGTH;
diff --git a/wagon-providers/wagon-ssh/pom.xml b/wagon-providers/wagon-ssh/pom.xml
index 94e3a12..f811179 100644
--- a/wagon-providers/wagon-ssh/pom.xml
+++ b/wagon-providers/wagon-ssh/pom.xml
@@ -1,52 +1,39 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one
-  ~ or more contributor license agreements.  See the NOTICE file
-  ~ distributed with this work for additional information
-  ~ regarding copyright ownership.  The ASF licenses this file
-  ~ to you 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <artifactId>wagon-providers</artifactId>
     <groupId>org.apache.maven.wagon</groupId>
-    <version>1.0-rc1-SNAPSHOT</version>
+    <version>1.0-beta-3</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>wagon-ssh</artifactId>
   <name>Maven Wagon SSH Provider</name>
 
-  <build>
-    <plugins>
-      <plugin>
-        <artifactId>maven-surefire-plugin</artifactId>
-        <configuration>
-          <!-- Tests that require an ssh server on localhost -->
-          <excludes>
-            <exclude>**/ScpWagon*Test.*</exclude>
-            <exclude>**/SftpWagonTest.*</exclude>
-            <exclude>**/SshCommandExecutorTest.*</exclude>
-            <exclude>**/KnownHostsProviderTest.*</exclude>
-          </excludes>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
+  <profiles>
+    <profile>
+      <id>no-ssh-tests</id>
+      <activation>
+        <property>
+          <name>!ssh-tests</name>
+        </property>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <configuration>
+              <!-- Tests that require an ssh server on localhost -->
+              <excludes>
+                <exclude>**/SftpWagonTest.*</exclude>
+                <exclude>**/SshCommandExecutorTest.*</exclude>
+                <exclude>**/KnownHostsProviderTest.*</exclude>
+                <exclude>**/ScpWagon*Test.*</exclude>
+              </excludes>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
   <dependencies>
     <dependency>
       <groupId>com.jcraft</groupId>
@@ -55,10 +42,6 @@
     </dependency>
     <dependency>
       <groupId>org.codehaus.plexus</groupId>
-      <artifactId>plexus-interactivity-api</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.codehaus.plexus</groupId>
       <artifactId>plexus-utils</artifactId>
     </dependency>
     <dependency>
@@ -71,4 +54,4 @@
       <scope>test</scope>
     </dependency>
   </dependencies>
-</project>
+</project>
\ No newline at end of file
diff --git a/wagon-providers/wagon-ssh/src/main/java/org/apache/maven/wagon/providers/ssh/jsch/AbstractJschWagon.java b/wagon-providers/wagon-ssh/src/main/java/org/apache/maven/wagon/providers/ssh/jsch/AbstractJschWagon.java
index 9e90f3a..be95208 100644
--- a/wagon-providers/wagon-ssh/src/main/java/org/apache/maven/wagon/providers/ssh/jsch/AbstractJschWagon.java
+++ b/wagon-providers/wagon-ssh/src/main/java/org/apache/maven/wagon/providers/ssh/jsch/AbstractJschWagon.java
@@ -27,13 +27,22 @@
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.util.List;
 import java.util.Properties;
 
 import org.apache.maven.wagon.CommandExecutionException;
+import org.apache.maven.wagon.CommandExecutor;
+import org.apache.maven.wagon.ResourceDoesNotExistException;
+import org.apache.maven.wagon.StreamWagon;
 import org.apache.maven.wagon.Streams;
+import org.apache.maven.wagon.TransferFailedException;
+import org.apache.maven.wagon.WagonConstants;
 import org.apache.maven.wagon.authentication.AuthenticationException;
-import org.apache.maven.wagon.providers.ssh.AbstractSshWagon;
+import org.apache.maven.wagon.authentication.AuthenticationInfo;
+import org.apache.maven.wagon.authorization.AuthorizationException;
+import org.apache.maven.wagon.events.TransferEvent;
 import org.apache.maven.wagon.providers.ssh.CommandExecutorStreamProcessor;
+import org.apache.maven.wagon.providers.ssh.ScpHelper;
 import org.apache.maven.wagon.providers.ssh.SshWagon;
 import org.apache.maven.wagon.providers.ssh.interactive.InteractiveUserInfo;
 import org.apache.maven.wagon.providers.ssh.interactive.NullInteractiveUserInfo;
@@ -42,6 +51,7 @@
 import org.apache.maven.wagon.providers.ssh.knownhost.KnownHostsProvider;
 import org.apache.maven.wagon.providers.ssh.knownhost.UnknownHostException;
 import org.apache.maven.wagon.proxy.ProxyInfo;
+import org.apache.maven.wagon.resource.Resource;
 import org.codehaus.plexus.util.IOUtil;
 import org.codehaus.plexus.util.StringInputStream;
 
@@ -63,9 +73,11 @@
  * @version $Id$
  */
 public abstract class AbstractJschWagon
-    extends AbstractSshWagon
-    implements SshWagon
+    extends StreamWagon
+    implements SshWagon, CommandExecutor
 {
+    protected ScpHelper sshTool = new ScpHelper( this );
+    
     protected Session session;
     
     /**
@@ -90,7 +102,10 @@
     public void openConnectionInternal()
         throws AuthenticationException
     {
-        super.openConnectionInternal();
+        if ( authenticationInfo == null )
+        {
+            authenticationInfo = new AuthenticationInfo();
+        }
 
         if ( !interactive )
         {
@@ -103,7 +118,7 @@
         File privateKey;
         try
         {
-            privateKey = getPrivateKey();
+            privateKey = ScpHelper.getPrivateKey( authenticationInfo );
         }
         catch ( FileNotFoundException e )
         {
@@ -112,6 +127,7 @@
 
         if ( privateKey != null && privateKey.exists() )
         {
+            fireSessionDebug( "Using private key: " + privateKey );
             try
             {
                 sch.addIdentity( privateKey.getAbsolutePath(), authenticationInfo.getPassphrase() );
@@ -123,7 +139,8 @@
         }
 
         String host = getRepository().getHost();
-        int port = getPort();
+        int port =
+            repository.getPort() == WagonConstants.UNKNOWN_PORT ? ScpHelper.DEFAULT_SSH_PORT : repository.getPort();
         try
         {
             String userName = authenticationInfo.getUserName();
@@ -258,8 +275,6 @@
 
     public void closeConnection()
     {
-        super.closeConnection();
-        
         if ( session != null )
         {
             session.disconnect();
@@ -316,6 +331,49 @@
         }
     }
 
+    protected void handleGetException( Resource resource, Exception e )
+        throws TransferFailedException
+    {
+        fireTransferError( resource, e, TransferEvent.REQUEST_GET );
+
+        String msg =
+            "Error occured while downloading '" + resource + "' from the remote repository:" + getRepository() + ": "
+                + e.getMessage();
+
+        throw new TransferFailedException( msg, e );
+    }
+
+    public List getFileList( String destinationDirectory )
+        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
+    {
+        return sshTool.getFileList( destinationDirectory, repository );
+    }
+
+    public void putDirectory( File sourceDirectory, String destinationDirectory )
+        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
+    {
+        sshTool.putDirectory( this, sourceDirectory, destinationDirectory );
+    }
+
+    public boolean resourceExists( String resourceName )
+        throws TransferFailedException, AuthorizationException
+    {
+        return sshTool.resourceExists( resourceName, repository );
+    }
+
+    public boolean supportsDirectoryCopy()
+    {
+        return true;
+    }
+
+    public void executeCommand( String command )
+        throws CommandExecutionException
+    {
+        fireTransferDebug( "Executing command: " + command );
+
+        executeCommand( command, false );
+    }
+
     public InteractiveUserInfo getInteractiveUserInfo()
     {
         return this.interactiveUserInfo;
diff --git a/wagon-providers/wagon-ssh/src/main/java/org/apache/maven/wagon/providers/ssh/jsch/ScpWagon.java b/wagon-providers/wagon-ssh/src/main/java/org/apache/maven/wagon/providers/ssh/jsch/ScpWagon.java
index 075d8a5..f57ebe8 100644
--- a/wagon-providers/wagon-ssh/src/main/java/org/apache/maven/wagon/providers/ssh/jsch/ScpWagon.java
+++ b/wagon-providers/wagon-ssh/src/main/java/org/apache/maven/wagon/providers/ssh/jsch/ScpWagon.java
@@ -19,34 +19,29 @@
  * under the License.
  */
 
-import com.jcraft.jsch.ChannelExec;
-import com.jcraft.jsch.JSchException;
-import org.apache.maven.wagon.CommandExecutionException;
-import org.apache.maven.wagon.ResourceDoesNotExistException;
-import org.apache.maven.wagon.TransferFailedException;
-import org.apache.maven.wagon.authorization.AuthorizationException;
-import org.apache.maven.wagon.events.TransferEvent;
-import org.apache.maven.wagon.providers.ssh.ScpHelper;
-import org.apache.maven.wagon.repository.RepositoryPermissions;
-import org.apache.maven.wagon.resource.Resource;
-import org.codehaus.plexus.util.IOUtil;
-
 import java.io.EOFException;
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
+import org.apache.maven.wagon.CommandExecutionException;
+import org.apache.maven.wagon.InputData;
+import org.apache.maven.wagon.OutputData;
+import org.apache.maven.wagon.ResourceDoesNotExistException;
+import org.apache.maven.wagon.TransferFailedException;
+import org.apache.maven.wagon.events.TransferEvent;
+import org.apache.maven.wagon.providers.ssh.ScpHelper;
+import org.apache.maven.wagon.repository.RepositoryPermissions;
+import org.apache.maven.wagon.resource.Resource;
+
+import com.jcraft.jsch.ChannelExec;
+import com.jcraft.jsch.JSchException;
+
 /**
- * SFTP protocol wagon.
+ * SCP protocol wagon.
  * 
- * A base class for deployers and fetchers using protocols from SSH2 family and
- * JSch library for underlying implementation.
- * 
- * This is responsible for authentification stage of the process.
- * 
- * We will first try to use public keys for authentication and if that doesn't
- * work then we fall back to using the login and password.
+ * Note that this implementation is <i>not</i> thread-safe, and multiple channels can not be used on the session at
+ * the same time.
  * 
  * See <a href="http://blogs.sun.com/janp/entry/how_the_scp_protocol_works">http://blogs.sun.com/janp/entry/how_the_scp_protocol_works</a>
  * for information on how the SCP protocol works.
@@ -61,56 +56,6 @@
 public class ScpWagon
     extends AbstractJschWagon
 {
-    public void put( File source, String destination )
-        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-    {
-        String basedir = getRepository().getBasedir();
-
-        Resource resource = getResource( destination );
-
-        String dir = getResourceDirectory( resource.getName() );
-
-        firePutInitiated( resource, source );
-
-        try
-        {
-            ScpHelper.createRemoteDirectories( getPath( basedir, dir ), getRepository().getPermissions(), this );
-
-            RepositoryPermissions permissions = getRepository().getPermissions();
-
-            put( source, basedir, resource, getOctalMode( permissions ) );
-
-            setFileGroup( permissions, basedir, resource );
-        }
-        catch ( CommandExecutionException e )
-        {
-            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
-            
-            throw new TransferFailedException( e.getMessage(), e );
-        }
-    }
-
-    private void setFileGroup( RepositoryPermissions permissions, String basedir, Resource resource )
-        throws CommandExecutionException
-    {
-        if ( permissions != null && permissions.getGroup() != null )
-        {
-            executeCommand( "chgrp -f " + permissions.getGroup() + " " + getPath( basedir, resource.getName() ) );
-        }
-    }
-
-    public void get( String resourceName, File destination )
-        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-    {
-        String basedir = getRepository().getBasedir();
-
-        Resource resource = new Resource( resourceName );
-
-        fireGetInitiated( resource, destination );
-
-        get( basedir, resource, destination );
-    }
-
     private static final char COPY_START_CHAR = 'C';
 
     private static final char ACK_SEPARATOR = ' ';
@@ -121,107 +66,58 @@
 
     private static final byte LF = '\n';
 
-    public void put( File source, String basedir, Resource resource, String octalMode )
+    private ChannelExec channel;
+
+    private InputStream channelInputStream;
+
+    private OutputStream channelOutputStream;
+
+    private void setFileGroup( RepositoryPermissions permissions, String basedir, Resource resource )
+        throws CommandExecutionException
+    {
+        if ( permissions != null && permissions.getGroup() != null )
+        {
+            executeCommand( "chgrp -f " + permissions.getGroup() + " " + getPath( basedir, resource.getName() ) );
+        }
+    }
+
+    protected void cleanupPutTransfer( Resource resource )
+    {
+        if ( channel != null )
+        {
+            channel.disconnect();
+            channel = null;
+        }
+    }
+
+    protected void finishPutTransfer( Resource resource, InputStream input, OutputStream output )
         throws TransferFailedException
     {
-        String path = getPath( basedir, resource.getName() );
-
-        String resourceName = resource.getName();
-
-        ChannelExec channel = null;
-
-        OutputStream out = null;
         try
         {
-            // exec 'scp -p -t rfile' remotely
-            String command = "scp";
-            if ( octalMode != null )
-            {
-                command += " -p";
-            }
-            command += " -t \"" + path + "\"";
+            sendEom( output );
 
-            fireTransferDebug( "Executing command: " + command );
-
-            channel = (ChannelExec) session.openChannel( EXEC_CHANNEL );
-
-            channel.setCommand( command );
-
-            // get I/O streams for remote scp
-            out = channel.getOutputStream();
-
-            InputStream in = channel.getInputStream();
-
-            channel.connect();
-
-            checkAck( in );
-
-            // send "C0644 filesize filename", where filename should not include '/'
-            long filesize = source.length();
-
-            String mode = octalMode == null ? "0644" : octalMode;
-            command = "C" + mode + " " + filesize + " ";
-
-            if ( resourceName.lastIndexOf( PATH_SEPARATOR ) > 0 )
-            {
-                command += resourceName.substring( resourceName.lastIndexOf( PATH_SEPARATOR ) + 1 );
-            }
-            else
-            {
-                command += resourceName;
-            }
-
-            command += "\n";
-
-            out.write( command.getBytes() );
-
-            out.flush();
-
-            checkAck( in );
-
-            putTransfer( resource, source, out, false );
-
-            sendEom( out );
-
-            checkAck( in );
+            checkAck( channelInputStream );
 
             // This came from SCPClient in Ganymede SSH2. It is sent after all files.
-            out.write( END_OF_FILES_MSG.getBytes() );
-            out.flush();
+            output.write( END_OF_FILES_MSG.getBytes() );
+            output.flush();
         }
         catch ( IOException e )
         {
-            if ( e.getMessage().indexOf( "set mode: Operation not permitted" ) >= 0 )
-            {
-                fireTransferDebug( e.getMessage() );                
-            }
-            else
-            {
-                fireTransferError( resource, e, TransferEvent.REQUEST_PUT );            
-                
-                String msg = "Error occured while deploying '" + resourceName + "' to remote repository: " +
-                    getRepository().getUrl() + ": " + e.getMessage();
-    
-                throw new TransferFailedException( msg, e );
-            }
+            handleIOException( resource, e );
         }
-        catch ( JSchException e )
-        {
-            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );            
-            
-            String msg = "Error occured while deploying '" + resourceName + "' to remote repository: " +
-                getRepository().getUrl() + ": " + e.getMessage();
 
-            throw new TransferFailedException( msg, e );
+        String basedir = getRepository().getBasedir();
+        try
+        {
+            setFileGroup( getRepository().getPermissions(), basedir, resource );
         }
-        finally
+        catch ( CommandExecutionException e )
         {
-            if ( channel != null )
-            {
-                IOUtil.close( out );
+            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
 
-                channel.disconnect();
-            }
+            throw new TransferFailedException( e.getMessage(), e );
         }
     }
 
@@ -247,43 +143,113 @@
         {
             throw new IOException( "SCP terminated with unknown error code" );
         }
+    }    
+
+    protected void finishGetTransfer( Resource resource, InputStream input, OutputStream output )
+        throws TransferFailedException
+    {
+        try
+        {
+            checkAck( input );
+    
+            sendEom( channelOutputStream );
+        }
+        catch ( IOException e )
+        {
+            handleGetException( resource, e );
+        }
+    }
+    
+    protected void cleanupGetTransfer( Resource resource )
+    {
+        if ( channel != null )
+        {
+            channel.disconnect();
+        }
+    }
+    
+    protected void getTransfer( Resource resource, OutputStream output, InputStream input, boolean closeInput,
+                                int maxSize )
+        throws TransferFailedException
+    {
+        super.getTransfer( resource, output, input, closeInput, (int) resource.getContentLength() );
+    }
+    
+    protected String readLine( InputStream in )
+        throws IOException
+    {
+        StringBuffer sb = new StringBuffer();
+
+        while ( true )
+        {
+            if ( sb.length() > LINE_BUFFER_SIZE )
+            {
+                throw new IOException( "Remote server sent a too long line" );
+            }
+
+            int c = in.read();
+
+            if ( c < 0 )
+            {
+                throw new IOException( "Remote connection terminated unexpectedly." );
+            }
+
+            if ( c == LF )
+            {
+                break;
+            }
+
+            sb.append( (char) c );
+        }
+        return sb.toString();
     }
 
-    public void get( String basedir, Resource resource, File destination )
-        throws ResourceDoesNotExistException, TransferFailedException
+    protected static void sendEom( OutputStream out )
+        throws IOException
     {
-        String path = getPath( basedir, resource.getName() );
+        out.write( 0 );
 
-        //I/O streams for remote scp
-        OutputStream out = null;
+        out.flush();
+    }
 
-        ChannelExec channel = null;
+    public void fillInputData( InputData inputData )
+        throws TransferFailedException, ResourceDoesNotExistException
+    {
+        Resource resource = inputData.getResource();
+        
+        String path = getPath( getRepository().getBasedir(), resource.getName() );
+        String cmd = "scp -p -f " + path;
+
+        fireTransferDebug( "Executing command: " + cmd );
 
         try
         {
-            String cmd = "scp -f " + path;
-
-            fireTransferDebug( "Executing command: " + cmd );
-
             channel = (ChannelExec) session.openChannel( EXEC_CHANNEL );
 
             channel.setCommand( cmd );
 
             // get I/O streams for remote scp
-            out = channel.getOutputStream();
-
+            channelOutputStream = channel.getOutputStream();
+            
             InputStream in = channel.getInputStream();
+            inputData.setInputStream( in );
 
             channel.connect();
 
-            sendEom( out );
+            sendEom( channelOutputStream );
 
             int exitCode = in.read();
 
-            if ( exitCode == 'P' )
+            if ( exitCode == 'T' )
             {
-                // ignore modification times
+                String line = readLine( in );
+                
+                String[] times = line.split( " " );
+                
+                resource.setLastModified( Long.valueOf( times[0] ).longValue() * 1000 );
 
+                sendEom( channelOutputStream );
+                
                 exitCode = in.read();
             }
 
@@ -328,75 +294,148 @@
             String filename = line.substring( index + 1 );
             fireTransferDebug( "Remote filename: " + filename );
 
-            sendEom( out );
-
-            getTransfer( resource, destination, in, false, filesize );
-
-            if ( destination.length() != filesize )
-            {
-                throw new IOException(
-                    "Expected file length: " + filesize + "; received = " + destination.length() );
-            }
-
-            // TODO: we could possibly have received additional files here
-
-            checkAck( in );
-
-            sendEom( out );
+            sendEom( channelOutputStream );
         }
         catch ( JSchException e )
         {
-            handleGetException( resource, e, destination );
+            handleGetException( resource, e );
         }
         catch ( IOException e )
         {
-            handleGetException( resource, e, destination );
-        }
-        finally
-        {
-            IOUtil.close( out );
-
-            if ( channel != null )
-            {
-                channel.disconnect();
-            }
+            handleGetException( resource, e );
         }
     }
 
-    protected String readLine( InputStream in )
-        throws IOException
+    public void fillOutputData( OutputData outputData )
+        throws TransferFailedException
     {
-        StringBuffer sb = new StringBuffer();
+        Resource resource = outputData.getResource();
+        
+        String basedir = getRepository().getBasedir();
 
-        while ( true )
+        String path = getPath( basedir, resource.getName() );
+
+        String dir = ScpHelper.getResourceDirectory( resource.getName() );
+
+        try
         {
-            if ( sb.length() > LINE_BUFFER_SIZE )
-            {
-                throw new IOException( "Remote server sent a too long line" );
-            }
-
-            int c = in.read();
-
-            if ( c < 0 )
-            {
-                throw new IOException( "Remote connection terminated unexpectedly." );
-            }
-
-            if ( c == LF )
-            {
-                break;
-            }
-
-            sb.append( (char) c );
+            sshTool.createRemoteDirectories( getPath( basedir, dir ), getRepository().getPermissions() );
         }
-        return sb.toString();
+        catch ( CommandExecutionException e )
+        {
+            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
+
+            throw new TransferFailedException( e.getMessage(), e );
+        }
+        
+        String octalMode = getOctalMode( getRepository().getPermissions() );
+        
+        // exec 'scp -p -t rfile' remotely
+        String command = "scp";
+        if ( octalMode != null )
+        {
+            command += " -p";
+        }
+        command += " -t \"" + path + "\"";
+
+        fireTransferDebug( "Executing command: " + command );
+
+        String resourceName = resource.getName();
+
+        OutputStream out = null;
+        try
+        {
+            channel = (ChannelExec) session.openChannel( EXEC_CHANNEL );
+
+            channel.setCommand( command );
+
+            // get I/O streams for remote scp
+            out = channel.getOutputStream();
+            outputData.setOutputStream( out );
+
+            channelInputStream = channel.getInputStream();
+
+            channel.connect();
+
+            checkAck( channelInputStream );
+
+            // send "C0644 filesize filename", where filename should not include '/'
+            long filesize = resource.getContentLength();
+
+            String mode = octalMode == null ? "0644" : octalMode;
+            command = "C" + mode + " " + filesize + " ";
+
+            if ( resourceName.lastIndexOf( ScpHelper.PATH_SEPARATOR ) > 0 )
+            {
+                command += resourceName.substring( resourceName.lastIndexOf( ScpHelper.PATH_SEPARATOR ) + 1 );
+            }
+            else
+            {
+                command += resourceName;
+            }
+
+            command += "\n";
+
+            out.write( command.getBytes() );
+
+            out.flush();
+
+            checkAck( channelInputStream );
+        }
+        catch ( JSchException e )
+        {
+            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );            
+            
+            String msg = "Error occured while deploying '" + resourceName + "' to remote repository: " +
+                getRepository().getUrl() + ": " + e.getMessage();
+
+            throw new TransferFailedException( msg, e );
+        }
+        catch ( IOException e )
+        {
+            handleIOException( resource, e );
+        }
     }
 
-    protected static void sendEom( OutputStream out )
-        throws IOException
+    private void handleIOException( Resource resource, IOException e )
+        throws TransferFailedException
     {
-        out.write( 0 );
+        if ( e.getMessage().indexOf( "set mode: Operation not permitted" ) >= 0 )
+        {
+            fireTransferDebug( e.getMessage() );                
+        }
+        else
+        {
+            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );            
+            
+            String msg = "Error occured while deploying '" + resource.getName() + "' to remote repository: " +
+                getRepository().getUrl() + ": " + e.getMessage();
+   
+            throw new TransferFailedException( msg, e );
+        }
+    }
 
-        out.flush();
+    public String getOctalMode( RepositoryPermissions permissions )
+    {
+        String mode = null;
+        if ( permissions != null && permissions.getFileMode() != null )
+        {
+            if ( permissions.getFileMode().matches( "[0-9]{3,4}" ) )
+            {
+                mode = permissions.getFileMode();
+
+                if ( mode.length() == 3 )
+                {
+                    mode = "0" + mode;
+                }
+            }
+            else
+            {
+                // TODO: calculate?
+                // TODO: as warning
+                fireSessionDebug( "Not using non-octal permissions: " + permissions.getFileMode() );
+            }
+        }
+        return mode;
     }
 }
diff --git a/wagon-providers/wagon-ssh/src/main/java/org/apache/maven/wagon/providers/ssh/jsch/SftpWagon.java b/wagon-providers/wagon-ssh/src/main/java/org/apache/maven/wagon/providers/ssh/jsch/SftpWagon.java
index ddd1c0c..a9286d5 100644
--- a/wagon-providers/wagon-ssh/src/main/java/org/apache/maven/wagon/providers/ssh/jsch/SftpWagon.java
+++ b/wagon-providers/wagon-ssh/src/main/java/org/apache/maven/wagon/providers/ssh/jsch/SftpWagon.java
@@ -20,16 +20,22 @@
  */
 
 import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Vector;
 
+import org.apache.maven.wagon.InputData;
+import org.apache.maven.wagon.OutputData;
 import org.apache.maven.wagon.PathUtils;
 import org.apache.maven.wagon.ResourceDoesNotExistException;
 import org.apache.maven.wagon.TransferFailedException;
+import org.apache.maven.wagon.authentication.AuthenticationException;
 import org.apache.maven.wagon.authorization.AuthorizationException;
 import org.apache.maven.wagon.events.TransferEvent;
+import org.apache.maven.wagon.providers.ssh.ScpHelper;
 import org.apache.maven.wagon.repository.RepositoryPermissions;
 import org.apache.maven.wagon.resource.Resource;
 
@@ -44,6 +50,7 @@
  * @author <a href="mailto:brett@apache.org">Brett Porter</a>
  * @version $Id$
  * @todo [BP] add compression flag
+ * @todo see if SftpProgressMonitor allows us to do streaming (without it, we can't do checksums as the input stream is lost)
  * 
  * @plexus.component role="org.apache.maven.wagon.Wagon" 
  *   role-hint="sftp"
@@ -58,41 +65,39 @@
 
     private static final long MILLIS_PER_SEC = 1000L;
 
-    public void put( String basedir, Resource resource, File source )
-        throws TransferFailedException
+    private ChannelSftp channel;
+    
+    public void closeConnection()
     {
-        String resourceName = resource.getName();
-        String dir = getResourceDirectory( resourceName );
+        if ( channel != null )
+        {
+            channel.disconnect();
+        }
+        super.closeConnection();
+    }
 
-        ChannelSftp channel = null;
+    public void openConnectionInternal()
+        throws AuthenticationException
+    {
+        super.openConnectionInternal();
 
         try
         {
             channel = (ChannelSftp) session.openChannel( SFTP_CHANNEL );
 
             channel.connect();
+        }
+        catch ( JSchException e )
+        {
+            throw new AuthenticationException( "Error connecting to remote repository: " + getRepository().getUrl(), e );
+        }
+    }
 
-            RepositoryPermissions permissions = getRepository().getPermissions();
-
-            int directoryMode = getDirectoryMode( permissions );
-
-            channel.cd( "/" );
-            
-            try
-            {
-                mkdirs( channel, basedir + "/", directoryMode );
-                
-                mkdirs( channel, resourceName, directoryMode );
-            }
-            catch ( TransferFailedException e )
-            {
-                fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
-
-                throw e;
-            }
-
-            putFile( channel, source, resource, permissions );
-
+    private void returnToParentDirectory( Resource resource )
+    {
+        try
+        {
+            String dir = ScpHelper.getResourceDirectory( resource.getName() );
             String[] dirs = PathUtils.dirnames( dir );
             for ( int i = 0; i < dirs.length; i++ )
             {
@@ -101,39 +106,18 @@
         }
         catch ( SftpException e )
         {
-            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
-            
-            String msg = "Error occured while deploying '" + resourceName + "' " + "to remote repository: " +
-                getRepository().getUrl();
-
-            throw new TransferFailedException( msg, e );
-        }
-        catch ( JSchException e )
-        {
-            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
-
-            String msg = "Error occured while deploying '" + resourceName + "' " + "to remote repository: " +
-                getRepository().getUrl();
-
-            throw new TransferFailedException( msg, e );
-        }
-        finally
-        {
-            if ( channel != null )
-            {
-                channel.disconnect();
-            }
+            fireTransferDebug( "Error returning to parent directory: " + e.getMessage() );
         }
     }
 
-    private void putFile( ChannelSftp channel, File source, Resource resource, RepositoryPermissions permissions )
+    private void putFile( File source, Resource resource, RepositoryPermissions permissions )
         throws SftpException, TransferFailedException
     {
         resource.setContentLength( source.length() );
         
         resource.setLastModified( source.lastModified() );
         
-        String filename = getResourceFilename( resource.getName() );
+        String filename = ScpHelper.getResourceFilename( resource.getName() );
 
         firePutStarted( resource, source );
 
@@ -143,19 +127,18 @@
 
         if ( permissions != null && permissions.getGroup() != null )
         {
-            setGroup( channel, filename, permissions );
+            setGroup( filename, permissions );
         }
 
         if ( permissions != null && permissions.getFileMode() != null )
         {
-            setFileMode( channel, filename, permissions );
+            setFileMode( filename, permissions );
         }
 
         firePutCompleted( resource, source );
     }
 
-    private void setGroup( ChannelSftp channel, String filename, RepositoryPermissions permissions )
-        throws SftpException
+    private void setGroup( String filename, RepositoryPermissions permissions )
     {
         try
         {
@@ -167,9 +150,13 @@
             // TODO: warning level
             fireTransferDebug( "Not setting group: must be a numerical GID for SFTP" );
         }
+        catch ( SftpException e )
+        {
+            fireTransferDebug( "Not setting group: " + e.getMessage() );            
+        }
     }
 
-    private void setFileMode( ChannelSftp channel, String filename, RepositoryPermissions permissions )
+    private void setFileMode( String filename, RepositoryPermissions permissions )
     {
         try
         {
@@ -187,19 +174,19 @@
         }
     }
 
-    private void mkdirs( ChannelSftp channel, String resourceName, int mode )
+    private void mkdirs( String resourceName, int mode )
         throws SftpException, TransferFailedException
     {
         String[] dirs = PathUtils.dirnames( resourceName );
         for ( int i = 0; i < dirs.length; i++ )
         {
-            mkdir( channel, dirs[i], mode );
+            mkdir( dirs[i], mode );
 
             channel.cd( dirs[i] );
         }
     }
 
-    private void mkdir( ChannelSftp channel, String dir, int mode )
+    private void mkdir( String dir, int mode )
         throws TransferFailedException, SftpException
     {
         try
@@ -229,79 +216,7 @@
         }
     }
 
-    public boolean getIfNewer( Resource resource, File destination, long timestamp )
-        throws ResourceDoesNotExistException, TransferFailedException
-    {
-        String filename = getResourceFilename( resource.getName() );
-
-        String dir = getResourceDirectory( resource.getName() );
-
-        // we already setuped the root directory. Ignore beginning /
-        if ( dir.length() > 0 && dir.charAt( 0 ) == PATH_SEPARATOR )
-        {
-            dir = dir.substring( 1 );
-        }
-
-        ChannelSftp channel = null;
-        
-        boolean bDownloaded = true;
-        try
-        {
-            channel = (ChannelSftp) session.openChannel( SFTP_CHANNEL );
-
-            channel.connect();
-
-            SftpATTRS attrs = changeToRepositoryDirectory( channel, dir, filename );
-
-            long lastModified = attrs.getMTime() * MILLIS_PER_SEC;
-            if ( timestamp <= 0 || lastModified > timestamp )
-            {
-                resource.setContentLength( attrs.getSize() );
-                
-                resource.setLastModified( lastModified );
-                
-                fireGetStarted( resource, destination );
-
-                channel.get( filename, destination.getAbsolutePath() );
-
-                postProcessListeners( resource, destination, TransferEvent.REQUEST_GET );
-
-                fireGetCompleted( resource, destination );
-
-                String[] dirs = PathUtils.dirnames( dir );
-
-                for ( int i = 0; i < dirs.length; i++ )
-                {
-                    channel.cd( ".." );
-                }
-
-                bDownloaded = true;
-            }
-            else
-            {
-                bDownloaded = false;
-            }
-        }
-        catch ( SftpException e )
-        {
-            handleGetException( resource, e, destination );
-        }
-        catch ( JSchException e )
-        {
-            handleGetException( resource, e, destination );
-        }
-        finally
-        {
-            if ( channel != null )
-            {
-                channel.disconnect();
-            }
-        }
-
-        return bDownloaded;
-    }
-
-    private SftpATTRS changeToRepositoryDirectory( ChannelSftp channel, String dir, String filename )
+    private SftpATTRS changeToRepositoryDirectory( String dir, String filename )
         throws ResourceDoesNotExistException, SftpException
     {
         // This must be called first to ensure that if the file doesn't exist it throws an exception
@@ -331,61 +246,26 @@
         return attrs;
     }
 
-    public void put( File source, String destination )
-        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-    {
-        String basedir = getRepository().getBasedir();
-
-        Resource resource = getResource( destination );
-
-        firePutInitiated( resource, source );
-
-        put( basedir, resource, source );
-    }
-
-    public void get( String resourceName, File destination )
-        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-    {
-        getIfNewer( resourceName, destination, 0 );
-    }
-
-    public boolean getIfNewer( String resourceName, File destination, long timestamp )
-        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-    {
-        createParentDirectories( destination );
-
-        Resource resource = getResource( resourceName );
-
-        fireGetInitiated( resource, destination );
-
-        return getIfNewer( resource, destination, timestamp );
-    }
-
     public void putDirectory( File sourceDirectory, String destinationDirectory )
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
     {
         final RepositoryPermissions permissions = repository.getPermissions();
 
-        ChannelSftp channel = null;
-
         try
         {
-            channel = (ChannelSftp) session.openChannel( SFTP_CHANNEL );
-
-            channel.connect();
-
             channel.cd( "/" );
             
             String basedir = getRepository().getBasedir();
             int directoryMode = getDirectoryMode( permissions );
             
-            mkdirs( channel, basedir + "/", directoryMode );
+            mkdirs( basedir + "/", directoryMode );
             
             fireTransferDebug( "Recursively uploading directory " + sourceDirectory.getAbsolutePath() + " as "
                 + destinationDirectory );
             
-            mkdirs( channel, destinationDirectory, directoryMode );
-            ftpRecursivePut( channel, sourceDirectory, null, getResourceFilename( destinationDirectory ), directoryMode );
+            mkdirs( destinationDirectory, directoryMode );
+            ftpRecursivePut( sourceDirectory, null, ScpHelper.getResourceFilename( destinationDirectory ),
+                             directoryMode );
         }
         catch ( SftpException e )
         {
@@ -395,24 +275,9 @@
 
             throw new TransferFailedException( msg, e );
         }
-        catch ( JSchException e )
-        {
-            String msg =
-                "Error occured while deploying '" + sourceDirectory.getAbsolutePath() + "' " + "to remote repository: "
-                    + getRepository().getUrl();
-
-            throw new TransferFailedException( msg, e );
-        }
-        finally
-        {
-            if ( channel != null )
-            {
-                channel.disconnect();
-            }
-        }
     }
 
-    private void ftpRecursivePut( ChannelSftp channel, File sourceFile, String prefix, String fileName, int directoryMode )
+    private void ftpRecursivePut( File sourceFile, String prefix, String fileName, int directoryMode )
         throws TransferFailedException, SftpException
     {
         final RepositoryPermissions permissions = repository.getPermissions();
@@ -422,7 +287,7 @@
             if ( !fileName.equals( "." ) )
             {
                 prefix = getFileName( prefix, fileName );
-                mkdir( channel, fileName, directoryMode );
+                mkdir( fileName, directoryMode );
                 channel.cd( fileName );
             }
 
@@ -434,14 +299,14 @@
                 {
                     if ( files[i].isDirectory() )
                     {
-                        ftpRecursivePut( channel, files[i], prefix, files[i].getName(), directoryMode );
+                        ftpRecursivePut( files[i], prefix, files[i].getName(), directoryMode );
                     }
                 }
                 for ( int i = 0; i < files.length; i++ )
                 {
                     if ( !files[i].isDirectory() )
                     {
-                        ftpRecursivePut( channel, files[i], prefix, files[i].getName(), directoryMode );
+                        ftpRecursivePut( files[i], prefix, files[i].getName(), directoryMode );
                     }
                 }
             }
@@ -450,11 +315,11 @@
         }
         else
         {
-            Resource resource = getResource( getFileName( prefix, fileName ) );
+            Resource resource = ScpHelper.getResource( getFileName( prefix, fileName ) );
 
             firePutInitiated( resource, sourceFile );
 
-            putFile( channel, sourceFile, resource, permissions );
+            putFile( sourceFile, resource, permissions );
         }
     }
 
@@ -474,24 +339,19 @@
     public List getFileList( String destinationDirectory )
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
     {
-        String filename = getResourceFilename( destinationDirectory );
+        String filename = ScpHelper.getResourceFilename( destinationDirectory );
 
-        String dir = getResourceDirectory( destinationDirectory );
+        String dir = ScpHelper.getResourceDirectory( destinationDirectory );
 
         // we already setuped the root directory. Ignore beginning /
-        if ( dir.length() > 0 && dir.charAt( 0 ) == PATH_SEPARATOR )
+        if ( dir.length() > 0 && dir.charAt( 0 ) == ScpHelper.PATH_SEPARATOR )
         {
             dir = dir.substring( 1 );
         }
 
-        ChannelSftp channel = null;
         try
         {
-            channel = (ChannelSftp) session.openChannel( SFTP_CHANNEL );
-
-            channel.connect();
-
-            SftpATTRS attrs = changeToRepositoryDirectory( channel, dir, filename );
+            SftpATTRS attrs = changeToRepositoryDirectory( dir, filename );
             if ( ( attrs.getPermissions() & S_IFDIR ) == 0 )
             {
                 throw new TransferFailedException( "Remote path is not a directory:" + dir );
@@ -515,44 +375,24 @@
 
             throw new TransferFailedException( msg, e );
         }
-        catch ( JSchException e )
-        {
-            String msg =
-                "Error occured while listing '" + destinationDirectory + "' " + "on remote repository: "
-                    + getRepository().getUrl();
-
-            throw new TransferFailedException( msg, e );
-        }
-        finally
-        {
-            if ( channel != null )
-            {
-                channel.disconnect();
-            }
-        }
     }
     
     public boolean resourceExists( String resourceName )
         throws TransferFailedException, AuthorizationException
     {
-        String filename = getResourceFilename( resourceName );
+        String filename = ScpHelper.getResourceFilename( resourceName );
 
-        String dir = getResourceDirectory( resourceName );
+        String dir = ScpHelper.getResourceDirectory( resourceName );
 
         // we already setuped the root directory. Ignore beginning /
-        if ( dir.length() > 0 && dir.charAt( 0 ) == PATH_SEPARATOR )
+        if ( dir.length() > 0 && dir.charAt( 0 ) == ScpHelper.PATH_SEPARATOR )
         {
             dir = dir.substring( 1 );
         }
 
-        ChannelSftp channel = null;
         try
         {
-            channel = (ChannelSftp) session.openChannel( SFTP_CHANNEL );
-
-            channel.connect();
-
-            changeToRepositoryDirectory( channel, dir, filename );
+            changeToRepositoryDirectory( dir, filename );
             
             return true;
         }
@@ -568,20 +408,134 @@
 
             throw new TransferFailedException( msg, e );
         }
-        catch ( JSchException e )
+    }
+
+    protected void cleanupGetTransfer( Resource resource )
+    {
+        returnToParentDirectory( resource );
+    }
+    
+    protected void cleanupPutTransfer( Resource resource )
+    {
+        returnToParentDirectory( resource );
+    }
+
+    protected void finishPutTransfer( Resource resource, InputStream input, OutputStream output )
+        throws TransferFailedException
+    {
+        RepositoryPermissions permissions = getRepository().getPermissions();
+
+        String filename = ScpHelper.getResourceFilename( resource.getName() );
+        if ( permissions != null && permissions.getGroup() != null )
         {
+            setGroup( filename, permissions );
+        }
+        
+        if ( permissions != null && permissions.getFileMode() != null )
+        {
+            setFileMode( filename, permissions );
+        }
+    }
+
+    public void fillInputData( InputData inputData )
+        throws TransferFailedException, ResourceDoesNotExistException
+    {
+        Resource resource = inputData.getResource();
+        
+        String filename = ScpHelper.getResourceFilename( resource.getName() );
+
+        String dir = ScpHelper.getResourceDirectory( resource.getName() );
+
+        // we already setuped the root directory. Ignore beginning /
+        if ( dir.length() > 0 && dir.charAt( 0 ) == ScpHelper.PATH_SEPARATOR )
+        {
+            dir = dir.substring( 1 );
+        }
+
+        try
+        {
+            SftpATTRS attrs = changeToRepositoryDirectory( dir, filename );
+
+            long lastModified = attrs.getMTime() * MILLIS_PER_SEC;
+            resource.setContentLength( attrs.getSize() );
+
+            resource.setLastModified( lastModified );
+            
+            inputData.setInputStream( channel.get( filename ) );
+        }
+        catch ( SftpException e )
+        {
+            handleGetException( resource, e );
+        }
+    }
+
+    public void fillOutputData( OutputData outputData )
+        throws TransferFailedException
+    {
+        int directoryMode = getDirectoryMode( getRepository().getPermissions() );
+
+        Resource resource = outputData.getResource();
+        
+        try
+        {
+            channel.cd( "/" );
+
+            String basedir = getRepository().getBasedir();
+            mkdirs( basedir + "/", directoryMode );
+
+            mkdirs( resource.getName(), directoryMode );
+
+            String filename = ScpHelper.getResourceFilename( resource.getName() );
+            outputData.setOutputStream( channel.put( filename ) );
+        }
+        catch ( TransferFailedException e )
+        {
+            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
+
+            throw e;
+        }
+        catch ( SftpException e )
+        {
+            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
+
             String msg =
-                "Error occured while looking for '" + resourceName + "' " + "on remote repository: "
+                "Error occured while deploying '" + resource.getName() + "' " + "to remote repository: "
                     + getRepository().getUrl();
 
             throw new TransferFailedException( msg, e );
         }
-        finally
+    }
+    
+    /**
+     * @param permissions repository's permissions
+     * @return the directory mode for the repository or <code>-1</code> if it
+     *         wasn't set
+     */
+    public int getDirectoryMode( RepositoryPermissions permissions )
+    {
+        int ret = -1;
+
+        if ( permissions != null )
         {
-            if ( channel != null )
-            {
-                channel.disconnect();
-            }
+            ret = getOctalMode( permissions.getDirectoryMode() );
         }
+
+        return ret;
+    }
+
+    public int getOctalMode( String mode )
+    {
+        int ret;
+        try
+        {
+            ret = Integer.valueOf( mode, 8 ).intValue();
+        }
+        catch ( NumberFormatException e )
+        {
+            // TODO: warning level
+            fireTransferDebug( "the file mode must be a numerical mode for SFTP" );
+            ret = -1;
+        }
+        return ret;
     }
 }
diff --git a/wagon-providers/wagon-ssh/src/test/java/org/apache/maven/wagon/providers/ssh/jsch/ScpWagonTest.java b/wagon-providers/wagon-ssh/src/test/java/org/apache/maven/wagon/providers/ssh/jsch/ScpWagonTest.java
index 8d65157..558c6ce 100644
--- a/wagon-providers/wagon-ssh/src/test/java/org/apache/maven/wagon/providers/ssh/jsch/ScpWagonTest.java
+++ b/wagon-providers/wagon-ssh/src/test/java/org/apache/maven/wagon/providers/ssh/jsch/ScpWagonTest.java
@@ -19,20 +19,20 @@
  * under the License.
  */
 
-import org.apache.maven.wagon.WagonTestCase;
+import java.io.File;
+
+import org.apache.maven.wagon.StreamingWagonTestCase;
 import org.apache.maven.wagon.authentication.AuthenticationInfo;
 import org.apache.maven.wagon.providers.ssh.TestData;
 import org.apache.maven.wagon.repository.Repository;
 import org.apache.maven.wagon.resource.Resource;
 
-import java.io.File;
-
 /**
  * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
  * @version $Id$
  */
 public class ScpWagonTest
-    extends WagonTestCase
+    extends StreamingWagonTestCase
 {
     protected String getProtocol()
     {
@@ -64,7 +64,7 @@
 
     protected long getExpectedLastModifiedOnGet( Repository repository, Resource resource )
     {
-        return 0;
+        return new File( repository.getBasedir(), resource.getName() ).lastModified();
     }
 
 }
diff --git a/wagon-providers/wagon-ssh/src/test/java/org/apache/maven/wagon/providers/ssh/jsch/ScpWagonWithSshPrivateKeySearchTest.java b/wagon-providers/wagon-ssh/src/test/java/org/apache/maven/wagon/providers/ssh/jsch/ScpWagonWithSshPrivateKeySearchTest.java
index 37ac361..61c3e41 100644
--- a/wagon-providers/wagon-ssh/src/test/java/org/apache/maven/wagon/providers/ssh/jsch/ScpWagonWithSshPrivateKeySearchTest.java
+++ b/wagon-providers/wagon-ssh/src/test/java/org/apache/maven/wagon/providers/ssh/jsch/ScpWagonWithSshPrivateKeySearchTest.java
@@ -19,7 +19,9 @@
  * under the License.
  */
 
-import org.apache.maven.wagon.WagonTestCase;
+import java.io.File;
+
+import org.apache.maven.wagon.StreamingWagonTestCase;
 import org.apache.maven.wagon.authentication.AuthenticationInfo;
 import org.apache.maven.wagon.providers.ssh.TestData;
 import org.apache.maven.wagon.repository.Repository;
@@ -30,7 +32,7 @@
  * @version $Id$
  */
 public class ScpWagonWithSshPrivateKeySearchTest
-    extends WagonTestCase
+    extends StreamingWagonTestCase
 {
     protected String getProtocol()
     {
@@ -42,7 +44,6 @@
         return TestData.getTestRepositoryUrl();
     }
 
-
     protected AuthenticationInfo getAuthInfo()
     {
         AuthenticationInfo authInfo = super.getAuthInfo();
@@ -56,6 +57,6 @@
 
     protected long getExpectedLastModifiedOnGet( Repository repository, Resource resource )
     {
-        return 0;
+        return new File( repository.getBasedir(), resource.getName() ).lastModified();
     }
 }
diff --git a/wagon-providers/wagon-ssh/src/test/java/org/apache/maven/wagon/providers/ssh/jsch/SftpWagonTest.java b/wagon-providers/wagon-ssh/src/test/java/org/apache/maven/wagon/providers/ssh/jsch/SftpWagonTest.java
index 170d5be..229b868 100644
--- a/wagon-providers/wagon-ssh/src/test/java/org/apache/maven/wagon/providers/ssh/jsch/SftpWagonTest.java
+++ b/wagon-providers/wagon-ssh/src/test/java/org/apache/maven/wagon/providers/ssh/jsch/SftpWagonTest.java
@@ -21,7 +21,7 @@
 
 import java.io.File;
 
-import org.apache.maven.wagon.WagonTestCase;
+import org.apache.maven.wagon.StreamingWagonTestCase;
 import org.apache.maven.wagon.authentication.AuthenticationInfo;
 import org.apache.maven.wagon.providers.ssh.TestData;
 import org.apache.maven.wagon.repository.Repository;
@@ -32,7 +32,7 @@
  * @version $Id$
  */
 public class SftpWagonTest
-    extends WagonTestCase
+    extends StreamingWagonTestCase
 {
     protected String getProtocol()
     {
diff --git a/wagon-providers/wagon-webdav-jackrabbit/pom.xml b/wagon-providers/wagon-webdav-jackrabbit/pom.xml
index 0f50f92..8686c66 100644
--- a/wagon-providers/wagon-webdav-jackrabbit/pom.xml
+++ b/wagon-providers/wagon-webdav-jackrabbit/pom.xml
@@ -1,31 +1,8 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one
-  ~ or more contributor license agreements.  See the NOTICE file
-  ~ distributed with this work for additional information
-  ~ regarding copyright ownership.  The ASF licenses this file
-  ~ to you 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.
--->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <artifactId>wagon-providers</artifactId>
     <groupId>org.apache.maven.wagon</groupId>
-    <version>1.0-rc1-SNAPSHOT</version>
+    <version>1.0-beta-3</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>wagon-webdav-jackrabbit</artifactId>
@@ -49,7 +26,7 @@
     <dependency>
       <groupId>${pom.groupId}</groupId>
       <artifactId>wagon-http-shared</artifactId>
-      <version>1.0-rc1-SNAPSHOT</version>
+      <version>1.0-beta-3</version>
     </dependency>
     <dependency>
       <groupId>org.apache.jackrabbit</groupId>
@@ -68,7 +45,7 @@
     </dependency>
     <dependency>
       <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-simple</artifactId>
+      <artifactId>slf4j-nop</artifactId>
       <version>1.5.0</version>
       <scope>runtime</scope>
     </dependency>
@@ -93,4 +70,4 @@
       <scope>test</scope>
     </dependency>
   </dependencies>
-</project>
+</project>
\ No newline at end of file
diff --git a/wagon-providers/wagon-webdav-jackrabbit/src/main/java/org/apache/maven/wagon/providers/webdav/WebDavWagon.java b/wagon-providers/wagon-webdav-jackrabbit/src/main/java/org/apache/maven/wagon/providers/webdav/WebDavWagon.java
index 839c3c1..addedeb 100644
--- a/wagon-providers/wagon-webdav-jackrabbit/src/main/java/org/apache/maven/wagon/providers/webdav/WebDavWagon.java
+++ b/wagon-providers/wagon-webdav-jackrabbit/src/main/java/org/apache/maven/wagon/providers/webdav/WebDavWagon.java
@@ -41,9 +41,7 @@
 import org.apache.maven.wagon.ResourceDoesNotExistException;
 import org.apache.maven.wagon.TransferFailedException;
 import org.apache.maven.wagon.authorization.AuthorizationException;
-import org.apache.maven.wagon.events.TransferEvent;
 import org.apache.maven.wagon.repository.Repository;
-import org.apache.maven.wagon.resource.Resource;
 import org.apache.maven.wagon.shared.http.AbstractHttpClientWagon;
 import org.codehaus.plexus.util.FileUtils;
 import org.codehaus.plexus.util.StringUtils;
@@ -84,51 +82,6 @@
     };
     
     /**
-     * Puts a file into the remote repository
-     *
-     * @param source       the file to transfer
-     * @param resourceName the name of the resource
-     * @throws TransferFailedException
-     * @throws ResourceDoesNotExistException
-     * @throws AuthorizationException
-     */
-    public void put( File source, String resourceName )
-        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
-    {
-        Repository repository = getRepository();
-
-        resourceName = StringUtils.replace( resourceName, "\\", "/" );
-        String dir = PathUtils.dirname( resourceName );
-        dir = StringUtils.replace( dir, "\\", "/" );
-
-        String dest = repository.getUrl();
-        Resource resource = new Resource( resourceName );
-
-        if ( dest.endsWith( "/" ) )
-        {
-            dest = dest + resource.getName();
-        }
-        else
-        {
-            dest = dest + "/" + resource.getName();
-        }
-
-        firePutInitiated( resource, source );
-
-        //Parent directories need to be created before posting
-        try
-        {
-            mkdirs( dir );
-        }
-        catch ( IOException e )
-        {
-            fireTransferError( resource, e, TransferEvent.REQUEST_GET );
-        }
-
-        super.put(source, resource);
-    }
-
-    /**
      * This wagon supports directory copying
      *
      * @return <code>true</code> always
@@ -147,7 +100,7 @@
      * @throws HttpException 
      * @throws TransferFailedException
      */
-    private void mkdirs( String dir ) throws HttpException, IOException
+    protected void mkdirs( String dir ) throws HttpException, IOException
     {
         Repository repository = getRepository();
         String basedir = repository.getBasedir();
diff --git a/wagon-providers/wagon-webdav-jackrabbit/src/test/java/org/apache/maven/wagon/providers/webdav/WebDavWagonTest.java b/wagon-providers/wagon-webdav-jackrabbit/src/test/java/org/apache/maven/wagon/providers/webdav/WebDavWagonTest.java
index 82355d0..df51094 100644
--- a/wagon-providers/wagon-webdav-jackrabbit/src/test/java/org/apache/maven/wagon/providers/webdav/WebDavWagonTest.java
+++ b/wagon-providers/wagon-webdav-jackrabbit/src/test/java/org/apache/maven/wagon/providers/webdav/WebDavWagonTest.java
@@ -23,7 +23,7 @@
 import java.io.IOException;
 
 import org.apache.maven.wagon.FileTestUtils;
-import org.apache.maven.wagon.WagonTestCase;
+import org.apache.maven.wagon.StreamingWagonTestCase;
 import org.apache.maven.wagon.repository.Repository;
 import org.apache.maven.wagon.resource.Resource;
 
@@ -34,7 +34,7 @@
  * @author <a href="mailto:carlos@apache.org">Carlos Sanchez</a>
  */
 public class WebDavWagonTest
-    extends WagonTestCase
+    extends StreamingWagonTestCase
 {
     private ServletServer server;