Author: davsclaus
Date: Mon Dec 20 18:48:14 2010
New Revision: 1051239

URL: http://svn.apache.org/viewvc?rev=1051239&view=rev
Log:
CAMEL-3448: Fixed issue with route scoped onException may pickup an onException 
from another route. Also fixes so it now prefers route scoped over context 
scoped.

Added:
    
camel/trunk/camel-core/src/test/java/org/apache/camel/issues/TwoRouteScopedOnExceptionWithInterceptSendToEndpointIssueWithPredicateTest.java
      - copied, changed from r1051125, 
camel/trunk/camel-core/src/test/java/org/apache/camel/issues/RouteScopedOnExceptionWithInterceptSendToEndpointIssueWithPredicateTest.java
    
camel/trunk/camel-core/src/test/java/org/apache/camel/processor/onexception/RouteScopedOnExceptionSameTypeTest.java
      - copied, changed from r1051125, 
camel/trunk/camel-core/src/test/java/org/apache/camel/processor/onexception/OnExceptionAfterRouteTest.java
Modified:
    
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/ErrorHandlerSupport.java
    
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/exceptionpolicy/DefaultExceptionPolicyStrategy.java
    
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/exceptionpolicy/ExceptionPolicyKey.java

Modified: 
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/ErrorHandlerSupport.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/processor/ErrorHandlerSupport.java?rev=1051239&r1=1051238&r2=1051239&view=diff
==============================================================================
--- 
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/ErrorHandlerSupport.java
 (original)
+++ 
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/ErrorHandlerSupport.java
 Mon Dec 20 18:48:14 2010
@@ -24,6 +24,8 @@ import org.apache.camel.Exchange;
 import org.apache.camel.Processor;
 import org.apache.camel.impl.ServiceSupport;
 import org.apache.camel.model.OnExceptionDefinition;
+import org.apache.camel.model.ProcessorDefinitionHelper;
+import org.apache.camel.model.RouteDefinition;
 import 
org.apache.camel.processor.exceptionpolicy.DefaultExceptionPolicyStrategy;
 import org.apache.camel.processor.exceptionpolicy.ExceptionPolicyKey;
 import org.apache.camel.processor.exceptionpolicy.ExceptionPolicyStrategy;
@@ -49,7 +51,9 @@ public abstract class ErrorHandlerSuppor
         List<Class> list = exceptionType.getExceptionClasses();
 
         for (Class clazz : list) {
-            ExceptionPolicyKey key = new ExceptionPolicyKey(clazz, 
exceptionType.getOnWhen());
+            RouteDefinition route = 
ProcessorDefinitionHelper.getRoute(exceptionType);
+            String routeId = route != null ? route.getId() : null;
+            ExceptionPolicyKey key = new ExceptionPolicyKey(routeId, clazz, 
exceptionType.getOnWhen());
             exceptionPolicies.put(key, exceptionType);
         }
     }

Modified: 
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/exceptionpolicy/DefaultExceptionPolicyStrategy.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/processor/exceptionpolicy/DefaultExceptionPolicyStrategy.java?rev=1051239&r1=1051238&r2=1051239&view=diff
==============================================================================
--- 
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/exceptionpolicy/DefaultExceptionPolicyStrategy.java
 (original)
+++ 
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/exceptionpolicy/DefaultExceptionPolicyStrategy.java
 Mon Dec 20 18:48:14 2010
@@ -17,6 +17,7 @@
 package org.apache.camel.processor.exceptionpolicy;
 
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
@@ -61,17 +62,29 @@ public class DefaultExceptionPolicyStrat
                                                     Exchange exchange, 
Throwable exception) {
 
         Map<Integer, OnExceptionDefinition> candidates = new TreeMap<Integer, 
OnExceptionDefinition>();
+        Map<ExceptionPolicyKey, OnExceptionDefinition> routeScoped = new 
LinkedHashMap<ExceptionPolicyKey, OnExceptionDefinition>();
+        Map<ExceptionPolicyKey, OnExceptionDefinition> contextScoped = new 
LinkedHashMap<ExceptionPolicyKey, OnExceptionDefinition>();
 
+        // split policies into route and context scoped
+        initRouteAndContextScopedExceptionPolicies(exceptionPolicies, 
routeScoped, contextScoped);
+
+        // at first check route scoped as we prefer them over context scoped
         // recursive up the tree using the iterator
         boolean exactMatch = false;
         Iterator<Throwable> it = createExceptionIterator(exception);
         while (!exactMatch && it.hasNext()) {
             // we should stop looking if we have found an exact match
-            exactMatch = findMatchedExceptionPolicy(exceptionPolicies, 
exchange, it.next(), candidates);
+            exactMatch = findMatchedExceptionPolicy(routeScoped, exchange, 
it.next(), candidates);
         }
 
-        // now go through the candidates and find the best
+        // fallback to check context scoped (only do this if there was no 
exact match)
+        it = createExceptionIterator(exception);
+        while (!exactMatch && it.hasNext()) {
+            // we should stop looking if we have found an exact match
+            exactMatch = findMatchedExceptionPolicy(contextScoped, exchange, 
it.next(), candidates);
+        }
 
+        // now go through the candidates and find the best
         if (LOG.isTraceEnabled()) {
             LOG.trace("Found " + candidates.size() + " candidates");
         }
@@ -80,11 +93,26 @@ public class DefaultExceptionPolicyStrat
             // no type found
             return null;
         } else {
-            // return the first in the map as its sorted and
+            // return the first in the map as its sorted and we checked route 
scoped first, which we prefer
             return candidates.values().iterator().next();
         }
     }
 
+    private void 
initRouteAndContextScopedExceptionPolicies(Map<ExceptionPolicyKey, 
OnExceptionDefinition> exceptionPolicies,
+                                                            
Map<ExceptionPolicyKey, OnExceptionDefinition> routeScoped,
+                                                            
Map<ExceptionPolicyKey, OnExceptionDefinition> contextScoped) {
+
+        // loop through all the entries and split into route and context scoped
+        Set<Map.Entry<ExceptionPolicyKey, OnExceptionDefinition>> entries = 
exceptionPolicies.entrySet();
+        for (Map.Entry<ExceptionPolicyKey, OnExceptionDefinition> entry : 
entries) {
+            if (entry.getKey().getRouteId() != null) {
+                routeScoped.put(entry.getKey(), entry.getValue());
+            } else {
+                contextScoped.put(entry.getKey(), entry.getValue());
+            }
+        }
+    }
+
 
     private boolean findMatchedExceptionPolicy(Map<ExceptionPolicyKey, 
OnExceptionDefinition> exceptionPolicies,
                                                Exchange exchange, Throwable 
exception,

Modified: 
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/exceptionpolicy/ExceptionPolicyKey.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/processor/exceptionpolicy/ExceptionPolicyKey.java?rev=1051239&r1=1051238&r2=1051239&view=diff
==============================================================================
--- 
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/exceptionpolicy/ExceptionPolicyKey.java
 (original)
+++ 
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/exceptionpolicy/ExceptionPolicyKey.java
 Mon Dec 20 18:48:14 2010
@@ -20,16 +20,30 @@ import org.apache.camel.model.WhenDefini
 
 /**
  * Exception policy key is a compound key for storing:
- * <b>exception class</b> + <b>when</b> => <b>exception type</b>.
+ * <b>route id </b> + <b>exception class</b> + <b>when</b> => <b>exception 
type</b>.
  * <p/>
  * This is used by Camel to store the onException types configured that has or 
has not predicates attached (when).
  */
 public final class ExceptionPolicyKey {
 
+    private final String routeId;
     private final Class exceptionClass;
     private final WhenDefinition when;
 
+    @Deprecated
     public ExceptionPolicyKey(Class exceptionClass, WhenDefinition when) {
+        this(null, exceptionClass, when);
+    }
+
+    /**
+     * Key for exception clause
+     *
+     * @param routeId          the route, or use <tt>null</tt> for a global 
scoped
+     * @param exceptionClass   the exception class
+     * @param when             optional predicate when the exception clause 
should trigger
+     */
+    public ExceptionPolicyKey(String routeId, Class exceptionClass, 
WhenDefinition when) {
+        this.routeId = routeId;
         this.exceptionClass = exceptionClass;
         this.when = when;
     }
@@ -42,10 +56,16 @@ public final class ExceptionPolicyKey {
         return when;
     }
 
+    public String getRouteId() {
+        return routeId;
+    }
+
+    @Deprecated
     public static ExceptionPolicyKey newInstance(Class exceptionClass) {
         return new ExceptionPolicyKey(exceptionClass, null);
     }
 
+    @Deprecated
     public static ExceptionPolicyKey newInstance(Class exceptionClass, 
WhenDefinition when) {
         return new ExceptionPolicyKey(exceptionClass, when);
     }
@@ -61,7 +81,10 @@ public final class ExceptionPolicyKey {
 
         ExceptionPolicyKey that = (ExceptionPolicyKey) o;
 
-        if (!exceptionClass.equals(that.exceptionClass)) {
+        if (exceptionClass != null ? 
!exceptionClass.equals(that.exceptionClass) : that.exceptionClass != null) {
+            return false;
+        }
+        if (routeId != null ? !routeId.equals(that.routeId) : that.routeId != 
null) {
             return false;
         }
         if (when != null ? !when.equals(that.when) : that.when != null) {
@@ -73,13 +96,14 @@ public final class ExceptionPolicyKey {
 
     @Override
     public int hashCode() {
-        int result = exceptionClass.hashCode();
+        int result = routeId != null ? routeId.hashCode() : 0;
+        result = 31 * result + (exceptionClass != null ? 
exceptionClass.hashCode() : 0);
         result = 31 * result + (when != null ? when.hashCode() : 0);
         return result;
     }
 
     @Override
     public String toString() {
-        return "ExceptionPolicyKey[" + exceptionClass + (when != null ? " " + 
when : "") + "]";
+        return "ExceptionPolicyKey[route: " + (routeId != null ? routeId : 
"<global>") + ", " + exceptionClass + (when != null ? " " + when : "") + "]";
     }
 }

Copied: 
camel/trunk/camel-core/src/test/java/org/apache/camel/issues/TwoRouteScopedOnExceptionWithInterceptSendToEndpointIssueWithPredicateTest.java
 (from r1051125, 
camel/trunk/camel-core/src/test/java/org/apache/camel/issues/RouteScopedOnExceptionWithInterceptSendToEndpointIssueWithPredicateTest.java)
URL: 
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/issues/TwoRouteScopedOnExceptionWithInterceptSendToEndpointIssueWithPredicateTest.java?p2=camel/trunk/camel-core/src/test/java/org/apache/camel/issues/TwoRouteScopedOnExceptionWithInterceptSendToEndpointIssueWithPredicateTest.java&p1=camel/trunk/camel-core/src/test/java/org/apache/camel/issues/RouteScopedOnExceptionWithInterceptSendToEndpointIssueWithPredicateTest.java&r1=1051125&r2=1051239&rev=1051239&view=diff
==============================================================================
--- 
camel/trunk/camel-core/src/test/java/org/apache/camel/issues/RouteScopedOnExceptionWithInterceptSendToEndpointIssueWithPredicateTest.java
 (original)
+++ 
camel/trunk/camel-core/src/test/java/org/apache/camel/issues/TwoRouteScopedOnExceptionWithInterceptSendToEndpointIssueWithPredicateTest.java
 Mon Dec 20 18:48:14 2010
@@ -33,7 +33,7 @@ import org.apache.camel.model.RouteDefin
  *
  * @version $Revision$
  */
-public class 
RouteScopedOnExceptionWithInterceptSendToEndpointIssueWithPredicateTest extends 
ContextTestSupport {
+public class 
TwoRouteScopedOnExceptionWithInterceptSendToEndpointIssueWithPredicateTest 
extends ContextTestSupport {
 
     private final AtomicInteger invoked = new AtomicInteger();
 
@@ -89,6 +89,15 @@ public class RouteScopedOnExceptionWithI
                             .to("mock:exhausted")
                         .end()
                         .to("seda:foo");
+
+                from("direct:start2")
+                        // no redelivery delay for faster unit tests
+                        
.onException(ConnectException.class).maximumRedeliveries(3).redeliveryDelay(0)
+                            
.logRetryAttempted(true).retryAttemptedLogLevel(LoggingLevel.FATAL)
+                            // send to mock when we are exhausted
+                            .to("mock:exhausted2")
+                        .end()
+                        .to("seda:foo2");
             }
         };
     }

Copied: 
camel/trunk/camel-core/src/test/java/org/apache/camel/processor/onexception/RouteScopedOnExceptionSameTypeTest.java
 (from r1051125, 
camel/trunk/camel-core/src/test/java/org/apache/camel/processor/onexception/OnExceptionAfterRouteTest.java)
URL: 
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/onexception/RouteScopedOnExceptionSameTypeTest.java?p2=camel/trunk/camel-core/src/test/java/org/apache/camel/processor/onexception/RouteScopedOnExceptionSameTypeTest.java&p1=camel/trunk/camel-core/src/test/java/org/apache/camel/processor/onexception/OnExceptionAfterRouteTest.java&r1=1051125&r2=1051239&rev=1051239&view=diff
==============================================================================
--- 
camel/trunk/camel-core/src/test/java/org/apache/camel/processor/onexception/OnExceptionAfterRouteTest.java
 (original)
+++ 
camel/trunk/camel-core/src/test/java/org/apache/camel/processor/onexception/RouteScopedOnExceptionSameTypeTest.java
 Mon Dec 20 18:48:14 2010
@@ -16,36 +16,285 @@
  */
 package org.apache.camel.processor.onexception;
 
+import java.io.IOException;
+
 import org.apache.camel.ContextTestSupport;
 import org.apache.camel.builder.RouteBuilder;
 
 /**
  * @version $Revision$
  */
-public class OnExceptionAfterRouteTest extends ContextTestSupport {
+public class RouteScopedOnExceptionSameTypeTest extends ContextTestSupport {
 
     @Override
     public boolean isUseRouteBuilder() {
         return false;
     }
 
-    public void testOnExceptionAfterRoute() throws Exception {
-        try {
-            context.addRoutes(new RouteBuilder() {
-                @Override
-                public void configure() throws Exception {
-                    from("direct:start")
-                        .throwException(new IllegalArgumentException("Damn"));
-
-                    onException(IllegalArgumentException.class)
-                        .handled(true)
-                        .to("mock:damn");
-                }
-            });
-            fail("Should have thrown an exception");
-        } catch (IllegalArgumentException e) {
-            assertEquals("onException must be defined before any routes in the 
RouteBuilder", e.getMessage());
-        }
+    public void testOnExceptionExactType() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .onException(IllegalArgumentException.class)
+                        .handled(true)
+                        .to("mock:damn")
+                    .end()
+                    .throwException(new IllegalArgumentException("Damn"));
+
+                from("direct:foo")
+                    .onException(IllegalArgumentException.class)
+                        .handled(true)
+                        .to("mock:foo")
+                    .end()
+                    .throwException(new IllegalArgumentException("Damn"));
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:damn").expectedMessageCount(1);
+        getMockEndpoint("mock:foo").expectedMessageCount(0);
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testOnExceptionDifferentType() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .onException(IllegalArgumentException.class)
+                        .handled(true)
+                        .to("mock:damn")
+                    .end()
+                    .throwException(new IllegalArgumentException("Damn"));
+
+                from("direct:foo")
+                    .onException(IOException.class)
+                        .handled(true)
+                        .to("mock:foo")
+                    .end()
+                    .throwException(new IllegalArgumentException("Damn"));
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:damn").expectedMessageCount(1);
+        getMockEndpoint("mock:foo").expectedMessageCount(0);
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testOnExceptionSameTypeRouteLast() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:foo")
+                    .onException(IllegalArgumentException.class)
+                        .handled(true)
+                        .to("mock:foo")
+                    .end()
+                    .throwException(new IllegalArgumentException("Damn"));
+
+                from("direct:start")
+                    .onException(IllegalArgumentException.class)
+                        .handled(true)
+                        .to("mock:damn")
+                    .end()
+                    .throwException(new IllegalArgumentException("Damn"));
+
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:damn").expectedMessageCount(1);
+        getMockEndpoint("mock:foo").expectedMessageCount(0);
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testOnExceptionDifferentTypeRouteLast() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:foo")
+                    .onException(IOException.class)
+                        .handled(true)
+                        .to("mock:foo")
+                    .end()
+                    .throwException(new IllegalArgumentException("Damn"));
+
+                from("direct:start")
+                    .onException(IllegalArgumentException.class)
+                        .handled(true)
+                        .to("mock:damn")
+                    .end()
+                    .throwException(new IllegalArgumentException("Damn"));
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:damn").expectedMessageCount(1);
+        getMockEndpoint("mock:foo").expectedMessageCount(0);
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testOnExceptionExactTypeDLC() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                errorHandler(deadLetterChannel("mock:dead"));
+
+                from("direct:start")
+                    .onException(IllegalArgumentException.class)
+                        .handled(true)
+                        .to("mock:damn")
+                    .end()
+                    .throwException(new IllegalArgumentException("Damn"));
+
+                from("direct:foo")
+                    .onException(IllegalArgumentException.class)
+                        .handled(true)
+                        .to("mock:foo")
+                    .end()
+                    .throwException(new IllegalArgumentException("Damn"));
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:damn").expectedMessageCount(1);
+        getMockEndpoint("mock:foo").expectedMessageCount(0);
+        getMockEndpoint("mock:dlc").expectedMessageCount(0);
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testTwoOnExceptionExactType() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .onException(IOException.class)
+                        .handled(true)
+                        .to("mock:io")
+                    .end()
+                    .onException(IllegalArgumentException.class)
+                        .handled(true)
+                        .to("mock:damn")
+                    .end()
+                    .throwException(new IllegalArgumentException("Damn"));
+
+                from("direct:foo")
+                    .onException(IOException.class)
+                        .handled(true)
+                        .to("mock:io")
+                    .end()
+                    .onException(IllegalArgumentException.class)
+                        .handled(true)
+                        .to("mock:foo")
+                    .end()
+                    .throwException(new IllegalArgumentException("Damn"));
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:damn").expectedMessageCount(1);
+        getMockEndpoint("mock:foo").expectedMessageCount(0);
+        getMockEndpoint("mock:io").expectedMessageCount(0);
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testOnExceptionRouteAndGlobalExactType() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                onException(IllegalArgumentException.class)
+                    .handled(true)
+                    .to("mock:foo");
+
+                from("direct:start")
+                    .onException(IllegalArgumentException.class)
+                        .handled(true)
+                        .to("mock:damn")
+                    .end()
+                    .throwException(new IllegalArgumentException("Damn"));
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:damn").expectedMessageCount(1);
+        getMockEndpoint("mock:foo").expectedMessageCount(0);
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testOnExceptionRouteAndGlobalDifferentType() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                onException(IOException.class)
+                    .handled(true)
+                    .to("mock:foo");
+
+                from("direct:start")
+                    .onException(IllegalArgumentException.class)
+                        .handled(true)
+                        .to("mock:damn")
+                    .end()
+                    .throwException(new IllegalArgumentException("Damn"));
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:damn").expectedMessageCount(1);
+        getMockEndpoint("mock:foo").expectedMessageCount(0);
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testOnExceptionRouteAndOnlyGlobalExactType() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                onException(IllegalArgumentException.class)
+                    .handled(true)
+                    .to("mock:foo");
+
+                from("direct:start")
+                    .onException(IOException.class)
+                        .handled(true)
+                        .to("mock:damn")
+                    .end()
+                    .throwException(new IllegalArgumentException("Damn"));
+            }
+        });
+        context.start();
+
+        // this time we pick global scoped as its an exact match, so foo 
should get the message
+        getMockEndpoint("mock:damn").expectedMessageCount(0);
+        getMockEndpoint("mock:foo").expectedMessageCount(1);
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
     }
 
 }


Reply via email to