Java DSL personal coding conventions used in Apache Camel route construction

In Apache Camel, when creating a "route", write it in Original DSL that simply connects the prepared methods. This DSL also supports ** conditional branching ** and ** loop **.

However, if you write according to the Java style guide, the methods that create conditional branching and loop internal processing will also have the same indentation, making it very difficult to read as a DSL. So ** I think it's better to add the indentation yourself **.

Also, there are multiple ways to write other parts, but I want to unify them on the side that is easy to read. That's why I have summarized the coding standards that I have decided personally. Only the functions that I have used can be decided, so I will add or modify them as my experience increases in the future.

RouteBuilder About the shape of the entire basic route. Only the contents of configure () are shown.

from("direct:sample").routeId("MySampleRoute")
		// some process
		.to("mock:aaa").id("SamplePartA")
		.log(LoggingLevel.DEBUG,
				"very long text...")

		// another process
		.to("mock:bbb")
;

Structured

Those that create conditional branches and loops are basically xxx () to ʻend () `. The common rules are as follows.

choice Corresponds to a switch statement (break is not required). However, since the conditional expression is written on the when side, SQL and the * expressionless case statement * that can be done with Ruby are better. It's similar.

https://camel.apache.org/components/latest/eips/choice-eip.html

from("direct:sample")
		.choice()
			.when(new MyPredicateA())
				.to("mock:aaa")
			.when(new MyPredicateB())
				.to("mock:bbb")
			.otherwise()
				.to("mock:zzz")
		.end()
;

Specify an instance of Predicate in the condition. (Documentation)

filter If you have only one when () branch, consider using filter () instead of choice (). The number of lines and indentation are reduced, making it easier to read. Especially useful when writing guard clauses.

https://camel.apache.org/components/latest/eips/filter-eip.html

Example of guard clause


//It is difficult to read without a guard clause (* This is not limited to camel)
from("direct:noGuard")
		.choice()
			.when(new MyPredicate())
				.to("mock:doNothing")
			.otherwise()
				.to("mock:doSomething1")
				.to("mock:doSomething2")
				.to("mock:doSomething3")
		.end()
;

//You can make a guard clause with choice, but it's a little exaggerated
from("direct:guardByChoice")
		.choice()
			.when(new MyPredicate())
				.to("mock:doNothing")
				.stop()
		.end()

		.to("mock:doSomething1")
		.to("mock:doSomething2")
		.to("mock:doSomething3")
;

//Concise with filter
from("direct:guardByFilter")
		.filter(new MyPredicate())
			.to("mock:doNothing")
			.stop()
		.end()

		.to("mock:doSomething1")
		.to("mock:doSomething2")
		.to("mock:doSomething3")
;

split Corresponds to extended for statement (for-each statement) and forEach method. Lists and character strings are separated and individually packed in a new exchange for processing.

https://camel.apache.org/components/latest/eips/split-eip.html

from("direct:sample")
		.split(body()).parallelProcessing()
			.log("content: ${body}")
			.to("mock:abc")
		.end()
;

loop, while You can index the number of loops (starting from 0). Use loop () to specify the number of times like a for statement, and loopDoWhile () to specify a condition like a while statement.

https://camel.apache.org/components/latest/eips/loop-eip.html

from("direct:sample")
		.loop(100).copy()
			.log("index: ${exchangeProperty[CamelLoopIndex]}")
			.to("mock:abc")
		.end()
;

try … catch … finally There is also exception handling.

https://camel.apache.org/manual/latest/try-catch-finally.html

Again, according to the Java syntax, doTry (), doCatch (), doFinally (), ʻend () `are indented the same, and the inside of each is lowered by one step.

Example: https://github.com/apache/camel/blob/camel-2.25.1/camel-core/src/test/java/org/apache/camel/processor/TryProcessorMultipleExceptionTest.java

TryProcessorMultipleExceptionTest.java


from("direct:start")
		.doTry()
			.process(new ProcessorFail())
			.to("mock:result")
		.doCatch(IOException.class, IllegalStateException.class)
			.to("mock:catch")
		.doFinally()
			.to("mock:finally")
		.end()
;

Is the argument a method chain?

The following method is for putting data in exchange.

There are two methods, "specify by argument" and "specify by method chain". In the convention, ** unify to "specify by argument" **. I think it is easier to understand if the one method that composes the route is one process as it is.

It is based on the same idea that the argument is specified for when () and split () in the structure.

// disliked
from("direct:dislikedSample")
		.setHeader("foo").expression(new MyExpression())
		.setProperty("bar").header("foo")
		.setBody().exchangeProperty("bar")
;

// preferred
from("direct:preferredSample")
		.setHeader("foo", new MyExpression())
		.setProperty("bar", header("foo"))
		.setBody(exchangeProperty("bar"))
;

Specify an instance of ʻExpression` as an argument. (Documentation)

Rule exception

However, if it becomes complicated to specify the argument, use a method chain. In this case, write on the same line as much as possible.

For example marshal (). There is also a method of passing a character string as an argument, but if possible, I would like to use a constant prepared to prevent typos. Then the argument will be longer.

from("direct:marshalSample")
		.marshal().json(JsonLibrary.Gson)
		.marshal(new JsonDataFormat(JsonLibrary.Gson))
;

Expression expansion

In the string of the argument of log (), you can write the same expression expansion as simple () (I don't know if you call it ...).

https://camel.apache.org/components/latest/languages/simple-language.html

from("direct:loggingSample")
		.log("foo :: ${header[foo]}")
		.log("bar :: ${exchangeProperty[bar]}")
		.log("baz :: ${body}")
;

You can write more powerful expressions such as method calls, but the detailed rules have not been decided yet. → https://camel.apache.org/components/latest/languages/ognl-language.html

Select according to the situation

Sometimes you can do something similar in different ways. It is more readable to choose the one that suits the purpose and the one that is specialized for the purpose.

Log output

There are two types available. You shouldn't get lost because the usage is different.

from("direct:start")
		.log(LoggingLevel.DEBUG, "org.apache.camel.sample", "any message")
		.to("log:org.apache.camel.sample?level=DEBUG&showAll=true&multiline=true")
;

choice or filter

As explained in the section on structuring. You can think that filter () can write only a branch of one if statement (without else).

Expression expansion or method combination

Simple Expression Language can create various values and conditional expressions by using ** expression expansion ** (?). On the other hand, normal values (ValueBuilder) There are also methods that can be manipulated against. Writing in a method increases the chances of noticing a simple mistake at compile time, but without a good method it tends to be long. Which one is easier to read depends on the situation, so it is for reference only.

from("direct:simpleOrValue")
		// create same strings
		.setHeader("sample1", simple("hoge: ${header[hoge]}"))
		.setHeader("sample2", header("hoge").prepend("hoge: "))

		// check same conditions
		.choice()
			.when(simple("${header[CamelSqlRowCount]} != 1"))
				.log("record not unique!")
			.when(header(SqlConstants.SQL_ROW_COUNT).isNotEqualTo(1))
				.log("record not unique!")
		.end()
;

Recommended Posts

Java DSL personal coding conventions used in Apache Camel route construction
How to implement coding conventions in Java
Java coding conventions
Loose Java coding conventions
JavaFX environment construction in Java 13
What's new in Apache Camel 2.19.0
Apache Camel in the cloud-native era