Kamelroutentest mit adviceWith mit OnException-Definitionen
Ich habe eine sehr einfache Camel-Routendefinition, die nur einige OnException-Prädikate zur Behandlung der jeweiligen Ausnahmen und einige Protokollanweisungen enthält.
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 Bean selbst ist ebenfalls einfach. Sie überprüft lediglich einen Header-Parameter und löst eine entsprechende Ausnahme aus
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 dieses Test-Setup nur ein kleiner Teil eines größeren Projekts ist, mag es albern klingen, es wie hier dargestellt zu machen, aber der Kern besteht darin, das redeliveryDelay zum Testzeitpunkt zu ändern, da eine "erzwungene" IOException nicht 3 Minuten warten muss Um die Einheitentests ein wenig zu beschleunigen, kann die Verzögerung für die erneute Zustellung auf etwa 10 ms reduziert werden.
Um dies zu erreichen, macht meine Testmethode folgendes:
@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 ersetzt der Test nur das Segment onException der IOException, um zunächst die Verzögerung für die erneute Zustellung von 3 Minuten auf 10 ms zu verringern und fügt am Ende einen Scheinendpunkt hinzu. Wenn ich jedoch versuche, den Komponententest durchzuführen, erhalte ich die folgende Ausnahme:
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.
Die Beispiele in deroffizielle DokumentationSoweit ich sie richtig verstanden habe, sind sie sehr ähnlich. Ich habe auch versucht, die Ausnahmedefinition über ein definiertes ID-Prädikat und die entsprechende Methode nachzuschlagenweaveById()
oder über dieweaveByToString()
Methode, aber ohne anderes Ergebnis. Ich habe auch versucht, die Ausnahmedefinition über zu entfernenweaveByType(OnExceptionDefinition.class).selectIndex(1).remove();
und fügen Sie den OnException-Teil über hinzuweaveAddFirst().onException(...).async...;
aber mit dem gleichen Ergebnis.
Das Anhängen eines verspotteten Fehlerendpunkts ist jedoch über z.weaveByToString("Log[io exception noticed]").after().to("mock:ioError");
Tipps zum Ändern von onException-Blöcken oder des redeliveryDelay für Komponententests sind daher sehr willkommen.
@Edit: Ich habe jetzt auch versucht, die onException-Deklarationen über die Routendefinition zu verschieben (from(...)
), wie in der Ausnahmemeldung vorgeschlagen, und dies war auch der bevorzugte Fall bei Camel'sAusnahmebeispiele. Dabei schlagen jedoch alle Tests (auch die funktionierenden) ab a fehlNullPointerException
aufcontext.getRouteDefinition("someQueueID").adviceWith(context, new AdviceWithRouteBuilder() {... });
Die Route selbst ist offensichtlich nicht mehr zu finden. Ich bezweifle, dass dies ein IntelliJ-Problem ist, da sich beide Klassen im selben Projekt befinden und daher eine Änderung der Route für die Testklasse sichtbar sein sollte.
Verwendete Kamelversion: 2.13.0, IntelliJ IDEA 13.1.2
@ Edit2: Aus irgendeinem Grundcontext.getRouteDefinitions("someQueueID")
Gibt null zurück, wenn die OnException-Elemente außerhalb von definiert sindfrom
Block, während die allgemeine Route erhalten werden kann übercontext.getRouteDefinitions().get(0)
- Die Ausnahme, dass der OnException-Teil als Element der obersten Ebene hinzugefügt werden muss, bleibt jedoch bestehen.