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")
;
Basically ** 1 line 1 method ** (1 process)
However, the same line is recommended for those that qualify immediately before
For example, ʻid () `is the same line (think of an XML DSL)
routeId ()
sets the ID for route, so the same line as from ()
, at least the next line
Provide a blank line if you want to represent a group unit of processing
Continued lines have two indents (*)
Compliant with Java style
Indentation 2 steps even when the method argument is broken
If the argument is too long, consider cutting it into a constant
Conversely, only the beginning from ()
pops out two steps
** Indent further when structuring with DSL ** (see below)
Ending ;
separates lines
When adding processing, diff does not make unnecessary changes
Indent 1 step = 4 spaces (checkstyle) or 1 tab, which must be set separately
Those that create conditional branches and loops are basically xxx ()
to ʻend () `. The common rules are as follows.
xxx ()
(if it is longer, move to the next line and lower it by one step)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()
;
when ()
and ʻotherwise ()` are lowered by one step, and internal processing is lowered by one step.when ()
when (). Simple (...)
etc. are prohibitedSpecify an instance of Predicate
in the condition. (Documentation)
header (name)
or simple ("$ {expr} ")
(ValueBuilder)header (name) .isEqualTo (value)
(PredicateBuilder)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()
;
split ()
split (). body ()
etc. are prohibitedloop, 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()
;
The following method is for putting data in exchange.
setHeader()
setProperty()
setBody()
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)
constant (obj)
header (name)
or simple ("$ {expr} ")
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))
;
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}")
;
.name
,: name
, [name]
[name]
is good like an associative array.header
and not headers
when referencing header elementsYou 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
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.
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")
;
As explained in the section on structuring. You can think that filter ()
can write only a branch of one if statement (without else).
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