You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by og...@apache.org on 2008/08/02 00:55:48 UTC

svn commit: r681887 [7/10] - in /maven/sandbox/trunk/mercury: mercury-artifact/ mercury-artifact/src/ mercury-artifact/src/main/ mercury-artifact/src/main/java/ mercury-artifact/src/main/java/org/ mercury-artifact/src/main/java/org/apache/ mercury-arti...

Added: maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/FileGetExchange.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/FileGetExchange.java?rev=681887&view=auto
==============================================================================
--- maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/FileGetExchange.java (added)
+++ maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/FileGetExchange.java Fri Aug  1 15:55:40 2008
@@ -0,0 +1,151 @@
+/**
+ * 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.
+ */
+
+package org.apache.maven.mercury.spi.http.client.retrieve;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletResponse;
+
+
+import org.apache.maven.mercury.spi.http.client.FileExchange;
+import org.apache.maven.mercury.spi.http.client.HttpClientException;
+import org.apache.maven.mercury.spi.http.client.ObservableOutputStream;
+import org.apache.maven.mercury.transport.api.Binding;
+import org.apache.maven.mercury.transport.api.StreamObserver;
+import org.mortbay.io.Buffer;
+import org.mortbay.jetty.HttpMethods;
+import org.mortbay.jetty.client.HttpClient;
+
+
+/**
+ * FileGetExchange
+ * <p/>
+ * Make an asynchronous request to download a file and stream its bytes to a file.
+ * When all bytes have been received onFileComplete will be called.
+ * <p/>
+ * As an optimization, the file that is being downloaded can have it's
+ * SHA-1 digest calculated as it is being streamed down.
+ */
+public abstract class FileGetExchange extends FileExchange
+{
+    private OutputStream _outputStream;
+    private Set<StreamObserver> _observers = new HashSet<StreamObserver>();
+
+    /**
+     * Constructor.
+     *
+     * @param binding        the remote file to fetch
+     * @param localFile      the local file location to store the remote file
+     * @param observers      observers of the io stream
+     * @param client         async http client
+     */
+    public FileGetExchange( Binding binding, File localFile, Set<StreamObserver> observers, HttpClient client )
+    {
+        super( binding, localFile, client );
+        _observers.addAll(observers);
+    }
+
+
+    /** Start the retrieval. */
+    public void send()
+    {
+        setMethod( HttpMethods.GET );
+        super.send();
+    }
+
+
+    protected void onResponseComplete()
+    {
+        //All bytes of file have been received
+        try
+        {
+            if ( _status == HttpServletResponse.SC_NOT_FOUND )
+            {
+                onFileError( _url, new FileNotFoundException( "File not found on remote server" ) );
+                return;
+            }
+            else if ( _status != HttpServletResponse.SC_OK )
+            {
+                onFileError( _url, new Exception( "Http status code=" + _status ) );
+                return;
+            }
+
+            onFileComplete( _url, _localFile );
+        }
+        catch ( Exception e )
+        {
+            onFileError( _url, new HttpClientException( _binding, e.getLocalizedMessage() ) );
+        }
+    }
+
+
+    /**
+     * Stream the downloaded bytes to a file
+     *
+     * @see org.mortbay.jetty.client.HttpExchange$ContentExchange#onResponseContent(org.sonatype.io.Buffer)
+     */
+    protected void onResponseContent( Buffer content )
+        throws IOException
+    {
+        try
+        {
+            OutputStream os = getOutputStream();
+            content.writeTo( os );
+        }
+        catch ( NoSuchAlgorithmException e )
+        {
+            throw new IOException( e.getLocalizedMessage() );
+        }
+    }
+
+
+    /**
+     * Get an output stream for the file contents. A digest can be optionally calculated
+     * for the file contents as they are being streamed.
+     *
+     * @return OutputStream for file contents
+     * @throws IOException              if io error occurs
+     * @throws NoSuchAlgorithmException if the SHA-1 algorithm is not supported
+     */
+    protected OutputStream getOutputStream()
+        throws IOException, NoSuchAlgorithmException
+    {
+        if ( _outputStream == null )
+        {
+            OutputStream os = null;
+            if (_binding.isFile())
+                os = new FileOutputStream( _localFile );
+            else if (_binding.isInMemory())
+                os = _binding.getLocalOutputStream();
+            
+            ObservableOutputStream oos = new ObservableOutputStream( os );
+            oos.addObservers(_observers);
+            _outputStream = oos;
+        }
+        return _outputStream;
+    }
+}

Added: maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/RetrievalCallback.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/RetrievalCallback.java?rev=681887&view=auto
==============================================================================
--- maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/RetrievalCallback.java (added)
+++ maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/RetrievalCallback.java Fri Aug  1 15:55:40 2008
@@ -0,0 +1,34 @@
+/**
+ * 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.
+ */
+
+package org.apache.maven.mercury.spi.http.client.retrieve;
+
+/**
+ * Classes that implement this method will be notified when a given job has
+ * been completed and validated.
+ */
+public interface RetrievalCallback
+{
+    /**
+     * Callback for asynchronous version of Retriever.retrieve.
+     *
+     * @param response empty if all artifacts retrieved ok, list of exceptions otherwise
+     */
+    public abstract void onComplete( RetrievalResponse response );
+}

Added: maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/RetrievalRequest.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/RetrievalRequest.java?rev=681887&view=auto
==============================================================================
--- maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/RetrievalRequest.java (added)
+++ maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/RetrievalRequest.java Fri Aug  1 15:55:40 2008
@@ -0,0 +1,42 @@
+/**
+ * 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.
+ */
+
+package org.apache.maven.mercury.spi.http.client.retrieve;
+
+import java.util.Set;
+
+
+import org.apache.maven.mercury.spi.http.validate.Validator;
+import org.apache.maven.mercury.transport.api.Binding;
+
+
+/**
+ * RetrievalRequest
+ * <p/>
+ * A set of files to retrieve from remote locations
+ * and a set of validators to apply to them.
+ */
+public interface RetrievalRequest
+{    
+    Set<Binding> getBindings();
+
+    boolean isFailFast();
+    
+    Set<Validator> getValidators();
+}

Added: maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/RetrievalResponse.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/RetrievalResponse.java?rev=681887&view=auto
==============================================================================
--- maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/RetrievalResponse.java (added)
+++ maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/RetrievalResponse.java Fri Aug  1 15:55:40 2008
@@ -0,0 +1,44 @@
+/**
+ * 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.
+ */
+
+package org.apache.maven.mercury.spi.http.client.retrieve;
+
+import java.util.Set;
+
+import org.apache.maven.mercury.spi.http.client.HttpClientException;
+
+
+/**
+ * RetrievalResponse
+ * <p/>
+ * Response from a request to download a set of files.
+ */
+public interface RetrievalResponse
+{
+    /**
+     * The set will be empty if the operation completed successfully,
+     * or will contain a single entry if the Request is failFast, otherwise
+     * there will be one exception for every Binding in the Request.
+     *
+     * @return
+     */
+    Set<HttpClientException> getExceptions();
+    
+    boolean hasExceptions();
+}

Added: maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/RetrievalTarget.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/RetrievalTarget.java?rev=681887&view=auto
==============================================================================
--- maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/RetrievalTarget.java (added)
+++ maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/RetrievalTarget.java Fri Aug  1 15:55:40 2008
@@ -0,0 +1,450 @@
+/**
+ * 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.
+ */
+
+package org.apache.maven.mercury.spi.http.client.retrieve;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.maven.mercury.spi.http.client.FileExchange;
+import org.apache.maven.mercury.spi.http.client.HttpClientException;
+import org.apache.maven.mercury.spi.http.validate.Validator;
+import org.apache.maven.mercury.transport.api.Binding;
+import org.apache.maven.mercury.transport.api.StreamObserver;
+import org.apache.maven.mercury.transport.api.Verifier;
+import org.mortbay.jetty.client.HttpExchange;
+
+
+/**
+ * RetrievalTarget
+ * <p/>
+ * A RetrievalTarget is a remote file that must be downloaded locally, checksummed
+ * and then atomically moved to its final location. The RetrievalTarget encapsulates
+ * the temporary local file to which the remote file is downloaded, and also the
+ * retrieval of the checksum file(s) and the checksum calculation(s).
+ */
+public abstract class RetrievalTarget
+{
+    public static final String __PREFIX = "JTY_";
+    public static final String __TEMP_SUFFIX = ".tmp";
+    public static final int __START_STATE = 1;
+    public static final int __REQUESTED_STATE = 2;
+    public static final int __READY_STATE = 3;
+
+    protected int _checksumState;
+    protected int _targetState;
+    
+    protected HttpClientException _exception;
+    protected Binding _binding;
+    protected File _tempFile;
+    protected DefaultRetriever _retriever;
+    protected boolean _complete;
+    protected HttpExchange _exchange;
+    protected Set<Validator> _validators;
+    protected Set<StreamObserver> _observers = new HashSet<StreamObserver>();
+    protected List<Verifier> _verifiers = new ArrayList<Verifier>();
+    protected Map<Verifier, String> _verifierMap = new HashMap<Verifier, String>();
+ 
+    
+    public abstract void onComplete();
+
+    public abstract void onError( HttpClientException exception );
+
+    /**
+     * Constructor
+     *
+     * @param binding
+     * @param callback
+     */
+    public RetrievalTarget( DefaultRetriever retriever, Binding binding, Set<Validator> validators, Set<StreamObserver> observers )
+    {
+        if ( binding == null || 
+                (binding.getRemoteResource() == null) || 
+                (binding.isFile() && (binding.getLocalFile() == null)) ||
+                (binding.isInMemory() && (binding.getLocalOutputStream() == null)))
+        {
+            throw new IllegalArgumentException( "Nothing to retrieve" );
+        }
+        _retriever = retriever;
+        _binding = binding;
+        _validators = validators;
+       
+        //sift out the potential checksum verifiers
+        for (StreamObserver o: observers)
+        {
+            if (Verifier.class.isAssignableFrom(o.getClass()))
+                _verifiers.add((Verifier)o);
+            else
+                _observers.add(o);
+        }
+        
+        if (_binding.isFile())
+        {
+            _tempFile = new File( _binding.getLocalFile().getParentFile(),
+                                  __PREFIX + _binding.getLocalFile().getName() + __TEMP_SUFFIX );        
+            _tempFile.deleteOnExit();
+            if ( !_tempFile.getParentFile().exists() )
+            {
+                _tempFile.getParentFile().mkdirs();
+            }
+
+            if ( _tempFile.exists() )
+            {
+                onError( new HttpClientException( binding, "File exists " + _tempFile.getAbsolutePath() ) );
+            }
+            else if ( !_tempFile.getParentFile().canWrite() )
+            {
+                onError( new HttpClientException( binding,
+                        "Unable to write to dir " + _tempFile.getParentFile().getAbsolutePath() ) );
+            }
+        }
+    }
+
+   
+
+    public File getTempFile()
+    {
+        return _tempFile;
+    }
+
+    public String getUrl()
+    {
+        return _binding.getRemoteResource().toExternalForm();
+    }
+
+
+    /** Start by getting the appropriate checksums */
+    public void retrieve()
+    {
+        //if there are no checksum verifiers configured, proceed directly to get the file
+        if (_verifiers.size() == 0)
+        {
+            _checksumState = __READY_STATE;
+            updateTargetState(__START_STATE, null);
+        }
+        else
+        {
+            _checksumState = __START_STATE;
+            updateChecksumState(-1, null);
+        }
+    }
+
+
+    /** Move the temporary file to its final location */
+    public boolean move()
+    {
+        if (_binding.isFile())
+            return _tempFile.renameTo( _binding.getLocalFile() );
+        else
+            return true;
+    }
+
+    /** Cleanup temp files */
+    public synchronized void cleanup()
+    {
+        deleteTempFile();
+        if ( _exchange != null )
+        {
+            _exchange.cancel();
+        }
+    }
+
+    public synchronized boolean isComplete()
+    {
+        return _complete;
+    }
+
+    public String toString()
+    {
+        return "T:" + _binding.getRemoteResource() + ":" + _targetState + ":" + _checksumState + ":" + _complete;
+    }
+
+    private void updateChecksumState (int index, Throwable ex)
+    {
+        if ( _exception == null && ex != null )
+        {
+            if ( ex instanceof HttpClientException )
+            {
+                _exception = (HttpClientException) ex;
+            }
+            else
+            {
+                _exception = new HttpClientException( _binding, ex );
+            }
+        }
+        
+        if (ex != null)
+        {
+            _checksumState = __READY_STATE;
+            onError(_exception);
+        }
+        else
+        {     
+            boolean proceedWithTargetFile = false;
+            if (index >= 0)
+            {
+                //check if the just-completed retrieval means that we can stop trying to download checksums 
+                Verifier v = _verifiers.get(index);
+                if (_verifierMap.containsKey(v) && v.isSufficient())
+                    proceedWithTargetFile = true;
+            }
+
+            index++;
+            
+            if ((index < _verifiers.size()) && !proceedWithTargetFile)
+            {
+                retrieveChecksum(index);
+            }
+            else
+            {
+                _checksumState = __READY_STATE;
+
+                //finished retrieving all possible checksums. Add all verifiers
+                //that had matching checksums into the observers list
+                _observers.addAll(_verifierMap.keySet());
+
+                //now get the file now we have the checksum sorted out
+                updateTargetState( __START_STATE, null );
+            }
+        }
+    }
+    
+    
+   
+   
+
+    /**
+     * Check the actual checksum against the expected checksum
+     *
+     * @return
+     */
+    public boolean verifyChecksum()
+    {
+        boolean ok = true;
+        
+        synchronized (_verifierMap)
+        {
+            Iterator<Map.Entry<Verifier, String>> itor = _verifierMap.entrySet().iterator();
+            while (itor.hasNext() && ok)
+            {               
+                Map.Entry<Verifier, String> e = itor.next();
+                ok = e.getKey().verifySignature(e.getValue());
+            }
+        }
+        
+        return ok;
+    }
+        
+    
+
+    public boolean validate( List<String> errors )
+    {
+        if ( _validators == null || _validators.isEmpty() )
+        {
+            return true;
+        }
+
+        String ext =  _binding.getRemoteResource().toString();
+        if (ext.endsWith("/"))
+            ext = ext.substring(0, ext.length()-1);
+        
+        int i = ext.lastIndexOf( "." );
+        ext = ( i > 0 ? ext.substring( i + 1 ) : "" );
+
+        for ( Validator v : _validators )
+        {
+            String vExt = v.getFileExtension();
+            if ( vExt.equalsIgnoreCase( ext ) )
+            {
+                try
+                {
+                    if (_binding.isFile())
+                    {
+                        if ( !v.validate( _tempFile.getCanonicalPath(), errors ) )
+                        {
+                            return false;
+                        }
+                    }
+                    else if (_binding.isInMemory())
+                    {
+                        //TODO ????
+                        //v.validate(_binding.getInboundContent()) 
+                    }
+                }
+                catch ( IOException e )
+                {
+                    errors.add( e.getMessage() );
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+  
+
+    protected synchronized void updateTargetState( int state, Throwable ex )
+    {
+        _targetState = state;
+        if ( _exception == null && ex != null )
+        {
+            if ( ex instanceof HttpClientException )
+            {
+                _exception = (HttpClientException) ex;
+            }
+            else
+            {
+                _exception = new HttpClientException( _binding, ex );
+            }
+        }
+
+        if ( _targetState == __START_STATE )
+        {
+            _exchange = retrieveTargetFile();
+        }
+
+        //if both checksum and target file are ready, we're ready to return callback
+        if (_targetState == __READY_STATE )
+        {
+            _complete = true;
+            if ( _exception == null )
+            {
+                onComplete();
+            }
+            else
+            {
+                onError( _exception );
+            }
+        }
+    }
+
+    /** Asynchronously fetch the checksum for the target file. */
+    private HttpExchange retrieveChecksum(final int index)
+    {
+        HttpExchange exchange = new HttpExchange.ContentExchange()
+        {
+            protected void onException( Throwable ex )
+            {
+                //if the checksum is mandatory, then propagate the exception and stop processing
+                if (!_verifiers.get(index).isLenient())
+                {
+                    updateChecksumState(index, ex);
+                }
+                else
+                    updateChecksumState(index, null);
+                
+            }
+
+            protected void onResponseComplete() throws IOException
+            {
+                super.onResponseComplete();
+                Verifier v = _verifiers.get(index);
+                
+                if ( getResponseStatus() == HttpServletResponse.SC_OK )
+                {
+                    //We got a checksum so match it up with the verifier it is for
+                    synchronized (_verifierMap)
+                    {
+                        if (v.isSufficient())
+                            _verifierMap.clear(); //remove all other entries, we only need one checksum
+                        _verifierMap.put(v, getResponseContent().trim());
+                    }
+                    updateChecksumState(index, null);
+                }
+                else 
+                {
+                    if (!v.isLenient()) 
+                    {
+                        //checksum file MUST be present, fail
+                        updateChecksumState(index, new Exception ("Mandatory checksum file not found "+this.getURI()));
+                    }
+                    else
+                        updateChecksumState(index, null);
+                }
+            }
+        };
+        
+        exchange.setURL( getChecksumFileURLAsString( _verifiers.get(index)) );
+
+        try
+        {
+            _retriever.getHttpClient().send( exchange );
+        }
+        catch ( IOException ex )
+        {
+            updateChecksumState(index, ex);
+        }
+        return exchange;
+    }
+
+
+    /** Asynchronously fetch the target file. */
+    private HttpExchange retrieveTargetFile()
+    {
+        updateTargetState( __REQUESTED_STATE, null );
+
+        //get the file, calculating the digest for it on the fly
+        FileExchange exchange = new FileGetExchange( _binding, getTempFile(), _observers, _retriever.getHttpClient() )
+        {
+            public void onFileComplete( String url, File localFile )
+            {
+                //we got the target file ok, so tell our main callback
+                _targetState = __READY_STATE;
+                updateTargetState( __READY_STATE, null );
+            }
+
+            public void onFileError( String url, Exception e )
+            {
+                //an error occurred whilst fetching the file, return an error
+                _targetState = __READY_STATE;
+                updateTargetState( __READY_STATE, e );
+            }
+        };
+        exchange.send();
+        return exchange;
+    }
+
+    private String getChecksumFileURLAsString (Verifier verifier)
+    {
+        String extension = verifier.getExtension();
+        if (extension.charAt(0) != '.')
+            extension = "."+extension;
+        return _binding.getRemoteResource().toString() + extension;
+    }
+
+    private boolean deleteTempFile()
+    {
+        if ( _tempFile != null && _tempFile.exists() )
+        {
+            return _tempFile.delete();
+        }
+        return false;
+    }
+    
+}

Added: maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/Retriever.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/Retriever.java?rev=681887&view=auto
==============================================================================
--- maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/Retriever.java (added)
+++ maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/client/retrieve/Retriever.java Fri Aug  1 15:55:40 2008
@@ -0,0 +1,51 @@
+/**
+ * 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.
+ */
+
+package org.apache.maven.mercury.spi.http.client.retrieve;
+
+/**
+ * Retriever
+ * <p/>
+ * Component to retrieve a set of remote files as an atomic operation.
+ */
+public interface Retriever
+{
+    /**
+     * Retrieve a set of artifacts and wait until all retrieved successfully
+     * or an error occurs.
+     * <p/>
+     * Note: whilst this method is synchronous for the caller, the implementation
+     * will be asynchronous so many artifacts are fetched in parallel.
+     *
+     * @param request
+     * @return
+     */
+    RetrievalResponse retrieve( RetrievalRequest request );
+
+
+    /**
+     * Retrieve a set of artifacts without waiting for the results.
+     * When all results have been obtained (or an error occurs) the
+     * RetrievalResponse will be called.
+     *
+     * @param request
+     * @param callback
+     */
+    void retrieve( RetrievalRequest request, RetrievalCallback callback );
+}

Added: maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/server/BatchFilter.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/server/BatchFilter.java?rev=681887&view=auto
==============================================================================
--- maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/server/BatchFilter.java (added)
+++ maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/server/BatchFilter.java Fri Aug  1 15:55:40 2008
@@ -0,0 +1,452 @@
+/**
+ * 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.
+ */
+
+package org.apache.maven.mercury.spi.http.server;
+
+import org.mortbay.jetty.HttpMethods;
+import org.mortbay.servlet.PutFilter;
+import org.mortbay.util.IO;
+import org.mortbay.util.URIUtil;
+
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * BatchFilter
+ * <p/>
+ * Base class for handling atomic uploads of batches of files.
+ * Subclasses should implement their own means of making the
+ * uploads atomic. The methods putFile, commitFiles, discardFiles
+ * can be overridden/implemented in order to facilitate this.
+ * For example, the DefaultBatchFilter subclass copies all files
+ * to a staging area before moving them to their final locations
+ * upon receipt of a Jetty-Batch-Commit message.
+ * <p/>
+ * TODO consider having a scavenger thread to remove failed or incomplete uploads?
+ *
+ * @see org.sonatype.mercury.server.jetty.DefaultBatchFilter
+ */
+public abstract class BatchFilter extends PutFilter
+{
+    protected ConcurrentMap<String, Batch> _batches = new ConcurrentHashMap<String, Batch>();
+    protected String _batchIdHeader = "Jetty-Batch-Id";
+    protected String _batchSupportedHeader = "Jetty-Batch-Supported";
+    protected String _batchCommitHeader = "Jetty-Batch-Commit";
+    protected String _batchDiscardHeader = "Jetty-Batch-Discard";
+
+    /**
+     * Batch
+     * <p/>
+     * Retains the status of a mercury. If a mercury succeeds it is removed
+     * from the list. If it fails, then it is retained in the list
+     * but marked as failed. If a mercury is not completed, then the
+     * timestamp can be used by a timer thread to clean up.
+     */
+    protected class Batch
+    {
+        protected String _batchId;
+        protected long _timestamp;
+        protected boolean _ok;
+        protected List<String> _files;
+
+
+        public Batch( String batchId, long timestamp )
+        {
+            _batchId = batchId;
+            _timestamp = timestamp;
+            _files = new ArrayList<String>();
+        }
+
+        public String getBatchId()
+        {
+            return _batchId;
+        }
+
+        public void addFile( String file )
+        {
+            _files.add( file );
+        }
+
+        public List getFiles()
+        {
+            return _files;
+        }
+
+        public void failed()
+        {
+            _ok = false;
+        }
+
+        public boolean isOK()
+        {
+            return _ok;
+        }
+
+        public long getTimestamp()
+        {
+            return _timestamp;
+        }
+
+        public String toString()
+        {
+            return "BatchStatus: id=" + _batchId + " ts=" + _timestamp + " count=" + _files.size() + ", " + _ok;
+        }
+    }
+
+    /**
+     * Implement this method to finish the upload of the files by making them
+     * available for download. When this method returns, all files forming part of
+     * the mercury should be available.
+     *
+     * @param request
+     * @param response
+     * @param batchId
+     * @return
+     * @throws Exception
+     */
+    public abstract boolean commitFiles( HttpServletRequest request, HttpServletResponse response, Batch batch )
+        throws Exception;
+
+    /**
+     * Implement this method to abort the upload of a mercury of files. When this method returns,
+     * none of the files forming part of the upload should be available for download.
+     *
+     * @param request
+     * @param response
+     * @param batchId
+     * @return
+     * @throws Exception
+     */
+    public abstract boolean discardFiles( HttpServletRequest request, HttpServletResponse response, Batch batch )
+        throws Exception;
+
+
+    /**
+     * Initialize the filter. Read all configurable parameters.
+     *
+     * @see org.sonatype.servlet.PutFilter#init(javax.servlet.FilterConfig)
+     */
+    public void init( FilterConfig config )
+        throws ServletException
+    {
+        super.init( config );
+
+        //allow name of headers to be exchanged to be configured
+        String s = config.getInitParameter( "batchIdHeader" );
+        if ( s != null )
+        {
+            _batchIdHeader = s;
+        }
+        s = config.getInitParameter( "batchSupportedHeader" );
+        if ( s != null )
+        {
+            _batchSupportedHeader = s;
+        }
+        s = config.getInitParameter( "batchCommitHeader" );
+        if ( s != null )
+        {
+            _batchCommitHeader = s;
+        }
+        s = config.getInitParameter( "batchDiscardHeader" );
+        if ( s != null )
+        {
+            _batchDiscardHeader = s;
+        }
+    }
+
+
+    /**
+     * Run the filter.
+     *
+     * @see org.sonatype.servlet.PutFilter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
+     */
+    public void doFilter( ServletRequest req, ServletResponse res, FilterChain chain )
+        throws IOException, ServletException
+    {
+        HttpServletRequest request = (HttpServletRequest) req;
+        HttpServletResponse response = (HttpServletResponse) res;
+
+        //if GET fall through to filter chain
+        if ( request.getMethod().equals( HttpMethods.GET ) )
+        {
+            chain.doFilter( req, res );
+            return;
+        }
+
+
+        String batchId = request.getHeader( _batchIdHeader );
+        String commitId = request.getHeader( _batchCommitHeader );
+        String discardId = request.getHeader( _batchDiscardHeader );
+
+        //System.err.println("method="+request.getMethod()+" batchid="+batchId+" commitId="+commitId+" discardId="+discardId);
+
+        //we can't do  atomic batches, handle as a normal PUT
+        if ( batchId == null && commitId == null && discardId == null )
+        {
+            System.err.println( "Not a batching request, doing PutFilter instead" );
+            super.doFilter( req, res, chain );
+            return;
+        }
+
+        /* TODO Is it worth handling this situation? This would mean that a directory was sent as the url 
+         * along with a batchId. The cost is that the pathContext would be calculated twice in this case.
+         
+        if (pathInContext.endsWith("/"))
+        {
+            super.doFilter(req,res,chain); 
+            return;
+        }
+        */
+
+        if ( batchId != null )
+        {
+            handlePut( request, response, batchId );
+            return;
+        }
+
+        if ( discardId != null )
+        {
+            handleDiscard( request, response, discardId );
+            return;
+        }
+
+        if ( commitId != null )
+        {
+            handleCommit( request, response, commitId );
+            return;
+        }
+
+        //otherwise - shouldn't get here
+        chain.doFilter( req, res );
+    }
+
+    /**
+     * Handle a PUT request.
+     * <p/>
+     * The batchId is saved to a list of currently active batchIds so that
+     * all files forming part of the mercury can be committed or discarded as a
+     * whole later on.
+     * <p/>
+     * If a file already exists, then status 200 is returned; if the file
+     * did not previously exist, then status 201 is returned, otherwise
+     * a 403 is returned.
+     *
+     * @param request
+     * @param response
+     * @param batchId
+     * @throws ServletException
+     * @throws IOException
+     */
+    public void handlePut( HttpServletRequest request, HttpServletResponse response, String batchId )
+        throws ServletException, IOException
+    {
+        String servletPath = request.getServletPath();
+        String pathInfo = request.getPathInfo();
+        String pathInContext = URIUtil.addPaths( servletPath, pathInfo );
+        String finalResource = URIUtil.addPaths( _baseURI, pathInContext );
+        File finalFile = null;
+        try
+        {
+            finalFile = new File( new URI( finalResource ) );
+            boolean exists = finalFile.exists();
+
+            putFile( request, response, pathInContext, batchId );
+
+            Batch batch = addBatch( batchId, finalResource );
+
+            String contextPath = _context.getContextPath();
+            if ( contextPath.equals( "" ) )
+            {
+                contextPath = "/";
+            }
+            if ( !contextPath.endsWith( "/" ) )
+            {
+                contextPath += "/";
+            }
+            String commitBatchUrl = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + contextPath;
+            response.setHeader( _batchSupportedHeader, commitBatchUrl );
+            response.setStatus( exists ? HttpServletResponse.SC_OK : HttpServletResponse.SC_CREATED );
+            response.flushBuffer();
+        }
+        catch ( Exception ex )
+        {
+            _context.log( ex.toString(), ex );
+            response.sendError( HttpServletResponse.SC_FORBIDDEN );
+        }
+    }
+
+    /**
+     * Client side wants us to discard all files in mercury.
+     *
+     * @param request
+     * @param response
+     * @param batchId
+     * @throws ServletException
+     * @throws IOException
+     */
+    public void handleDiscard( HttpServletRequest request, HttpServletResponse response, String batchId )
+        throws ServletException, IOException
+    {
+        boolean ok = true;
+        try
+        {
+            ok = discardFiles( request, response, _batches.get( batchId ) );
+            response.setStatus( ( ok ? HttpServletResponse.SC_OK : HttpServletResponse.SC_INTERNAL_SERVER_ERROR ) );
+            response.flushBuffer();
+        }
+        catch ( Exception ex )
+        {
+            _context.log( ex.toString(), ex );
+            response.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
+        }
+        finally
+        {
+            updateBatch( batchId, ok );
+        }
+    }
+
+
+    /**
+     * Client side wants us to move files into final position.
+     *
+     * @param request
+     * @param response
+     * @param batchId
+     * @throws ServletException
+     * @throws IOException
+     */
+    public void handleCommit( HttpServletRequest request, HttpServletResponse response, String batchId )
+        throws ServletException, IOException
+    {
+        boolean ok = true;
+        try
+        {
+            ok = commitFiles( request, response, _batches.get( batchId ) );
+            response.setStatus( ( ok ? HttpServletResponse.SC_OK : HttpServletResponse.SC_INTERNAL_SERVER_ERROR ) );
+            response.flushBuffer();
+        }
+        catch ( Exception ex )
+        {
+            _context.log( ex.toString(), ex );
+            response.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
+        }
+        finally
+        {
+            updateBatch( batchId, ok );
+        }
+    }
+
+    /**
+     * Default behaviour is to put the file directly to it's final location.
+     * <p/>
+     * Subclasses can choose to override this method and put the file
+     * into a staging area first.
+     *
+     * @param request
+     * @param response
+     * @param pathInContext
+     * @param batchId
+     * @throws Exception
+     * @see org.sonatype.mercury.server.jetty.DefaultBatchFilter
+     */
+    public void putFile( HttpServletRequest request,
+                         HttpServletResponse response,
+                         String pathInContext,
+                         String batchId )
+        throws Exception
+    {
+        String finalResource = URIUtil.addPaths( _baseURI, pathInContext );
+        File finalFile = null;
+        finalFile = new File( new URI( finalResource ) );
+        File parent = finalFile.getParentFile();
+        parent.mkdirs();
+        int toRead = request.getContentLength();
+        InputStream in = request.getInputStream();
+        OutputStream out = new FileOutputStream( finalFile, false );
+        if ( toRead >= 0 )
+        {
+            IO.copy( in, out, toRead );
+        }
+        else
+        {
+            IO.copy( in, out );
+        }
+
+    }
+
+    /**
+     * Remember a mercury, or update the count of files in the mercury.
+     *
+     * @param batchId
+     */
+    protected Batch addBatch( String batchId, String file )
+    {
+        Batch status = (Batch) _batches.get( batchId );
+        long timestamp = System.currentTimeMillis();
+        if ( status == null )
+        {
+            status = new Batch( batchId, timestamp );
+            _batches.put( batchId, status );
+        }
+        status.addFile( file );
+        return status;
+    }
+
+
+    /**
+     * Update the status of the mercury.
+     *
+     * @param batchId
+     * @param ok      if true, the mercury job is removed from the list; otherwise it is marked as failed
+     */
+    protected void updateBatch( String batchId, boolean ok )
+    {
+        Batch status = (Batch) _batches.get( batchId );
+        if ( status == null )
+        {
+            _context.log( "Unknown mercury id to update: " + batchId );
+        }
+        else
+        {
+            if ( ok )
+            {
+                _batches.remove( batchId );
+            }
+            else
+            {
+                status.failed(); //mark as failed
+            }
+        }
+    }
+}

Added: maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/server/StagingBatchFilter.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/server/StagingBatchFilter.java?rev=681887&view=auto
==============================================================================
--- maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/server/StagingBatchFilter.java (added)
+++ maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/server/StagingBatchFilter.java Fri Aug  1 15:55:40 2008
@@ -0,0 +1,229 @@
+/**
+ * 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.
+ */
+
+package org.apache.maven.mercury.spi.http.server;
+
+import org.mortbay.util.IO;
+import org.mortbay.util.URIUtil;
+
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+
+
+/**
+ * DefaultBatchFilter
+ * <p/>
+ * Handles the atomic upload (using PUT messages) of a batch of files. "Atomic" means
+ * that either all file uploads succeed or none do. This transactionality can only be
+ * guaranteed when using the mercury client, as a "commit/discard" message
+ * is sent from the client side to indicate how to terminate the mercury operation. If
+ * a commit is received, then all files that form part of the batch - indicated by a
+ * batch id in the PUT headers - are moved from a staging location to the final
+ * location. If a discard is received, then all files forming part of the mercury will
+ * be deleted. If the client side is not the jetty batcher, then the server side
+ * cannot know when the batch has ended, and therefore will immediately copy files
+ * to their final locations during the PUT.
+ */
+public class StagingBatchFilter extends BatchFilter
+{
+    private String _stagingDirURI;
+
+    public void init( FilterConfig config )
+        throws ServletException
+    {
+        super.init( config );
+
+        //allow tmp dir location to be configured
+        String t = config.getInitParameter( "stagingDirURI" );
+        if ( t != null )
+        {
+            _stagingDirURI = t;
+        }
+        else
+        {
+            //fall back to WEB-INF/lib
+            File f = new File( _context.getRealPath( "/" ) );
+            File w = new File( f, "WEB-INF" );
+            File l = new File( w, "lib" );
+            _stagingDirURI = l.toURI().toString();
+        }
+    }
+
+
+    /**
+     * Put the file to a staging area before doing move to final location
+     * on a commit.
+     *
+     * @see BatchFilter#putFile(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.String, java.lang.String)
+     */
+    public void putFile( HttpServletRequest request,
+                         HttpServletResponse response,
+                         String pathInContext,
+                         String batchId )
+        throws Exception
+    {
+        String stagedResource = URIUtil.addPaths( _stagingDirURI,
+            batchId ); //put the file into staging dir under the batchid
+        stagedResource = URIUtil.addPaths( stagedResource, pathInContext );
+        File stagedFile = null;
+
+        try
+        {
+            stagedFile = new File( new URI( stagedResource ) );
+            File parent = stagedFile.getParentFile();
+            parent.mkdirs();
+
+            int toRead = request.getContentLength();
+            InputStream in = request.getInputStream();
+            OutputStream out = new FileOutputStream( stagedFile, false );
+            if ( toRead >= 0 )
+            {
+                IO.copy( in, out, toRead );
+            }
+            else
+            {
+                IO.copy( in, out );
+            }
+            out.close();
+        }
+        catch ( Exception e )
+        {
+            try
+            {
+                if ( stagedFile.exists() )
+                {
+                    stagedFile.delete();
+                }
+                throw e;
+            }
+            catch ( Exception ex )
+            {
+                _context.log( ex.toString(), ex );
+            }
+        }
+    }
+
+
+    /**
+     * Do the move of all files in mercury to a final location
+     *
+     * @see BatchFilter#commitFiles(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.String)
+     */
+    public boolean commitFiles( HttpServletRequest request, HttpServletResponse response, Batch batch )
+        throws Exception
+    {
+        if ( batch == null )
+        {
+            return true; //nothing to do
+        }
+
+        boolean ok = true;
+        String stagedResource = URIUtil.addPaths( _stagingDirURI, batch.getBatchId() );
+        File batchDir = new File( new URI( stagedResource ) );
+        File[] files = batchDir.listFiles();
+        for ( int i = 0; files != null && i < files.length; i++ )
+        {
+            String name = files[i].getName();
+            File dest = new File( new URI( URIUtil.addPaths( _baseURI, name ) ) );
+            if ( !files[i].renameTo( dest ) )
+            {
+                ok = false;
+            }
+        }
+        if ( ok )
+        {
+            ok = batchDir.delete();
+        }
+        return ok;
+    }
+
+
+    /**
+     * Delete all files in the mercury from the staging area.
+     *
+     * @see BatchFilter#discardFiles(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.String)
+     */
+    public boolean discardFiles( HttpServletRequest request, HttpServletResponse response, Batch batch )
+        throws Exception
+    {
+        if ( batch == null )
+        {
+            return true; //nothing to do
+        }
+
+        String stagedResource = URIUtil.addPaths( _stagingDirURI, batch.getBatchId() );
+        File batchDir = new File( new URI( stagedResource ) );
+        boolean ok = true;
+        if ( !deleteFile( batchDir ) )
+        {
+            ok = false;
+        }
+        return ok;
+    }
+
+
+    /**
+     * Recursively descend file hierarchy and delete all files.
+     *
+     * @param f
+     * @return
+     */
+    private boolean deleteFile( File f )
+    {
+
+        if ( f == null )
+        {
+            return true;
+        }
+        if ( f.isFile() )
+        {
+            return f.delete();
+        }
+        else if ( f.isDirectory() )
+        {
+            File[] files = f.listFiles();
+            boolean ok = true;
+            for ( int i = 0; files != null && i < files.length; i++ )
+            {
+                if ( !deleteFile( files[i] ) )
+                {
+                    ok = false;
+                }
+            }
+
+            if ( !f.delete() )
+            {
+                ok = false;
+            }
+
+            return ok;
+        }
+        else
+        {
+            return true;
+        }
+    }
+}

Added: maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/validate/PomValidator.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/validate/PomValidator.java?rev=681887&view=auto
==============================================================================
--- maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/validate/PomValidator.java (added)
+++ maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/validate/PomValidator.java Fri Aug  1 15:55:40 2008
@@ -0,0 +1,56 @@
+/**
+ * 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.
+ */
+
+package org.apache.maven.mercury.spi.http.validate;
+
+import org.codehaus.plexus.util.FileUtils;
+
+import java.io.IOException;
+import java.util.List;
+
+public class PomValidator implements Validator
+{
+    public String getFileExtension()
+    {
+        return "pom";
+    }
+
+    public boolean validate(String stagedFile, List<String> errors)
+    {
+        try
+        {
+            String file = FileUtils.fileRead( stagedFile );
+
+            if ( file.contains( "<project>" ) )
+            {
+                return true;
+            }
+            else
+            {
+                errors.add( "file not valid" );
+            }
+        }
+        catch ( IOException ioe )
+        {
+            errors.add( "ioe" );
+        }
+
+        return false;
+    }
+}

Added: maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/validate/Validator.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/validate/Validator.java?rev=681887&view=auto
==============================================================================
--- maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/validate/Validator.java (added)
+++ maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/spi/http/validate/Validator.java Fri Aug  1 15:55:40 2008
@@ -0,0 +1,47 @@
+/**
+ * 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.
+ */
+
+package org.apache.maven.mercury.spi.http.validate;
+
+import java.util.List;
+
+/**
+ * Validator
+ * <p/>
+ * Interface for validating mercury content prior to completion
+ */
+public interface Validator
+{
+    /**
+     * The file extension the validator will process
+     *
+     * @return String file extension
+     */
+    public String getFileExtension();
+
+    /**
+     * validation for a given file target with errors being able to be logged in the list
+     *
+     * @param stagedFile
+     * @param errors
+     * @return true if target file is valid
+     */
+    public boolean validate( String stagedFile, List<String> errors );
+
+}

Added: maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/ChecksumCalculator.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/ChecksumCalculator.java?rev=681887&view=auto
==============================================================================
--- maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/ChecksumCalculator.java (added)
+++ maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/ChecksumCalculator.java Fri Aug  1 15:55:40 2008
@@ -0,0 +1,43 @@
+/**
+ * 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.
+ */
+
+package org.apache.maven.mercury.transport;
+
+
+
+public class ChecksumCalculator
+{
+    private static final byte[] __HEX_DIGITS = "0123456789abcdef".getBytes();
+
+
+    public static String encodeToAsciiHex( byte[] bytes )
+    {
+        int l = bytes.length;
+
+        byte[] raw = new byte[l * 2];
+
+        for ( int i = 0, j = 0; i < l; i++ )
+        {
+            raw[j++] = __HEX_DIGITS[( 0xF0 & bytes[i] ) >>> 4];
+            raw[j++] = __HEX_DIGITS[0x0F & bytes[i]];
+        }
+
+        return new String( raw );
+    }
+}

Added: maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/SHA1Verifier.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/SHA1Verifier.java?rev=681887&view=auto
==============================================================================
--- maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/SHA1Verifier.java (added)
+++ maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/SHA1Verifier.java Fri Aug  1 15:55:40 2008
@@ -0,0 +1,113 @@
+/**
+ * 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.
+ */
+
+package org.apache.maven.mercury.transport;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import org.apache.maven.mercury.transport.api.Verifier;
+
+/**
+ * SHA1Verifier
+ *
+ *
+ */
+public class SHA1Verifier implements Verifier
+{
+    public static final String digestAlgorithm = "SHA-1";
+    private MessageDigest digest;
+    private byte[] digestBytes;
+    private boolean isLenient;
+    private boolean isSufficient;
+    
+    
+    public SHA1Verifier (boolean isLenient, boolean isSufficient)
+    {
+        this.isLenient = isLenient;
+        this.isSufficient = isSufficient;
+        try
+        {
+            digest = MessageDigest.getInstance( digestAlgorithm );
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            //TODO
+        }
+    }
+
+    public String getExtension()
+    {
+        return ".sha1";
+    }
+
+    public byte[] getSignatureBytes ()
+    {
+        if (digestBytes == null)
+            digestBytes = digest.digest();
+        return digestBytes;
+    }
+    
+    public String getSignature()
+    {
+        return ChecksumCalculator.encodeToAsciiHex( getSignatureBytes() );
+    }
+    
+    public void setLenient (boolean lenient)
+    {
+        isLenient = lenient;
+    }
+
+    public boolean isLenient()
+    {
+        return isLenient;
+    }
+
+    public void setSufficient (boolean sufficient)
+    {
+        isSufficient = sufficient;
+    }
+    public boolean isSufficient()
+    {
+        return isSufficient;
+    }
+
+    public boolean verifySignature(String sig)
+    {
+        String calculatedSignature = getSignature();
+        if (calculatedSignature == null && sig == null)
+            return true;
+        if ((calculatedSignature != null) && calculatedSignature.equals(sig))
+            return true;
+        return false;
+    }
+
+    public void byteReady(int b)
+    {
+        if (digest != null)
+            digest.update((byte)b);
+    }
+
+    public void bytesReady(byte[] b, int off, int len)
+    {
+        if (digest != null)
+            digest.update(b, off, len);
+    }
+
+}

Added: maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/SHA1VerifierFactory.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/SHA1VerifierFactory.java?rev=681887&view=auto
==============================================================================
--- maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/SHA1VerifierFactory.java (added)
+++ maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/SHA1VerifierFactory.java Fri Aug  1 15:55:40 2008
@@ -0,0 +1,40 @@
+/**
+ * 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.
+ */
+
+package org.apache.maven.mercury.transport;
+
+import org.apache.maven.mercury.transport.api.StreamObserver;
+import org.apache.maven.mercury.transport.api.StreamObserverFactory;
+
+public class SHA1VerifierFactory implements StreamObserverFactory
+{
+    private boolean isLenient;
+    private boolean isSufficient;
+
+    public SHA1VerifierFactory (boolean isLenient, boolean isSufficient)
+    {
+        this.isLenient = isLenient;
+        this.isSufficient = isSufficient;
+    }
+    public StreamObserver newInstance()
+    {
+       return new SHA1Verifier(this.isLenient, this.isSufficient);
+    }
+
+}

Added: maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/http/HttpReaderTransport.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/http/HttpReaderTransport.java?rev=681887&view=auto
==============================================================================
--- maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/http/HttpReaderTransport.java (added)
+++ maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/http/HttpReaderTransport.java Fri Aug  1 15:55:40 2008
@@ -0,0 +1,70 @@
+package org.apache.maven.mercury.transport.http;
+
+import org.apache.maven.mercury.spi.http.client.HttpClientException;
+import org.apache.maven.mercury.spi.http.client.retrieve.DefaultRetrievalRequest;
+import org.apache.maven.mercury.spi.http.client.retrieve.DefaultRetriever;
+import org.apache.maven.mercury.spi.http.client.retrieve.RetrievalRequest;
+import org.apache.maven.mercury.spi.http.client.retrieve.RetrievalResponse;
+import org.apache.maven.mercury.transport.api.AbstractTransport;
+import org.apache.maven.mercury.transport.api.Binding;
+import org.apache.maven.mercury.transport.api.ReaderTransport;
+import org.apache.maven.mercury.transport.api.TransportException;
+import org.apache.maven.mercury.transport.api.TransportTransaction;
+import org.codehaus.plexus.i18n.DefaultLanguage;
+import org.codehaus.plexus.i18n.Language;
+
+/**
+ * Jetty client adaptor to ReaderTransport APIs
+ *
+ * @author Oleg Gusakov
+ * @version $Id$
+ *
+ */
+public class HttpReaderTransport
+extends AbstractTransport
+implements ReaderTransport
+{
+  private static final Language _lang = new DefaultLanguage( HttpReaderTransport.class );
+  private static final org.slf4j.Logger _log = org.slf4j.LoggerFactory.getLogger( HttpReaderTransport.class ); 
+
+  DefaultRetriever _httpReader;
+  
+  /**
+   * 
+   */
+  public HttpReaderTransport()
+  throws TransportException
+  {
+    try
+    {
+      _httpReader = new DefaultRetriever();
+    }
+    catch( HttpClientException e )
+    {
+      throw new TransportException(e);
+    }
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.maven.mercury.transport.api.ReaderTransport#read(org.apache.maven.mercury.transport.api.TransportTransaction)
+   */
+  public TransportTransaction read( TransportTransaction trx )
+  throws TransportException
+  {
+    if( trx == null )
+    {
+      _log.error( _lang.getMessage( "empty.transaction", trx == null ? "null" : trx.toString() ) );
+       return trx;
+    }
+
+    DefaultRetrievalRequest request = new DefaultRetrievalRequest();
+    
+    for( Binding b : trx.getBindings() )
+      request.addBinding( b );
+    
+    RetrievalResponse response = _httpReader.retrieve( request );
+
+    return trx;
+  }
+
+}

Added: maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/http/Messages.properties
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/http/Messages.properties?rev=681887&view=auto
==============================================================================
--- maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/http/Messages.properties (added)
+++ maven/sandbox/trunk/mercury/mercury-transport/mercury-transport-http/src/main/java/org/apache/maven/mercury/transport/http/Messages.properties Fri Aug  1 15:55:40 2008
@@ -0,0 +1 @@
+empty.transaction=cannot serve empty transaction {0}, returning it back
\ No newline at end of file