2014-05-19 6 views
5

Ich habe eine sehr einfache Camel Route Definition, die nur einige OnException Prädikate enthält, um entsprechende Ausnahmen und einige Protokollanweisungen zu behandeln.Camel route-testing mit adviceWith mit OnException Definitionen

from("hazelcast:seda:someQueue") 
    .id("someQueueID") 
    .onException(CustomException.class) 
     .handled(true) 
     .log(LoggingLevel.WARN, "custom exception noticed") 
    .end() 
    .onException(IOException.class, FileNotFoundException.class) 
     .asyncDelayedRedelivery() 
     .redeliveryDelay(3*1000*60) // 3 Minutes 
     .maximumRedeliveries(3) 
     .log(LoggingLevel.WARN, "io exception noticed") 
    .end() 
    .onException(Exception.class) 
     .log(LoggingLevel.WARN, "general exception noticed") 
    .end() 

    .log("Starting route") 
    .bean(TestBean.class) 
    .log("Finished route"); 

Die Bohne selbst zu einfach ist, überprüft er nur einen Header-Parameter und löst eine entsprechende Ausnahme

public class TestBean 
{ 
    @Handler 
    public void checkData(@Headers final Map<String, Object> headers) 
      throws CustomException, IOException, Exception 
    { 
     Integer testVal = (Integer)headers.get("TestValue"); 
     if (0 == testVal) 
      throw new CustomException("CustomException"); 
     else if (1 == testVal) 
      throw new IOException("IOException"); 
     else 
      throw new Exception("Exception"); 
    } 
} 

Da dieser Test-Setup nur ein kleiner Teil eines größeren Projekts ist es albern klingt Dies ist wie hier dargestellt, aber der Kern besteht darin, die RedeliveryDelay zum Testzeitpunkt zu ändern, da eine "erzwungene" IOException nicht 3 Minuten warten muss. Daher kann die Neuzustellungsverzögerung reduziert werden, um Unit Tests ein wenig zu beschleunigen wie 10 ms.

Um diese meinen Test-Methode zu erreichen, führt Folgendes aus:

@ContextConfiguration(classes = OnExceptionRouteTest.ContextConfig.class, loader = AnnotationConfigContextLoader.class) 
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) 
public class OnExceptionRouteTest extends CamelSpringTestSupport 
{ 
    @Override 
    protected AbstractApplicationContext createApplicationContext() 
    { 
     return new AnnotationConfigApplicationContext(ContextConfig.class) 
    } 

    @Configuration 
    public static class ContextConfig extends CamelConfiguration 
    { 
     @Override 
     protected void setupCamelContext(CamelContext camelContext) throws Exception 
     { 
      super.setupCamelContext(camelContext); 
      camelContext.addComponent("hazelcast", new StubComponent()); 
      // some other unnecessary stuff 
     } 

     @Override 
     public List<RouteBuilder> routes() 
     { 
      final List<RouteBuilder> list = new ArrayList<>(); 
      list.add(new OnExceptionRoute()); 
      return list; 
     } 
    } 

    @Override 
    public boolean isUseAdviceWith() 
    { 
     return true; 
    } 

    @Test 
    public void testIOException() 
    { 
     context.getRouteDefinition("someQueueID") 
       .adviceWith(context, new AdviceWithRouteBuilder() 
       { 
        @Override 
        public void configure() throws Exception 
        { 
         this.weaveByType(OnExceptionDefinition.class) 
          .selectIndex(1) 
          .replace() 
           .onException(IOException.class, FileNotFound.class) 
            .asyncDelayedRedelivery() 
            .redeliveryDelay(10) 
            .maximumRedeliveries(3) 
            .log("modified io exception noticed") 
            .to("mock:ioError") 
           .end(); 
          ... 
          mockEndpoints(); 
        } 
       }); 
     context.start(); 
     MockEndpoint ioErrorEndpoint = getMockEndpoint("mock:ioError"); 
     ... 
     ioErrorEndpoint.setExpectedMessageCount(1); 
     ... 

     Map<String, Object> headers = new HashMap<>(); 
     headers.put("TestValue", new Integer(1)); 
     template.sendBodyAndHeaders("hazelcast:seda:someQueue", new Object(), headers); 

     ... 
     ioErrorEndpoint.assertIsSatisfied(); 
     ... 
    } 
} 

Hier der Test ersetzt nur das OnException Segment der IOException zuerst die Nachlieferung Verzögerung von 3 Minuten bis 10 ms zu reduzieren und fügt eine Schein-Endpunkt am Ende. Allerdings, wenn ich versuchen, das Gerät zu testen laufen werde ich die folgende Ausnahme erhalten:

java.lang.IllegalArgumentException: The output must be added as top-level on the route. Try moving OnException[[class java.io.IOException, class java.io.FileNotFoundException] -> []] to the top of route. 

Allerdings sind die Beispiele in der official documentation, soweit ich verstanden sie richtig, sind sehr ähnlich. Ich habe auch versucht, die Ausnahmedefinition über ein definiertes ID-Prädikat und seine entsprechende Methode weaveById() oder über die weaveByToString()-Methode nachzuschlagen, aber mit keinem anderen Ergebnis. Ich habe auch versucht, die Ausnahmedefinition über weaveByType(OnExceptionDefinition.class).selectIndex(1).remove(); zu entfernen und den OnException-Teil über weaveAddFirst().onException(...).async...; hinzuzufügen, aber mit dem gleichen Ergebnis.

Das Anfügen eines verspotteten Fehlerendpunkts ist jedoch über z.B. weaveByToString("Log[io exception noticed]").after().to("mock:ioError");

Also alle Tipps zum Ändern onException Blöcke oder die RedeliveryDelay für Komponententests sind mehr als willkommen.


@Edit: Ich nun auch versucht, die OnException Erklärungen über der Routendefinition zu bewegen (from(...)), wie durch die Ausnahmemeldung vorgeschlagen und dies war auch der bevorzugte Fall in Camel exception samples. Dabei versagen jedoch alle Tests (auch die Working-Tests) ab NullPointerException unter context.getRouteDefinition("someQueueID").adviceWith(context, new AdviceWithRouteBuilder() {... });, da offensichtlich die Route selbst nicht mehr gefunden werden kann. Ich bezweifle, dass dies ein IntelliJ-Problem ist, da beide Klassen innerhalb des gleichen Projekts liegen und daher eine Änderung der Route für die Testklasse sichtbar sein sollte.

Camel-Version im Einsatz: 2.13.0, IntelliJ IDEA 13.1.2


@ Edit2: Aus irgendeinem Grund context.getRouteDefinitions("someQueueID") NULL, wenn die OnException Elemente außerhalb des from Block definiert, während die allgemeine Route kann über context.getRouteDefinitions().get(0) abgerufen werden - obwohl die Ausnahme, die besagt, dass der OnException-Teil als Element auf oberster Ebene hinzugefügt werden muss, bleibt.

+1

Eine weitere Option ist die redeliveryDelay (und vielleicht auch verschiedene andere Einstellungen), um eine Eigenschaft zu machen, und dann eine Produktionseigenschaften Datei + ein Test Properties-Datei, mit differierende Werte Ihren Bedürfnissen in jeder passen Umgebung. –

+0

@SteveHarrington Obwohl Ihr Vorschlag beim Eingeben von Eigenschaftswerten in diese Felder funktioniert, löst dies meines Erachtens nicht das Kernproblem für Ratschläge in Verbindung mit OnException-Blöcken. Ich möchte jedoch Ihren Vorschlag als Antwort akzeptieren, wenn Sie bereit sind, eine Antwort zu veröffentlichen. –

Antwort

4

Bei der Verwendung von Java DSL wird die ID der Route mit der Methode .routeId() festgelegt, nicht .id(), wie oben beschrieben. Das kann bei deinen adviceWith Bedenken helfen.

Anstatt die Wiederholungsverzögerung fest zu codieren, wäre ein besserer Ansatz, die Verzögerung mithilfe von Eigenschaften konfigurierbar zu machen. Sehen Sie sich die Dokumentation zur Methode useOverridePropertiesWithPropertiesComponent() Ihrer Klasse CamelSpringTestSupport an.

EDIT

Sie müssen die onException Klausel nicht weben, einfach einen neuen angeben. Hier ist ein komplettes Beispiel:

import org.apache.camel.builder.AdviceWithRouteBuilder; 
import org.apache.camel.builder.RouteBuilder; 
import org.apache.camel.test.junit4.CamelTestSupport; 

public class DummyTest extends CamelTestSupport{ 
    @Override 
    protected RouteBuilder createRouteBuilder() throws Exception { 
     return new RouteBuilder(){ 
      @Override 
      public void configure() throws Exception { 

       from("direct://start") 
        .routeId("myroute") 
        .onException(Exception.class) 
         .id("myException") 
         .continued(true) 
        .end() 
        .throwException(new Exception()) 
        .to("mock:end"); 
      } 
     }; 
    } 

    @org.junit.Test 
    public void doTest() throws Exception{ 
     context.getRouteDefinition("myroute").adviceWith(context, new AdviceWithRouteBuilder(){ 
      @Override 
      public void configure() throws Exception { 
       context.getRouteDefinition("myroute") 
        .onException(Exception.class).setBody(constant("adviceWith")).continued(true); 
      }}); 
     context.start(); 
     template.sendBody("direct://start", "original"); 
     String bodyAtEndOfExchange = getMockEndpoint("mock:end") 
       .getExchanges().get(0).getIn().getBody(String.class); 
     assertEquals("adviceWith", bodyAtEndOfExchange);   
     context.stop(); 
    } 

    @Override 
    public boolean isUseAdviceWith() { 
     return true; 
    } 
} 
+0

Ich habe bereits das Problem in Bezug auf '.id (...)' und '.routeId (...) herausgefunden, aber danke, dass du es hier aufgezeigt hast, da ich den Post seitdem nicht aktualisiert habe. Ich habe auch die Verzögerung über Eigenschaften konfiguriert, wie bereits von @SteveHarrington vorgeschlagen. Während das Problem des testspezifischen deleay behoben wurde, bleibt das eigentliche Thema jedoch - wie kann man einen onException-Block richtig beraten? Natürlich können "inOnly" und "to" mit Interzeptoren abgefangen werden, aber was ist, wenn wir etwas in der Mitte der Straße hinzufügen möchten und keine IDs verwenden. Nicht sicher, ob Camel 2.15+ jetzt besser unterstützt wird, muss es nochmal versuchen –

Verwandte Themen