]> source.dussan.org Git - archiva.git/commitdiff
[MRM-159] should not respond with a 404 if proxying a file results in a remote error
authorBrett Porter <brett@apache.org>
Thu, 3 Apr 2008 07:58:24 +0000 (07:58 +0000)
committerBrett Porter <brett@apache.org>
Thu, 3 Apr 2008 07:58:24 +0000 (07:58 +0000)
git-svn-id: https://svn.apache.org/repos/asf/archiva/branches@644205 13f79535-47bb-0310-9956-ffa450edef68

24 files changed:
archiva-1.0.x/archiva-base/archiva-configuration/src/main/java/org/apache/maven/archiva/configuration/DefaultArchivaConfiguration.java
archiva-1.0.x/archiva-base/archiva-configuration/src/main/mdo/configuration.mdo
archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/CachedFailuresPolicy.java
archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/ChecksumPolicy.java
archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/DownloadErrorPolicy.java [new file with mode: 0644]
archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/DownloadPolicy.java
archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/Policy.java [new file with mode: 0644]
archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/PostDownloadPolicy.java
archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/PreDownloadPolicy.java
archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/PropagateErrorsDownloadPolicy.java [new file with mode: 0644]
archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/PropagateErrorsOnUpdateDownloadPolicy.java [new file with mode: 0644]
archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/ProxyDownloadException.java [new file with mode: 0644]
archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/ReleasesPolicy.java
archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/SnapshotsPolicy.java
archiva-1.0.x/archiva-base/archiva-proxy/src/main/java/org/apache/maven/archiva/proxy/DefaultRepositoryProxyConnectors.java
archiva-1.0.x/archiva-base/archiva-proxy/src/main/java/org/apache/maven/archiva/proxy/RepositoryProxyConnectors.java
archiva-1.0.x/archiva-base/archiva-proxy/src/test/java/org/apache/maven/archiva/proxy/AbstractProxyTestCase.java
archiva-1.0.x/archiva-base/archiva-proxy/src/test/java/org/apache/maven/archiva/proxy/ErrorHandlingTest.java [new file with mode: 0644]
archiva-1.0.x/archiva-base/archiva-proxy/src/test/resources/org/apache/maven/archiva/proxy/ErrorHandlingTest.xml [new file with mode: 0644]
archiva-1.0.x/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/admin/connectors/proxy/AbstractProxyConnectorFormAction.java
archiva-1.0.x/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/repository/ProxiedDavServer.java
archiva-1.0.x/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/admin/include/proxyConnectorForm.jspf
archiva-1.0.x/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/action/admin/connectors/proxy/AddProxyConnectorActionTest.java
archiva-1.0.x/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/action/admin/connectors/proxy/EditProxyConnectorActionTest.java

index 6fd182987005a4ae6c53dec29bef4740899f79ea..d83f44042bd604ba28ca98070888e0d2eaf6c236 100644 (file)
@@ -29,7 +29,7 @@ import org.apache.maven.archiva.configuration.io.registry.ConfigurationRegistryW
 import org.apache.maven.archiva.policies.AbstractUpdatePolicy;
 import org.apache.maven.archiva.policies.CachedFailuresPolicy;
 import org.apache.maven.archiva.policies.ChecksumPolicy;
-import org.apache.maven.archiva.policies.DownloadPolicy;
+import org.apache.maven.archiva.policies.Policy;
 import org.apache.maven.archiva.policies.PostDownloadPolicy;
 import org.apache.maven.archiva.policies.PreDownloadPolicy;
 import org.codehaus.plexus.evaluator.DefaultExpressionEvaluator;
@@ -276,7 +276,7 @@ public class DefaultArchivaConfiguration
                     // Validate existance of policy key.
                     if ( policyExists( policyId ) )
                     {
-                        DownloadPolicy policy = findPolicy( policyId );
+                        Policy policy = findPolicy( policyId );
                         // Does option exist?
                         if ( !policy.getOptions().contains( setting ) )
                         {
@@ -319,7 +319,7 @@ public class DefaultArchivaConfiguration
         return config;
     }
 
-    private DownloadPolicy findPolicy( String policyId )
+    private Policy findPolicy( String policyId )
     {
         if ( MapUtils.isEmpty( prePolicies ) )
         {
@@ -333,7 +333,7 @@ public class DefaultArchivaConfiguration
             return null;
         }
 
-        DownloadPolicy policy;
+        Policy policy;
 
         policy = prePolicies.get( policyId );
         if ( policy != null )
index 611adff86daf7751240ff282e0d88058ac7a1c47..5d3f1b87faab7df6e8d8af724612b11688cf3172 100644 (file)
      */
     public static final int UNORDERED = 0;
 
+    /**
+     * The policy key {@link #getPolicies()} for error handling.
+     * See {@link org.apache.maven.archiva.policies.DownloadErrorPolicy}
+     * for details on potential values to this policy key.
+     */
+    public static final String POLICY_PROPAGATE_ERRORS = "propagate-errors";
+
+    /**
+     * The policy key {@link #getPolicies()} for error handling when an artifact is present.
+     * See {@link org.apache.maven.archiva.policies.DownloadErrorPolicy}
+     * for details on potential values to this policy key.
+     */
+    public static final String POLICY_PROPAGATE_ERRORS_ON_UPDATE = "propagate-errors-on-update";
+
     /**
      * The policy key {@link #getPolicies()} for snapshot handling.
      * See {@link org.apache.maven.archiva.policies.SnapshotsPolicy}
index c7d44258c09a616bcf6f7c23c2d50276512191d2..28f2d3e6b40f2c473766f5a9ef4eaae7c2bf6f78 100644 (file)
@@ -105,6 +105,11 @@ public class CachedFailuresPolicy
         return "cache-failures";
     }
 
+    public String getName()
+    {
+        return "Cache failures";
+    }
+
     public List<String> getOptions()
     {
         return options;
index c86bbaaeca57ccaff7e8c720301684557d047722..2c96e83176588c83626abf1b4dcf51904050d7a2 100644 (file)
@@ -155,6 +155,11 @@ public class ChecksumPolicy
         return "checksum";
     }
 
+    public String getName()
+    {
+        return "Checksum";
+    }
+
     public List<String> getOptions()
     {
         return options;
diff --git a/archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/DownloadErrorPolicy.java b/archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/DownloadErrorPolicy.java
new file mode 100644 (file)
index 0000000..b5c209c
--- /dev/null
@@ -0,0 +1,50 @@
+package org.apache.maven.archiva.policies;
+
+/*
+ * 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.util.Map;
+import java.util.Properties;
+
+/**
+ * Policy to apply after the download has completed, but before the
+ * resource is made available to the calling client.
+ *
+ * @author <a href="mailto:brett@apache.org">Brett Porter</a>
+ * @version $Id$
+ */
+public interface DownloadErrorPolicy
+    extends Policy
+{
+    /**
+     * Apply the download error policy.
+     *
+     * @param policySetting      the policy setting.
+     * @param request            the list of request properties that the policy might use.
+     * @param localFile
+     * @param exception          the exception that triggered the error
+     * @param previousExceptions any previously triggered exceptions
+     * @return whether to process the exception or not
+     * @throws PolicyConfigurationException if the policy is improperly configured
+     */
+    public boolean applyPolicy( String policySetting, Properties request, File localFile, Exception exception,
+                                Map<String, Exception> previousExceptions )
+        throws PolicyConfigurationException;
+}
\ No newline at end of file
index 6d506308da67f544438ea5f6139e2aabcae98f3e..68036d9e8c7c96c5cfa81318bdc857caded7e66a 100644 (file)
@@ -20,7 +20,6 @@ package org.apache.maven.archiva.policies;
  */
 
 import java.io.File;
-import java.util.List;
 import java.util.Properties;
 
 /**
@@ -30,27 +29,8 @@ import java.util.Properties;
  * @version $Id$
  */
 public interface DownloadPolicy
+    extends Policy
 {
-    /**
-     * Get the list of options for this policy.
-     * 
-     * @return the list of options for this policy.
-     */
-    public List<String> getOptions();
-
-    /**
-     * Get the default option for this policy.
-     * 
-     * @return the default policy for this policy.
-     */
-    public String getDefaultOption();
-
-    /**
-     * Get the id for this policy.
-     * 
-     * @return the id for this policy.
-     */
-    public String getId();
 
     /**
      * Apply the download policy.
diff --git a/archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/Policy.java b/archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/Policy.java
new file mode 100644 (file)
index 0000000..f7add1b
--- /dev/null
@@ -0,0 +1,55 @@
+package org.apache.maven.archiva.policies;
+
+/*
+ * 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.util.List;
+
+public interface Policy
+{
+    /**
+     * Get the list of options for this policy.
+     *
+     * @return the list of options for this policy.
+     */
+    List<String> getOptions();
+
+    /**
+     * Get the default option for this policy.
+     *
+     * @return the default policy for this policy.
+     */
+    String getDefaultOption();
+
+    /**
+     * Get the id for this policy.
+     *
+     * @return the id for this policy.
+     */
+    String getId();
+
+    /**
+     * Get the display name for this policy.
+     *
+     * @todo i18n
+     *
+     * @return the name for this policy
+     */
+    String getName();
+}
index d1f6d216e652a42d9ffef0e7a11f0b2a9a50a1be..12bb6b41670e26248485b8f3b6539cadf2ee56ce 100644 (file)
@@ -19,9 +19,6 @@ package org.apache.maven.archiva.policies;
  * under the License.
  */
 
-import java.io.File;
-import java.util.Properties;
-
 /**
  * Policy to apply after the download has completed, but before the
  * resource is made available to the calling client. 
@@ -32,18 +29,4 @@ import java.util.Properties;
 public interface PostDownloadPolicy
     extends DownloadPolicy
 {
-    /**
-     * Apply the download policy.
-     * 
-     * A true result allows the download to succeed.  false indicates that the 
-     * download is a failure.
-     * 
-     * @param policySetting the policy setting.
-     * @param request the list of request properties that the policy might use.
-     * @param localFile the local file that this policy affects
-     * 
-     * @throws PolicyViolationException if the policy has been violated.
-     */
-    public void applyPolicy( String policySetting, Properties request, File localFile )
-        throws PolicyViolationException, PolicyConfigurationException;
 }
index f88eb938f065ef811a72b7f748608d148da2cb3d..1faf52025b223891ad8514677bad7573d697352f 100644 (file)
@@ -20,9 +20,6 @@ package org.apache.maven.archiva.policies;
  */
 
 
-import java.io.File;
-import java.util.Properties;
-
 /**
  * Policy to apply before the download is attempted.
  *
@@ -31,18 +28,4 @@ import java.util.Properties;
  */
 public interface PreDownloadPolicy extends DownloadPolicy
 {
-    /**
-     * Apply the download policy.
-     * 
-     * A true result lets the download occur.  A false result prevents the download
-     * from occuring. 
-     * 
-     * @param policySetting the policy setting.
-     * @param request the list of request properties that the policy might use.
-     * @param localFile the local file that this policy affects
-     * 
-     * @throws PolicyViolationException if the policy has been violated.
-     */
-    public void applyPolicy( String policySetting, Properties request, File localFile )
-        throws PolicyViolationException, PolicyConfigurationException;
 }
diff --git a/archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/PropagateErrorsDownloadPolicy.java b/archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/PropagateErrorsDownloadPolicy.java
new file mode 100644 (file)
index 0000000..cca1c67
--- /dev/null
@@ -0,0 +1,118 @@
+package org.apache.maven.archiva.policies;
+
+/*
+ * 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.commons.lang.StringUtils;
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * PropagateErrorsPolicy - a policy applied on error to determine how to treat the error.
+ *
+ * @plexus.component role="org.apache.maven.archiva.policies.DownloadErrorPolicy"
+ * role-hint="propagate-errors"
+ */
+public class PropagateErrorsDownloadPolicy
+    extends AbstractLogEnabled
+    implements DownloadErrorPolicy
+{
+    /**
+     * Signifies any error should stop searching for other proxies.
+     */
+    public static final String STOP = "stop";
+
+    /**
+     * Propagate errors at the end after all are gathered, if there was no successful download from other proxies.
+     */
+    public static final String QUEUE = "queue error";
+
+    /**
+     * Ignore errors and treat as if it were not found.
+     */
+    public static final String IGNORE = "ignore";
+
+    private List<String> options = new ArrayList<String>();
+
+    public PropagateErrorsDownloadPolicy()
+    {
+        options.add( STOP );
+        options.add( QUEUE );
+        options.add( IGNORE );
+    }
+
+    public boolean applyPolicy( String policySetting, Properties request, File localFile, Exception exception,
+                                Map<String, Exception> previousExceptions )
+        throws PolicyConfigurationException
+    {
+        if ( !options.contains( policySetting ) )
+        {
+            // Not a valid code.
+            throw new PolicyConfigurationException( "Unknown error policy setting [" + policySetting +
+                "], valid settings are [" + StringUtils.join( options.iterator(), "," ) + "]" );
+        }
+
+        if ( IGNORE.equals( policySetting ) )
+        {
+            // Ignore.
+            getLogger().debug( "Error policy set to IGNORE." );
+            return false;
+        }
+
+        String repositoryId = request.getProperty( "remoteRepositoryId" );
+        if ( STOP.equals( policySetting ) )
+        {
+            return true;
+        }
+
+        if ( QUEUE.equals( policySetting ) )
+        {
+            previousExceptions.put( repositoryId, exception );
+            return true;
+        }
+
+        throw new PolicyConfigurationException(
+            "Unable to process checksum policy of [" + policySetting + "], please file a bug report." );
+    }
+
+    public String getDefaultOption()
+    {
+        return QUEUE;
+    }
+
+    public String getId()
+    {
+        return "propagate-errors";
+    }
+
+    public String getName()
+    {
+        return "On remote error";
+    }
+
+    public List<String> getOptions()
+    {
+        return options;
+    }
+}
\ No newline at end of file
diff --git a/archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/PropagateErrorsOnUpdateDownloadPolicy.java b/archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/PropagateErrorsOnUpdateDownloadPolicy.java
new file mode 100644 (file)
index 0000000..d2b471a
--- /dev/null
@@ -0,0 +1,105 @@
+package org.apache.maven.archiva.policies;
+
+/*
+ * 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.commons.lang.StringUtils;
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * PropagateErrorsPolicy - a policy applied on error to determine how to treat the error.
+ *
+ * @plexus.component role="org.apache.maven.archiva.policies.DownloadErrorPolicy"
+ *                   role-hint="propagate-errors-on-update"
+ */
+public class PropagateErrorsOnUpdateDownloadPolicy
+    extends AbstractLogEnabled
+    implements DownloadErrorPolicy
+{
+    /**
+     * Signifies any error should cause a failure whether the artifact is already present or not.
+     */
+    public static final String ALWAYS = "always";
+
+    /**
+     * Signifies any error should cause a failure only if the artifact is not already present.
+     */
+    public static final String NOT_PRESENT = "artifact not already present";
+
+    private List<String> options = new ArrayList<String>();
+
+    public PropagateErrorsOnUpdateDownloadPolicy()
+    {
+        options.add( ALWAYS );
+        options.add( NOT_PRESENT );
+    }
+
+    public boolean applyPolicy( String policySetting, Properties request, File localFile, Exception exception,
+                             Map<String,Exception> previousExceptions )
+        throws PolicyConfigurationException
+    {
+        if ( !options.contains( policySetting ) )
+        {
+            // Not a valid code.
+            throw new PolicyConfigurationException( "Unknown error policy setting [" + policySetting
+                + "], valid settings are [" + StringUtils.join( options.iterator(), "," ) + "]" );
+        }
+
+        if ( ALWAYS.equals( policySetting ) )
+        {
+            // throw ther exception regardless
+            return true;
+        }
+
+        if ( NOT_PRESENT.equals( policySetting ) )
+        {
+            // cancel the exception if the file exists
+            return !localFile.exists();
+        }
+
+        throw new PolicyConfigurationException( "Unable to process checksum policy of [" + policySetting
+            + "], please file a bug report." );
+    }
+
+    public String getDefaultOption()
+    {
+        return NOT_PRESENT;
+    }
+
+    public String getId()
+    {
+        return "propagate-errors-on-update";
+    }
+
+    public String getName()
+    {
+        return "Return error when";
+    }
+
+    public List<String> getOptions()
+    {
+        return options;
+    }
+}
\ No newline at end of file
diff --git a/archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/ProxyDownloadException.java b/archiva-1.0.x/archiva-base/archiva-policies/src/main/java/org/apache/maven/archiva/policies/ProxyDownloadException.java
new file mode 100644 (file)
index 0000000..9d328c6
--- /dev/null
@@ -0,0 +1,66 @@
+package org.apache.maven.archiva.policies;
+
+/*
+ * 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.archiva.common.ArchivaException;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * One or more exceptions occurred downloading from a remote repository during the proxy phase.
+ */
+public class ProxyDownloadException
+    extends ArchivaException
+{
+    /**
+     * A list of failures keyed by repository ID.
+     */
+    private final Map<String, Exception> failures;
+
+    public ProxyDownloadException( String message, String repositoryId, Exception cause )
+    {
+        super( constructMessage( message, Collections.singletonMap( repositoryId, cause ) ), cause );
+
+        failures = Collections.singletonMap( repositoryId, cause );
+    }
+
+    public ProxyDownloadException( String message, Map<String, Exception> failures )
+    {
+        super( constructMessage( message, failures ) );
+
+        this.failures = failures;
+    }
+
+    private static String constructMessage( String message, Map<String, Exception> failures )
+    {
+        String msg = message + ":";
+        for ( Map.Entry<String, Exception> entry : failures.entrySet() )
+        {
+            msg += "\n\t" + entry.getKey() + ": " + entry.getValue().getMessage();
+        }
+        return msg;
+    }
+
+    public Map<String,Exception> getFailures()
+    {
+        return failures;
+    }
+}
index 8b53461a87242377793ca3d33772ddfdf56345fa..86ad7c3fca36d667809318b0e056c94576e9e8bc 100644 (file)
@@ -55,4 +55,9 @@ public class ReleasesPolicy
     {
         return "releases";
     }
+
+    public String getName()
+    {
+        return "Releases";
+    }
 }
index ffa903262f849b8487fcdc5f57660a7a4adacf23..ea10023c35ae5a66ab8ff2525d3b182dd3369d61 100644 (file)
@@ -55,4 +55,9 @@ public class SnapshotsPolicy
     {
         return "snapshots";
     }
+
+    public String getName()
+    {
+        return "Snapshots";
+    }
 }
index 5b3d1a515611b048811e4a7c748dc74a6f7b40ef..0252ac7c71469a67b50040d7ddf6b73df5f54e42 100644 (file)
@@ -31,11 +31,13 @@ import org.apache.maven.archiva.model.Keys;
 import org.apache.maven.archiva.model.ProjectReference;
 import org.apache.maven.archiva.model.RepositoryURL;
 import org.apache.maven.archiva.model.VersionedReference;
+import org.apache.maven.archiva.policies.DownloadErrorPolicy;
 import org.apache.maven.archiva.policies.DownloadPolicy;
 import org.apache.maven.archiva.policies.PolicyConfigurationException;
 import org.apache.maven.archiva.policies.PolicyViolationException;
 import org.apache.maven.archiva.policies.PostDownloadPolicy;
 import org.apache.maven.archiva.policies.PreDownloadPolicy;
+import org.apache.maven.archiva.policies.ProxyDownloadException;
 import org.apache.maven.archiva.policies.urlcache.UrlFailureCache;
 import org.apache.maven.archiva.repository.ContentNotFoundException;
 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
@@ -67,6 +69,7 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -77,6 +80,7 @@ import java.util.Properties;
  *
  * @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a>
  * @version $Id$
+ * @todo exception handling needs work - "not modified" is not really an exceptional case, and it has more layers than your average brown onion
  * @plexus.component role-hint="default"
  */
 public class DefaultRepositoryProxyConnectors
@@ -113,6 +117,11 @@ public class DefaultRepositoryProxyConnectors
      */
     private Map<String, PostDownloadPolicy> postDownloadPolicies;
 
+    /**
+     * @plexus.requirement role="org.apache.maven.archiva.policies.DownloadErrorPolicy"
+     */
+    private Map<String, DownloadErrorPolicy> downloadErrorPolicies;
+
     /**
      * @plexus.requirement role-hint="default"
      */
@@ -134,26 +143,31 @@ public class DefaultRepositoryProxyConnectors
      * @param artifact   the artifact reference to fetch.
      * @return the local file in the managed repository that was fetched, or null if the artifact was not (or
      *         could not be) fetched.
-     * @throws ProxyException if there was a problem fetching the artifact.
+     * @throws PolicyViolationException if there was a problem fetching the artifact.
      */
     public File fetchFromProxies( ManagedRepositoryContent repository, ArtifactReference artifact )
+        throws ProxyDownloadException
     {
         File localFile = toLocalFile( repository, artifact );
 
         Properties requestProperties = new Properties();
         requestProperties.setProperty( "filetype", "artifact" );
         requestProperties.setProperty( "version", artifact.getVersion() );
+        requestProperties.setProperty( "managedRepositoryId", repository.getId() );
 
         List<ProxyConnector> connectors = getProxyConnectors( repository );
+        Map<String, Exception> previousExceptions = new LinkedHashMap<String, Exception>();
         for ( ProxyConnector connector : connectors )
         {
             RemoteRepositoryContent targetRepository = connector.getTargetRepository();
+            requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
+
             String targetPath = targetRepository.toPath( artifact );
 
             try
             {
-                File downloadedFile = transferFile( connector, targetRepository, targetPath, localFile,
-                                                    requestProperties );
+                File downloadedFile =
+                    transferFile( connector, targetRepository, targetPath, localFile, requestProperties );
 
                 if ( fileExists( downloadedFile ) )
                 {
@@ -163,22 +177,27 @@ public class DefaultRepositoryProxyConnectors
             }
             catch ( NotFoundException e )
             {
-                getLogger().debug( "Artifact " + Keys.toKey( artifact ) + " not found on repository \""
-                                       + targetRepository.getRepository().getId() + "\"." );
+                getLogger().debug( "Artifact " + Keys.toKey( artifact ) + " not found on repository \"" +
+                    targetRepository.getRepository().getId() + "\"." );
             }
             catch ( NotModifiedException e )
             {
-                getLogger().debug( "Artifact " + Keys.toKey( artifact ) + " not updated on repository \""
-                                       + targetRepository.getRepository().getId() + "\"." );
+                getLogger().debug( "Artifact " + Keys.toKey( artifact ) + " not updated on repository \"" +
+                    targetRepository.getRepository().getId() + "\"." );
             }
             catch ( ProxyException e )
             {
-                getLogger().warn( "Transfer error from repository \"" + targetRepository.getRepository().getId() +
-                    "\" for artifact " + Keys.toKey( artifact ) + ", continuing to next repository. Error message: " +
-                    e.getMessage() );
-                getLogger().debug( "Full stack trace", e );
+                validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact,
+                                  targetRepository, localFile, e, previousExceptions );
             }
         }
+
+        if ( !previousExceptions.isEmpty() )
+        {
+            throw new ProxyDownloadException( "Failures occurred downloading from some remote repositories",
+                                              previousExceptions );
+        }
+
         getLogger().debug( "Exhausted all target repositories, artifact " + Keys.toKey( artifact ) + " not found." );
 
         return null;
@@ -218,15 +237,13 @@ public class DefaultRepositoryProxyConnectors
             }
             catch ( NotFoundException e )
             {
-                getLogger().debug( "Versioned Metadata " + Keys.toKey( metadata )
-                                       + " not found on remote repository \""
-                                       + targetRepository.getRepository().getId() + "\"." );
+                getLogger().debug( "Versioned Metadata " + Keys.toKey( metadata ) +
+                    " not found on remote repository \"" + targetRepository.getRepository().getId() + "\"." );
             }
             catch ( NotModifiedException e )
             {
-                getLogger().debug( "Versioned Metadata " + Keys.toKey( metadata )
-                                       + " not updated on remote repository \""
-                                       + targetRepository.getRepository().getId() + "\"." );
+                getLogger().debug( "Versioned Metadata " + Keys.toKey( metadata ) +
+                    " not updated on remote repository \"" + targetRepository.getRepository().getId() + "\"." );
             }
             catch ( ProxyException e )
             {
@@ -336,14 +353,13 @@ public class DefaultRepositoryProxyConnectors
             }
             catch ( NotFoundException e )
             {
-                getLogger().debug( "Project Metadata " + Keys.toKey( metadata ) + " not found on remote repository \""
-                                       + targetRepository.getRepository().getId() + "\"." );
+                getLogger().debug( "Project Metadata " + Keys.toKey( metadata ) + " not found on remote repository \"" +
+                    targetRepository.getRepository().getId() + "\"." );
             }
             catch ( NotModifiedException e )
             {
-                getLogger().debug( "Project Metadata " + Keys.toKey( metadata )
-                                       + " not updated on remote repository \""
-                                       + targetRepository.getRepository().getId() + "\"." );
+                getLogger().debug( "Project Metadata " + Keys.toKey( metadata ) +
+                    " not updated on remote repository \"" + targetRepository.getRepository().getId() + "\"." );
             }
             catch ( ProxyException e )
             {
@@ -469,10 +485,10 @@ public class DefaultRepositoryProxyConnectors
      * @param localFile         the local file to place the downloaded resource into
      * @param requestProperties the request properties to utilize for policy handling.
      * @return the local file that was downloaded, or null if not downloaded.
-     * @throws NotFoundException if the file was not found on the remote repository.
+     * @throws NotFoundException    if the file was not found on the remote repository.
      * @throws NotModifiedException if the localFile was present, and the resource was present on remote repository,
      *                              but the remote resource is not newer than the local File.
-     * @throws ProxyException if transfer was unsuccessful.
+     * @throws ProxyException       if transfer was unsuccessful.
      */
     private File transferFile( ProxyConnector connector, RemoteRepositoryContent remoteRepository, String remotePath,
                                File localFile, Properties requestProperties )
@@ -524,10 +540,10 @@ public class DefaultRepositoryProxyConnectors
             getLogger().info( emsg );
             return null;
         }
-                       
+
         Wagon wagon = null;
         try
-        {      
+        {
             RepositoryURL repoUrl = remoteRepository.getURL();
             String protocol = repoUrl.getProtocol();
             wagon = (Wagon) wagons.get( protocol );
@@ -689,13 +705,13 @@ public class DefaultRepositoryProxyConnectors
             }
             else
             {
-                getLogger().debug( "Retrieving " + remotePath + " from " + remoteRepository.getRepository().getName()
-                                       + " if updated" );
+                getLogger().debug( "Retrieving " + remotePath + " from " + remoteRepository.getRepository().getName() +
+                    " if updated" );
                 success = wagon.getIfNewer( remotePath, temp, localFile.lastModified() );
                 if ( !success )
                 {
-                    throw new NotModifiedException( "Not downloaded, as local file is newer than remote side: "
-                                                    + localFile.getAbsolutePath() );
+                    throw new NotModifiedException(
+                        "Not downloaded, as local file is newer than remote side: " + localFile.getAbsolutePath() );
                 }
 
                 if ( temp.exists() )
@@ -709,13 +725,15 @@ public class DefaultRepositoryProxyConnectors
         }
         catch ( ResourceDoesNotExistException e )
         {
-            throw new NotFoundException( "Resource [" + remoteRepository.getURL() + "/" + remotePath
-                + "] does not exist: " + e.getMessage(), e );
+            throw new NotFoundException(
+                "Resource [" + remoteRepository.getURL() + "/" + remotePath + "] does not exist: " + e.getMessage(),
+                e );
         }
         catch ( WagonException e )
         {
-            throw new ProxyException( "Download failure on resource [" + remoteRepository.getURL() + "/" + remotePath + "]:"
-                                  + e.getMessage(), e );
+            throw new ProxyException(
+                "Download failure on resource [" + remoteRepository.getURL() + "/" + remotePath + "]:" + e.getMessage(),
+                e );
         }
         finally
         {
@@ -740,10 +758,10 @@ public class DefaultRepositoryProxyConnectors
     {
         for ( Entry<String, ? extends DownloadPolicy> entry : policies.entrySet() )
         {
-            String key = (String) entry.getKey();
+            String key = entry.getKey();
             DownloadPolicy policy = entry.getValue();
             String defaultSetting = policy.getDefaultOption();
-            String setting = StringUtils.defaultString( (String) settings.get( key ), defaultSetting );
+            String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
 
             getLogger().debug( "Applying [" + key + "] policy with [" + setting + "]" );
             try
@@ -757,6 +775,56 @@ public class DefaultRepositoryProxyConnectors
         }
     }
 
+    private void validatePolicies( Map<String, DownloadErrorPolicy> policies, Map<String, String> settings,
+                                   Properties request, ArtifactReference artifact, RemoteRepositoryContent content,
+                                   File localFile, ProxyException exception, Map<String, Exception> previousExceptions )
+        throws ProxyDownloadException
+    {
+        boolean process = true;
+        for ( Entry<String, ? extends DownloadErrorPolicy> entry : policies.entrySet() )
+        {
+            String key = entry.getKey();
+            DownloadErrorPolicy policy = entry.getValue();
+            String defaultSetting = policy.getDefaultOption();
+            String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
+
+            getLogger().debug( "Applying [" + key + "] policy with [" + setting + "]" );
+            try
+            {
+                // all policies must approve the exception, any can cancel
+                process = policy.applyPolicy( setting, request, localFile, exception, previousExceptions );
+                if ( !process )
+                {
+                    break;
+                }
+            }
+            catch ( PolicyConfigurationException e )
+            {
+                getLogger().error( e.getMessage(), e );
+            }
+        }
+
+        if ( process )
+        {
+            // if the exception was queued, don't throw it
+            if ( !previousExceptions.containsKey( content.getId() ) )
+            {
+                throw new ProxyDownloadException(
+                    "An error occurred in downloading from the remote repository, and the policy is to fail immediately",
+                    content.getId(), exception );
+            }
+        }
+        else
+        {
+            // if the exception was queued, but cancelled, remove it
+            previousExceptions.remove( content.getId() );
+        }
+
+        getLogger().warn( "Transfer error from repository \"" + content.getRepository().getId() + "\" for artifact " +
+            Keys.toKey( artifact ) + ", continuing to next repository. Error message: " + exception.getMessage() );
+        getLogger().debug( "Full stack trace", exception );
+    }
+
     /**
      * Used to move the temporary file to its real destination.  This is patterned from the way WagonManager handles
      * its downloaded files.
@@ -800,7 +868,8 @@ public class DefaultRepositoryProxyConnectors
      * @param remoteRepository the remote repository to connect to.
      * @return true if the connection was successful. false if not connected.
      */
-    private boolean connectToRepository( ProxyConnector connector, Wagon wagon, RemoteRepositoryContent remoteRepository )
+    private boolean connectToRepository( ProxyConnector connector, Wagon wagon,
+                                         RemoteRepositoryContent remoteRepository )
     {
         boolean connected = false;
 
@@ -818,8 +887,8 @@ public class DefaultRepositoryProxyConnectors
 
             if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
             {
-                getLogger().debug( "Using username " + username + " to connect to remote repository "
-                                       + remoteRepository.getURL() );
+                getLogger().debug(
+                    "Using username " + username + " to connect to remote repository " + remoteRepository.getURL() );
                 authInfo = new AuthenticationInfo();
                 authInfo.setUserName( username );
                 authInfo.setPassword( password );
@@ -829,7 +898,8 @@ public class DefaultRepositoryProxyConnectors
                 getLogger().debug( "No authentication for remote repository needed" );
             }
 
-            Repository wagonRepository = new Repository( remoteRepository.getId(), remoteRepository.getURL().toString() );
+            Repository wagonRepository =
+                new Repository( remoteRepository.getId(), remoteRepository.getURL().toString() );
             if ( networkProxy != null )
             {
                 wagon.connect( wagonRepository, authInfo, networkProxy );
@@ -843,15 +913,13 @@ public class DefaultRepositoryProxyConnectors
         catch ( ConnectionException e )
         {
             getLogger().warn(
-                              "Could not connect to " + remoteRepository.getRepository().getName() + ": "
-                                  + e.getMessage() );
+                "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
             connected = false;
         }
         catch ( AuthenticationException e )
         {
             getLogger().warn(
-                              "Could not connect to " + remoteRepository.getRepository().getName() + ": "
-                                  + e.getMessage() );
+                "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
             connected = false;
         }
 
@@ -903,10 +971,10 @@ public class DefaultRepositoryProxyConnectors
 
     public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
     {
-        if ( ConfigurationNames.isNetworkProxy( propertyName )
-            || ConfigurationNames.isManagedRepositories( propertyName )
-            || ConfigurationNames.isRemoteRepositories( propertyName )
-            || ConfigurationNames.isProxyConnector( propertyName ) )
+        if ( ConfigurationNames.isNetworkProxy( propertyName ) ||
+            ConfigurationNames.isManagedRepositories( propertyName ) ||
+            ConfigurationNames.isRemoteRepositories( propertyName ) ||
+            ConfigurationNames.isProxyConnector( propertyName ) )
         {
             initConnectorsAndNetworkProxies();
         }
index 6d832bfbbc268a004510d0644bcbae97acb2314c..c27c5b8ab01b1a3f943c2cea8f967c967b72a46c 100644 (file)
@@ -22,6 +22,7 @@ package org.apache.maven.archiva.proxy;
 import org.apache.maven.archiva.model.ArtifactReference;
 import org.apache.maven.archiva.model.ProjectReference;
 import org.apache.maven.archiva.model.VersionedReference;
+import org.apache.maven.archiva.policies.ProxyDownloadException;
 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
 
 import java.io.File;
@@ -45,11 +46,11 @@ public interface RepositoryProxyConnectors
      * @param repository the source repository to use. (must be a managed repository)
      * @param artifact the artifact to fetch.
      * @return true if the fetch operation succeeded in obtaining content, false if no content was obtained.
-     * @throws ProxyException if there was a problem fetching the content from the target repositories.
+     * @throws ProxyDownloadException if there was a problem fetching the content from the target repositories.
      */
     public File fetchFromProxies( ManagedRepositoryContent repository, ArtifactReference artifact )
-        throws ProxyException;
-    
+        throws ProxyDownloadException;
+
     /**
      * Performs the metadata fetch operation against the target repositories
      * of the provided source repository.
@@ -60,11 +61,9 @@ public interface RepositoryProxyConnectors
      * @param repository the source repository to use. (must be a managed repository)
      * @param metadata the metadata to fetch.
      * @return true if the fetch operation succeeded in obtaining content, false if no content was obtained.
-     * @throws ProxyException if there was a problem fetching the content from the target repositories.
      */
-    public File fetchFromProxies( ManagedRepositoryContent repository, VersionedReference metadata )
-        throws ProxyException;
-    
+    public File fetchFromProxies( ManagedRepositoryContent repository, VersionedReference metadata );
+
     /**
      * Performs the metadata fetch operation against the target repositories
      * of the provided source repository.
@@ -75,10 +74,8 @@ public interface RepositoryProxyConnectors
      * @param repository the source repository to use. (must be a managed repository)
      * @param metadata the metadata to fetch.
      * @return true if the fetch operation succeeded in obtaining content, false if no content was obtained.
-     * @throws ProxyException if there was a problem fetching the content from the target repositories.
      */
-    public File fetchFromProxies( ManagedRepositoryContent repository, ProjectReference metadata )
-        throws ProxyException;
+    public File fetchFromProxies( ManagedRepositoryContent repository, ProjectReference metadata );
 
     /**
      * Get the List of {@link ProxyConnector} objects of the source repository.
index 8527ade4f110b1f5323596e6b1b9ef6dd9347b6b..cf660af49e848b37890114770abe502e2a149514 100644 (file)
@@ -26,6 +26,8 @@ import org.apache.maven.archiva.configuration.ProxyConnectorConfiguration;
 import org.apache.maven.archiva.configuration.RemoteRepositoryConfiguration;
 import org.apache.maven.archiva.policies.CachedFailuresPolicy;
 import org.apache.maven.archiva.policies.ChecksumPolicy;
+import org.apache.maven.archiva.policies.PropagateErrorsDownloadPolicy;
+import org.apache.maven.archiva.policies.PropagateErrorsOnUpdateDownloadPolicy;
 import org.apache.maven.archiva.policies.ReleasesPolicy;
 import org.apache.maven.archiva.policies.SnapshotsPolicy;
 import org.apache.maven.archiva.policies.urlcache.UrlFailureCache;
@@ -302,8 +304,23 @@ public abstract class AbstractProxyTestCase
                        SnapshotsPolicy.ALWAYS, CachedFailuresPolicy.NO );
     }
 
-    protected void saveConnector( String sourceRepoId, String targetRepoId, String checksumPolicy,
-                                  String releasePolicy, String snapshotPolicy, String cacheFailuresPolicy )
+    protected void saveConnector( String sourceRepoId, String targetRepoId, String checksumPolicy, String releasePolicy,
+                                  String snapshotPolicy, String cacheFailuresPolicy )
+    {
+        saveConnector( sourceRepoId, targetRepoId, checksumPolicy, releasePolicy, snapshotPolicy, cacheFailuresPolicy,
+                       PropagateErrorsDownloadPolicy.QUEUE );
+    }
+
+    protected void saveConnector( String sourceRepoId, String targetRepoId, String checksumPolicy, String releasePolicy,
+                                  String snapshotPolicy, String cacheFailuresPolicy, String errorPolicy )
+    {
+        saveConnector( sourceRepoId, targetRepoId, checksumPolicy, releasePolicy, snapshotPolicy, cacheFailuresPolicy,
+                       errorPolicy, PropagateErrorsOnUpdateDownloadPolicy.NOT_PRESENT );
+    }
+
+    protected void saveConnector( String sourceRepoId, String targetRepoId, String checksumPolicy, String releasePolicy,
+                                  String snapshotPolicy, String cacheFailuresPolicy, String errorPolicy,
+                                  String errorOnUpdatePolicy )
     {
         ProxyConnectorConfiguration connectorConfig = new ProxyConnectorConfiguration();
         connectorConfig.setSourceRepoId( sourceRepoId );
@@ -312,6 +329,8 @@ public abstract class AbstractProxyTestCase
         connectorConfig.addPolicy( ProxyConnectorConfiguration.POLICY_RELEASES, releasePolicy );
         connectorConfig.addPolicy( ProxyConnectorConfiguration.POLICY_SNAPSHOTS, snapshotPolicy );
         connectorConfig.addPolicy( ProxyConnectorConfiguration.POLICY_CACHE_FAILURES, cacheFailuresPolicy );
+        connectorConfig.addPolicy( ProxyConnectorConfiguration.POLICY_PROPAGATE_ERRORS, errorPolicy );
+        connectorConfig.addPolicy( ProxyConnectorConfiguration.POLICY_PROPAGATE_ERRORS_ON_UPDATE, errorOnUpdatePolicy );
 
         int count = config.getConfiguration().getProxyConnectors().size();
         config.getConfiguration().addProxyConnector( connectorConfig );
@@ -325,6 +344,10 @@ public abstract class AbstractProxyTestCase
         config.triggerChange( prefix + ".policies.checksum", connectorConfig.getPolicy( "checksum", "" ) );
         config.triggerChange( prefix + ".policies.snapshots", connectorConfig.getPolicy( "snapshots", "" ) );
         config.triggerChange( prefix + ".policies.cache-failures", connectorConfig.getPolicy( "cache-failures", "" ) );
+        config.triggerChange( prefix + ".policies.propagate-errors",
+                              connectorConfig.getPolicy( "propagate-errors", "" ) );
+        config.triggerChange( prefix + ".policies.propagate-errors-on-update",
+                              connectorConfig.getPolicy( "propagate-errors-on-update", "" ) );
     }
 
     protected void saveManagedRepositoryConfig( String id, String name, String path, String layout )
diff --git a/archiva-1.0.x/archiva-base/archiva-proxy/src/test/java/org/apache/maven/archiva/proxy/ErrorHandlingTest.java b/archiva-1.0.x/archiva-base/archiva-proxy/src/test/java/org/apache/maven/archiva/proxy/ErrorHandlingTest.java
new file mode 100644 (file)
index 0000000..0e62f82
--- /dev/null
@@ -0,0 +1,627 @@
+package org.apache.maven.archiva.proxy;
+
+/*
+ * 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.archiva.policies.CachedFailuresPolicy;
+import org.apache.maven.archiva.policies.ChecksumPolicy;
+import org.apache.maven.archiva.policies.PropagateErrorsDownloadPolicy;
+import org.apache.maven.archiva.policies.PropagateErrorsOnUpdateDownloadPolicy;
+import org.apache.maven.archiva.policies.ProxyDownloadException;
+import org.apache.maven.archiva.policies.ReleasesPolicy;
+import org.apache.maven.archiva.policies.SnapshotsPolicy;
+import org.apache.maven.archiva.repository.layout.LayoutException;
+import org.apache.maven.wagon.ResourceDoesNotExistException;
+import org.apache.maven.wagon.TransferFailedException;
+import org.apache.maven.wagon.authorization.AuthorizationException;
+
+import java.io.File;
+
+/**
+ * ErrorHandlingTest
+ *
+ * @author Brett Porter
+ * @version $Id$
+ */
+public class ErrorHandlingTest
+    extends AbstractProxyTestCase
+{
+    private static final String PATH_IN_BOTH_REMOTES_NOT_LOCAL =
+        "org/apache/maven/test/get-in-both-proxies/1.0/get-in-both-proxies-1.0.jar";
+
+    private static final String PATH_IN_BOTH_REMOTES_AND_LOCAL =
+        "org/apache/maven/test/get-on-multiple-repos/1.0/get-on-multiple-repos-1.0.pom";
+
+    private static final String ID_MOCKED_PROXIED1 = "badproxied1";
+
+    private static final String NAME_MOCKED_PROXIED1 = "Bad Proxied 1";
+
+    private static final String ID_MOCKED_PROXIED2 = "badproxied2";
+
+    private static final String NAME_MOCKED_PROXIED2 = "Bad Proxied 2";
+
+    public void testPropagateErrorImmediatelyWithErrorThenSuccess()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.STOP );
+        saveConnector( ID_DEFAULT_MANAGED, ID_PROXIED2 );
+
+        simulateGetError( path, expectedFile, createTransferException() );
+
+        confirmSingleFailure( path, ID_MOCKED_PROXIED1 );
+    }
+
+    public void testPropagateErrorImmediatelyWithNotFoundThenError()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.STOP );
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.STOP );
+
+        simulateGetError( path, expectedFile, createResourceNotFoundException() );
+
+        simulateGetError( path, expectedFile, createTransferException() );
+
+        confirmSingleFailure( path, ID_MOCKED_PROXIED2 );
+    }
+
+    public void testPropagateErrorImmediatelyWithSuccessThenError()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        saveConnector( ID_DEFAULT_MANAGED, ID_PROXIED1 );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.STOP );
+
+        confirmSuccess( path, expectedFile, REPOPATH_PROXIED1 );
+    }
+
+    public void testPropagateErrorImmediatelyWithNotFoundThenSuccess()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.STOP );
+
+        saveConnector( ID_DEFAULT_MANAGED, ID_PROXIED2 );
+
+        simulateGetError( path, expectedFile, createResourceNotFoundException() );
+
+        confirmSuccess( path, expectedFile, REPOPATH_PROXIED2 );
+    }
+
+    public void testPropagateErrorAtEndWithErrorThenSuccess()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.STOP );
+
+        saveConnector( ID_DEFAULT_MANAGED, ID_PROXIED2 );
+
+        simulateGetError( path, expectedFile, createTransferException() );
+
+        confirmSingleFailure( path, ID_MOCKED_PROXIED1 );
+    }
+
+    public void testPropagateErrorAtEndWithSuccessThenError()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        saveConnector( ID_DEFAULT_MANAGED, ID_PROXIED1 );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.QUEUE );
+
+        confirmSuccess( path, expectedFile, REPOPATH_PROXIED1 );
+    }
+
+    public void testPropagateErrorAtEndWithNotFoundThenError()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.QUEUE );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.QUEUE );
+
+        simulateGetError( path, expectedFile, createResourceNotFoundException() );
+
+        simulateGetError( path, expectedFile, createTransferException() );
+
+        confirmSingleFailure( path, ID_MOCKED_PROXIED2 );
+    }
+
+    public void testPropagateErrorAtEndWithErrorThenNotFound()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.QUEUE );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.QUEUE );
+
+        simulateGetError( path, expectedFile, createTransferException() );
+
+        simulateGetError( path, expectedFile, createResourceNotFoundException() );
+
+        confirmSingleFailure( path, ID_MOCKED_PROXIED1 );
+    }
+
+    public void testPropagateErrorAtEndWithErrorThenError()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.QUEUE );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.QUEUE );
+
+        simulateGetError( path, expectedFile, createTransferException() );
+
+        simulateGetError( path, expectedFile, createTransferException() );
+
+        confirmFailures( path, new String[]{ID_MOCKED_PROXIED1, ID_MOCKED_PROXIED2} );
+    }
+
+    public void testPropagateErrorAtEndWithNotFoundThenSuccess()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.QUEUE );
+
+        saveConnector( ID_DEFAULT_MANAGED, ID_PROXIED2 );
+
+        simulateGetError( path, expectedFile, createResourceNotFoundException() );
+
+        confirmSuccess( path, expectedFile, REPOPATH_PROXIED2 );
+    }
+
+    public void testIgnoreErrorWithErrorThenSuccess()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.IGNORE );
+
+        saveConnector( ID_DEFAULT_MANAGED, ID_PROXIED2 );
+
+        simulateGetError( path, expectedFile, createTransferException() );
+
+        confirmSuccess( path, expectedFile, REPOPATH_PROXIED2 );
+    }
+
+    public void testIgnoreErrorWithSuccessThenError()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        saveConnector( ID_DEFAULT_MANAGED, ID_PROXIED1 );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.IGNORE );
+
+        confirmSuccess( path, expectedFile, REPOPATH_PROXIED1 );
+    }
+
+    public void testIgnoreErrorWithNotFoundThenError()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.IGNORE );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.IGNORE );
+
+        simulateGetError( path, expectedFile, createResourceNotFoundException() );
+
+        simulateGetError( path, expectedFile, createTransferException() );
+
+        confirmNotDownloadedNoError( path );
+    }
+
+    public void testIgnoreErrorWithErrorThenNotFound()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.IGNORE );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.IGNORE );
+
+        simulateGetError( path, expectedFile, createTransferException() );
+
+        simulateGetError( path, expectedFile, createResourceNotFoundException() );
+
+        confirmNotDownloadedNoError( path );
+    }
+
+    public void testIgnoreErrorWithErrorThenError()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.IGNORE );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.IGNORE );
+
+        simulateGetError( path, expectedFile, createTransferException() );
+
+        simulateGetError( path, expectedFile, createTransferException() );
+
+        confirmNotDownloadedNoError( path );
+    }
+
+    public void testPropagateOnUpdateAlwaysArtifactNotPresent()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.STOP,
+                                    PropagateErrorsOnUpdateDownloadPolicy.ALWAYS );
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.STOP,
+                                    PropagateErrorsOnUpdateDownloadPolicy.ALWAYS );
+
+        simulateGetError( path, expectedFile, createTransferException() );
+
+        confirmSingleFailure( path, ID_MOCKED_PROXIED1 );
+    }
+
+    public void testPropagateOnUpdateAlwaysArtifactPresent()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_AND_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFilePresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.STOP,
+                                    PropagateErrorsOnUpdateDownloadPolicy.ALWAYS );
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.STOP,
+                                    PropagateErrorsOnUpdateDownloadPolicy.ALWAYS );
+
+        simulateGetIfNewerError( path, expectedFile, createTransferException() );
+
+        confirmSingleFailure( path, ID_MOCKED_PROXIED1 );
+    }
+
+    public void testPropagateOnUpdateAlwaysQueueArtifactNotPresent()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.QUEUE,
+                                    PropagateErrorsOnUpdateDownloadPolicy.ALWAYS );
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.QUEUE,
+                                    PropagateErrorsOnUpdateDownloadPolicy.ALWAYS );
+
+        simulateGetError( path, expectedFile, createTransferException() );
+        simulateGetError( path, expectedFile, createTransferException() );
+
+        confirmFailures( path, new String[] { ID_MOCKED_PROXIED1, ID_MOCKED_PROXIED2 } );
+    }
+
+    public void testPropagateOnUpdateAlwaysQueueArtifactPresent()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_AND_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFilePresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.QUEUE,
+                                    PropagateErrorsOnUpdateDownloadPolicy.ALWAYS );
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.QUEUE,
+                                    PropagateErrorsOnUpdateDownloadPolicy.ALWAYS );
+
+        simulateGetIfNewerError( path, expectedFile, createTransferException() );
+        simulateGetIfNewerError( path, expectedFile, createTransferException() );
+
+        confirmFailures( path, new String[] { ID_MOCKED_PROXIED1, ID_MOCKED_PROXIED2 } );
+    }
+
+    public void testPropagateOnUpdateAlwaysIgnoreArtifactNotPresent()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.IGNORE,
+                                    PropagateErrorsOnUpdateDownloadPolicy.ALWAYS );
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.IGNORE,
+                                    PropagateErrorsOnUpdateDownloadPolicy.ALWAYS );
+
+        simulateGetError( path, expectedFile, createTransferException() );
+        simulateGetError( path, expectedFile, createTransferException() );
+
+        confirmNotDownloadedNoError( path );
+    }
+
+    public void testPropagateOnUpdateAlwaysIgnoreArtifactPresent()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_AND_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFilePresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.IGNORE,
+                                    PropagateErrorsOnUpdateDownloadPolicy.ALWAYS );
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.IGNORE,
+                                    PropagateErrorsOnUpdateDownloadPolicy.ALWAYS );
+
+        simulateGetIfNewerError( path, expectedFile, createTransferException() );
+        simulateGetIfNewerError( path, expectedFile, createTransferException() );
+
+        confirmNotDownloadedNoError( path );
+        assertTrue( expectedFile.exists() );
+    }
+
+    public void testPropagateOnUpdateNotPresentArtifactNotPresent()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.STOP,
+                                    PropagateErrorsOnUpdateDownloadPolicy.NOT_PRESENT );
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.STOP,
+                                    PropagateErrorsOnUpdateDownloadPolicy.NOT_PRESENT );
+
+        simulateGetError( path, expectedFile, createTransferException() );
+
+        confirmSingleFailure( path, ID_MOCKED_PROXIED1 );
+    }
+
+    public void testPropagateOnUpdateNotPresentArtifactPresent()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_AND_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFilePresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.STOP,
+                                    PropagateErrorsOnUpdateDownloadPolicy.NOT_PRESENT );
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.STOP,
+                                    PropagateErrorsOnUpdateDownloadPolicy.NOT_PRESENT );
+
+        simulateGetIfNewerError( path, expectedFile, createTransferException() );
+
+        confirmNotDownloadedNoError( path );
+        assertTrue( expectedFile.exists() );
+    }
+
+    public void testPropagateOnUpdateNotPresentQueueArtifactNotPresent()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.QUEUE,
+                                    PropagateErrorsOnUpdateDownloadPolicy.NOT_PRESENT );
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.QUEUE,
+                                    PropagateErrorsOnUpdateDownloadPolicy.NOT_PRESENT );
+
+        simulateGetError( path, expectedFile, createTransferException() );
+        simulateGetError( path, expectedFile, createTransferException() );
+
+        confirmFailures( path, new String[] { ID_MOCKED_PROXIED1, ID_MOCKED_PROXIED2 } );
+    }
+
+    public void testPropagateOnUpdateNotPresentQueueArtifactPresent()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_AND_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFilePresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.QUEUE,
+                                    PropagateErrorsOnUpdateDownloadPolicy.NOT_PRESENT );
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.QUEUE,
+                                    PropagateErrorsOnUpdateDownloadPolicy.NOT_PRESENT );
+
+        simulateGetIfNewerError( path, expectedFile, createTransferException() );
+        simulateGetIfNewerError( path, expectedFile, createTransferException() );
+
+        confirmNotDownloadedNoError( path );
+        assertTrue( expectedFile.exists() );
+    }
+
+    public void testPropagateOnUpdateNotPresentIgnoreArtifactNotPresent()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_NOT_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFileNotPresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.IGNORE,
+                                    PropagateErrorsOnUpdateDownloadPolicy.NOT_PRESENT );
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.IGNORE,
+                                    PropagateErrorsOnUpdateDownloadPolicy.NOT_PRESENT );
+
+        simulateGetError( path, expectedFile, createTransferException() );
+        simulateGetError( path, expectedFile, createTransferException() );
+
+        confirmNotDownloadedNoError( path );
+    }
+
+    public void testPropagateOnUpdateNotPresentIgnoreArtifactPresent()
+        throws Exception
+    {
+        String path = PATH_IN_BOTH_REMOTES_AND_LOCAL;
+        File expectedFile = setupRepositoriesWithLocalFilePresent( path );
+
+        createMockedProxyConnector( ID_MOCKED_PROXIED1, NAME_MOCKED_PROXIED1, PropagateErrorsDownloadPolicy.IGNORE,
+                                    PropagateErrorsOnUpdateDownloadPolicy.NOT_PRESENT );
+        createMockedProxyConnector( ID_MOCKED_PROXIED2, NAME_MOCKED_PROXIED2, PropagateErrorsDownloadPolicy.IGNORE,
+                                    PropagateErrorsOnUpdateDownloadPolicy.NOT_PRESENT );
+
+        simulateGetIfNewerError( path, expectedFile, createTransferException() );
+        simulateGetIfNewerError( path, expectedFile, createTransferException() );
+
+        confirmNotDownloadedNoError( path );
+        assertTrue( expectedFile.exists() );
+    }
+
+    // ------------------------------------------
+    // HELPER METHODS
+    // ------------------------------------------
+
+    private void createMockedProxyConnector( String id, String name, String errorPolicy )
+    {
+        saveRemoteRepositoryConfig( id, name, "test://bad.machine.com/repo/", "default" );
+        saveConnector( ID_DEFAULT_MANAGED, id, ChecksumPolicy.FIX, ReleasesPolicy.ALWAYS, SnapshotsPolicy.ALWAYS,
+                       CachedFailuresPolicy.NO, errorPolicy );
+    }
+
+    private void createMockedProxyConnector( String id, String name, String errorPolicy, String errorOnUpdatePolicy )
+    {
+        saveRemoteRepositoryConfig( id, name, "test://bad.machine.com/repo/", "default" );
+        saveConnector( ID_DEFAULT_MANAGED, id, ChecksumPolicy.FIX, ReleasesPolicy.ALWAYS, SnapshotsPolicy.ALWAYS,
+                       CachedFailuresPolicy.NO, errorPolicy, errorOnUpdatePolicy );
+    }
+
+    private File setupRepositoriesWithLocalFileNotPresent( String path )
+        throws Exception
+    {
+        setupTestableManagedRepository( path );
+
+        File file = new File( managedDefaultDir, path );
+
+        assertNotExistsInManagedDefaultRepo( file );
+
+        return file;
+    }
+
+    private File setupRepositoriesWithLocalFilePresent( String path )
+        throws Exception
+    {
+        setupTestableManagedRepository( path );
+
+        File file = new File( managedDefaultDir, path );
+
+        assertTrue( file.exists() );
+
+        return file;
+    }
+
+    private void simulateGetError( String path, File expectedFile, Exception throwable )
+        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
+    {
+        wagonMock.get( path, createExpectedTempFile( expectedFile ) );
+        wagonMockControl.setThrowable( throwable, 1 );
+    }
+
+    private void simulateGetIfNewerError( String path, File expectedFile, TransferFailedException exception )
+        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
+    {
+        wagonMock.getIfNewer( path, createExpectedTempFile( expectedFile ), expectedFile.lastModified() );
+        wagonMockControl.setThrowable( exception, 1 );
+    }
+
+    private File createExpectedTempFile( File expectedFile )
+    {
+        return new File( expectedFile.getParentFile(), expectedFile.getName() + ".tmp" ).getAbsoluteFile();
+    }
+
+    private void confirmSingleFailure( String path, String id )
+        throws LayoutException
+    {
+        confirmFailures( path, new String[]{id} );
+    }
+
+    private void confirmFailures( String path, String[] ids )
+        throws LayoutException
+    {
+        wagonMockControl.replay();
+
+        // Attempt the proxy fetch.
+        File downloadedFile = null;
+        try
+        {
+            downloadedFile = proxyHandler.fetchFromProxies( managedDefaultRepository,
+                                                            managedDefaultRepository.toArtifactReference( path ) );
+            fail( "Proxy should not have succeeded" );
+        }
+        catch ( ProxyDownloadException e )
+        {
+            assertEquals( ids.length, e.getFailures().size() );
+            for ( String id : ids )
+            {
+                assertTrue( e.getFailures().keySet().contains( id ) );
+            }
+        }
+
+        wagonMockControl.verify();
+
+        assertNotDownloaded( downloadedFile );
+    }
+
+    private void confirmSuccess( String path, File expectedFile, String basedir )
+        throws Exception
+    {
+        File downloadedFile = performDownload( path );
+
+        File proxied1File = new File( basedir, path );
+        assertFileEquals( expectedFile, downloadedFile, proxied1File );
+    }
+
+    private void confirmNotDownloadedNoError( String path )
+        throws Exception
+    {
+        File downloadedFile = performDownload( path );
+
+        assertNotDownloaded( downloadedFile );
+    }
+
+    private File performDownload( String path )
+        throws ProxyDownloadException, LayoutException
+    {
+        wagonMockControl.replay();
+
+        // Attempt the proxy fetch.
+        File downloadedFile = proxyHandler.fetchFromProxies( managedDefaultRepository,
+                                                             managedDefaultRepository.toArtifactReference( path ) );
+
+        wagonMockControl.verify();
+        return downloadedFile;
+    }
+
+    private static TransferFailedException createTransferException()
+    {
+        return new TransferFailedException( "test download exception" );
+    }
+
+    private static ResourceDoesNotExistException createResourceNotFoundException()
+    {
+        return new ResourceDoesNotExistException( "test download not found" );
+    }
+}
\ No newline at end of file
diff --git a/archiva-1.0.x/archiva-base/archiva-proxy/src/test/resources/org/apache/maven/archiva/proxy/ErrorHandlingTest.xml b/archiva-1.0.x/archiva-base/archiva-proxy/src/test/resources/org/apache/maven/archiva/proxy/ErrorHandlingTest.xml
new file mode 100644 (file)
index 0000000..508b58b
--- /dev/null
@@ -0,0 +1,118 @@
+<!--
+  ~ 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.
+  -->
+
+<component-set>
+  <components>
+    <component>
+      <role>org.apache.maven.wagon.Wagon</role>
+      <role-hint>test</role-hint>
+      <implementation>org.apache.maven.archiva.proxy.WagonDelegate</implementation>
+    </component>
+    <component>
+      <role>org.apache.maven.archiva.configuration.ArchivaConfiguration</role>
+      <role-hint>mock</role-hint>
+      <implementation>org.apache.maven.archiva.proxy.MockConfiguration</implementation>
+    </component>
+    <component>
+      <role>org.apache.maven.archiva.repository.RepositoryContentFactory</role>
+      <role-hint>mocked</role-hint>
+      <implementation>org.apache.maven.archiva.repository.RepositoryContentFactory</implementation>
+      <description>RepositoryContentRequest</description>
+      <requirements>
+        <requirement>
+          <role>org.apache.maven.archiva.configuration.ArchivaConfiguration</role>
+          <role-hint>mock</role-hint>
+          <field-name>archivaConfiguration</field-name>
+        </requirement>
+      </requirements>
+    </component>
+    <component>
+      <role>org.apache.maven.archiva.proxy.RepositoryProxyConnectors</role>
+      <role-hint>default</role-hint>
+      <implementation>org.apache.maven.archiva.proxy.DefaultRepositoryProxyConnectors</implementation>
+      <description>DefaultRepositoryProxyConnectors</description>
+      <requirements>
+        <requirement>
+          <role>org.apache.maven.archiva.configuration.ArchivaConfiguration</role>
+          <role-hint>mock</role-hint>
+          <field-name>archivaConfiguration</field-name>
+        </requirement>
+        <requirement>
+          <role>org.apache.maven.wagon.Wagon</role>
+          <field-name>wagons</field-name>
+        </requirement>
+        <requirement>
+          <role>org.apache.maven.archiva.repository.RepositoryContentFactory</role>
+          <role-hint>mocked</role-hint>
+        </requirement>
+        <requirement>
+          <role>org.apache.maven.archiva.repository.metadata.MetadataTools</role>
+        </requirement>
+        <requirement>
+          <role>org.apache.maven.archiva.policies.PreDownloadPolicy</role>
+          <field-name>preDownloadPolicies</field-name>
+        </requirement>
+        <requirement>
+          <role>org.apache.maven.archiva.policies.PostDownloadPolicy</role>
+          <field-name>postDownloadPolicies</field-name>
+        </requirement>
+        <requirement>
+          <role>org.apache.maven.archiva.policies.DownloadErrorPolicy</role>
+          <field-name>downloadErrorPolicies</field-name>
+        </requirement>
+        <requirement>
+          <role>org.apache.maven.archiva.policies.urlcache.UrlFailureCache</role>
+          <role-hint>default</role-hint>
+          <field-name>urlFailureCache</field-name>
+        </requirement>
+        <requirement>
+          <role>org.apache.maven.archiva.repository.scanner.RepositoryContentConsumers</role>
+          <field-name>consumers</field-name>
+        </requirement>
+      </requirements>
+    </component>
+    
+    <component>
+      <role>org.codehaus.plexus.cache.Cache</role>
+      <role-hint>url-failures-cache</role-hint>
+      <implementation>org.codehaus.plexus.cache.ehcache.EhcacheCache</implementation>
+      <description>URL Failure Cache</description>
+      <configuration>
+        <disk-expiry-thread-interval-seconds>600</disk-expiry-thread-interval-seconds>
+        <disk-persistent>false</disk-persistent> <!--disabling disk persistence for unit testing. -->
+        <disk-store-path>${java.io.tmpdir}/archiva/urlcache</disk-store-path>
+        <eternal>false</eternal>
+        <max-elements-in-memory>1000</max-elements-in-memory>
+        <memory-eviction-policy>LRU</memory-eviction-policy>
+        <name>url-failures-cache</name>
+        <overflow-to-disk>false</overflow-to-disk>
+        <!-- 45 minutes = 2700 seconds -->
+        <time-to-idle-seconds>2700</time-to-idle-seconds>
+        <!-- 30 minutes = 1800 seconds  -->
+        <time-to-live-seconds>1800</time-to-live-seconds>
+      </configuration>
+    </component>    
+    
+    <component>
+      <role>org.codehaus.plexus.logging.LoggerManager</role>
+      <implementation>org.codehaus.plexus.logging.slf4j.Slf4jLoggerManager</implementation>
+      <lifecycle-handler>basic</lifecycle-handler>
+    </component>
+  </components>
+</component-set>
\ No newline at end of file
index 68adb6c57133d8d77d24ba60eeda794178672c50..1318f5765c0574aeb363672c9a7fe7f0beb29afd 100644 (file)
@@ -22,7 +22,8 @@ package org.apache.maven.archiva.web.action.admin.connectors.proxy;
 import com.opensymphony.xwork.Preparable;
 import org.apache.commons.lang.StringUtils;
 import org.apache.maven.archiva.configuration.ProxyConnectorConfiguration;
-import org.apache.maven.archiva.policies.DownloadPolicy;
+import org.apache.maven.archiva.policies.DownloadErrorPolicy;
+import org.apache.maven.archiva.policies.Policy;
 import org.apache.maven.archiva.policies.PostDownloadPolicy;
 import org.apache.maven.archiva.policies.PreDownloadPolicy;
 
@@ -54,6 +55,11 @@ public abstract class AbstractProxyConnectorFormAction
      */
     private Map<String, PostDownloadPolicy> postDownloadPolicyMap;
 
+    /**
+     * @plexus.requirement role="org.apache.maven.archiva.policies.DownloadErrorPolicy"
+     */
+    private Map<String, DownloadErrorPolicy> downloadErrorPolicyMap;
+
     /**
      * The list of network proxy ids that are available.
      */
@@ -72,7 +78,7 @@ public abstract class AbstractProxyConnectorFormAction
     /**
      * The map of policies that are available to be set.
      */
-    private Map<String, DownloadPolicy> policyMap;
+    private Map<String, Policy> policyMap;
 
     /**
      * The property key to add or remove.
@@ -185,7 +191,7 @@ public abstract class AbstractProxyConnectorFormAction
         return pattern;
     }
 
-    public Map<String, DownloadPolicy> getPolicyMap()
+    public Map<String, Policy> getPolicyMap()
     {
         return policyMap;
     }
@@ -318,7 +324,7 @@ public abstract class AbstractProxyConnectorFormAction
         this.pattern = pattern;
     }
 
-    public void setPolicyMap( Map<String, DownloadPolicy> policyMap )
+    public void setPolicyMap( Map<String, Policy> policyMap )
     {
         this.policyMap = policyMap;
     }
@@ -363,12 +369,13 @@ public abstract class AbstractProxyConnectorFormAction
         return options;
     }
 
-    protected Map<String, DownloadPolicy> createPolicyMap()
+    protected Map<String, Policy> createPolicyMap()
     {
-        Map<String, DownloadPolicy> policyMap = new HashMap<String, DownloadPolicy>();
+        Map<String, Policy> policyMap = new HashMap<String, Policy>();
 
         policyMap.putAll( preDownloadPolicyMap );
         policyMap.putAll( postDownloadPolicyMap );
+        policyMap.putAll( downloadErrorPolicyMap );
 
         return policyMap;
     }
@@ -387,10 +394,10 @@ public abstract class AbstractProxyConnectorFormAction
         else
         {
             // Validate / Fix policy settings arriving from browser.
-            for ( Map.Entry<String, DownloadPolicy> entry : getPolicyMap().entrySet() )
+            for ( Map.Entry<String, Policy> entry : getPolicyMap().entrySet() )
             {
-                String policyId = (String) entry.getKey();
-                DownloadPolicy policy = (DownloadPolicy) entry.getValue();
+                String policyId = entry.getKey();
+                Policy policy = entry.getValue();
                 List<String> options = policy.getOptions();
 
                 if ( !connector.getPolicies().containsKey( policyId ) )
index 027f58f54b3a898f02b57c500fabbf224f4e8e08..34ccbbf789994e2ec86c257337809a0b131590c4 100644 (file)
@@ -19,23 +19,11 @@ package org.apache.maven.archiva.web.repository;
  * under the License.
  */
 
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletResponse;
-
 import org.apache.maven.archiva.common.utils.PathUtil;
 import org.apache.maven.archiva.model.ArtifactReference;
 import org.apache.maven.archiva.model.ProjectReference;
 import org.apache.maven.archiva.model.VersionedReference;
-import org.apache.maven.archiva.proxy.ProxyException;
+import org.apache.maven.archiva.policies.ProxyDownloadException;
 import org.apache.maven.archiva.proxy.RepositoryProxyConnectors;
 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
 import org.apache.maven.archiva.repository.RepositoryContentFactory;
@@ -61,6 +49,17 @@ import org.codehaus.plexus.webdav.DavServerListener;
 import org.codehaus.plexus.webdav.servlet.DavServerRequest;
 import org.codehaus.plexus.webdav.util.WebdavMethodUtil;
 
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * ProxiedDavServer
  * 
@@ -365,7 +364,7 @@ public class ProxiedDavServer
         {
             /* eat it */
         }
-        catch ( ProxyException e )
+        catch ( ProxyDownloadException e )
         {
             throw new ServletException( "Unable to fetch artifact resource.", e );
         }
@@ -392,10 +391,6 @@ public class ProxiedDavServer
         {
             /* eat it */
         }
-        catch ( ProxyException e )
-        {
-            throw new ServletException( "Unable to fetch versioned metadata resource.", e );
-        }
 
         try
         {
@@ -410,10 +405,6 @@ public class ProxiedDavServer
         {
             /* eat it */
         }
-        catch ( ProxyException e )
-        {
-            throw new ServletException( "Unable to fetch project metadata resource.", e );
-        }
 
         return false;
     }
@@ -429,7 +420,7 @@ public class ProxiedDavServer
      * artifact.
      */
     protected void applyServerSideRelocation( ArtifactReference artifact )
-        throws ProxyException
+        throws ProxyDownloadException
     {
         if ( "pom".equals( artifact.getType() ) )
         {
index 52acf5881ce24b8cae630b22ad1974d9b24c3450..6e04cee2daebefc35c8ed8fa79d5bf4bae590242 100644 (file)
@@ -42,7 +42,7 @@
         <tr>
           <td>
             <ww:label for="policy_${policy.key}" required="true"
-                      theme="simple">${policy.key}:
+                      theme="simple">${policy.value.name}:
             </ww:label>
           </td>
           <td>
index b4747e393ed28681df915263891e41bc08e2b25e..213ce9fbb051847f16ac653bf5229576d43773d9 100644 (file)
@@ -28,6 +28,8 @@ import org.apache.maven.archiva.configuration.ProxyConnectorConfiguration;
 import org.apache.maven.archiva.configuration.RemoteRepositoryConfiguration;
 import org.apache.maven.archiva.policies.CachedFailuresPolicy;
 import org.apache.maven.archiva.policies.ChecksumPolicy;
+import org.apache.maven.archiva.policies.PropagateErrorsDownloadPolicy;
+import org.apache.maven.archiva.policies.PropagateErrorsOnUpdateDownloadPolicy;
 import org.apache.maven.archiva.policies.ReleasesPolicy;
 import org.apache.maven.archiva.policies.SnapshotsPolicy;
 import org.apache.maven.archiva.web.action.AbstractWebworkTestCase;
@@ -36,6 +38,7 @@ import org.codehaus.plexus.registry.RegistryException;
 import org.easymock.MockControl;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * AddProxyConnectorActionTest 
@@ -390,10 +393,13 @@ public class AddProxyConnectorActionTest
         connector.setTargetRepoId( "central" );
 
         // TODO: Set these options programatically via list of available policies.
-        connector.getPolicies().put( "releases", new ReleasesPolicy().getDefaultOption() );
-        connector.getPolicies().put( "snapshots", new SnapshotsPolicy().getDefaultOption() );
-        connector.getPolicies().put( "checksum", new ChecksumPolicy().getDefaultOption() );
-        connector.getPolicies().put( "cache-failures", new CachedFailuresPolicy().getDefaultOption() );
+        Map<String, String> policies = connector.getPolicies();
+        policies.put( "releases", new ReleasesPolicy().getDefaultOption() );
+        policies.put( "snapshots", new SnapshotsPolicy().getDefaultOption() );
+        policies.put( "checksum", new ChecksumPolicy().getDefaultOption() );
+        policies.put( "cache-failures", new CachedFailuresPolicy().getDefaultOption() );
+        policies.put( "propagate-errors", new PropagateErrorsDownloadPolicy().getDefaultOption() );
+        policies.put( "propagate-errors-on-update", new PropagateErrorsOnUpdateDownloadPolicy().getDefaultOption() );
     }
 
     @Override
index 626cf5654a791ad91ec86c065454bb455200e726..31364f4b8a558ce9dee485f2c855c53ad081a7fd 100644 (file)
@@ -28,6 +28,8 @@ import org.apache.maven.archiva.configuration.ProxyConnectorConfiguration;
 import org.apache.maven.archiva.configuration.RemoteRepositoryConfiguration;
 import org.apache.maven.archiva.policies.CachedFailuresPolicy;
 import org.apache.maven.archiva.policies.ChecksumPolicy;
+import org.apache.maven.archiva.policies.PropagateErrorsDownloadPolicy;
+import org.apache.maven.archiva.policies.PropagateErrorsOnUpdateDownloadPolicy;
 import org.apache.maven.archiva.policies.ReleasesPolicy;
 import org.apache.maven.archiva.policies.SnapshotsPolicy;
 import org.apache.maven.archiva.web.action.AbstractWebworkTestCase;
@@ -36,6 +38,7 @@ import org.codehaus.plexus.registry.RegistryException;
 import org.easymock.MockControl;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * EditProxyConnectorActionTest 
@@ -395,10 +398,13 @@ public class EditProxyConnectorActionTest
         connector.setTargetRepoId( TEST_TARGET_ID );
         
         // TODO: Set these options programatically via list of available policies.
-        connector.getPolicies().put( "releases", new ReleasesPolicy().getDefaultOption() );
-        connector.getPolicies().put( "snapshots", new SnapshotsPolicy().getDefaultOption() );
-        connector.getPolicies().put( "checksum", new ChecksumPolicy().getDefaultOption() );
-        connector.getPolicies().put( "cache-failures", new CachedFailuresPolicy().getDefaultOption() );
+        Map<String, String> policies = connector.getPolicies();
+        policies.put( "releases", new ReleasesPolicy().getDefaultOption() );
+        policies.put( "snapshots", new SnapshotsPolicy().getDefaultOption() );
+        policies.put( "checksum", new ChecksumPolicy().getDefaultOption() );
+        policies.put( "cache-failures", new CachedFailuresPolicy().getDefaultOption() );
+        policies.put( "propagate-errors", new PropagateErrorsDownloadPolicy().getDefaultOption() );
+        policies.put( "propagate-errors-on-update", new PropagateErrorsOnUpdateDownloadPolicy().getDefaultOption() );
 
         config.addProxyConnector( connector );