Java dsl что это

Java DSL

In the Java DSL you create a route by extending the RouteBuilder class, and implementing the configure method.

Java DSL example

This is best illustrate by an example. In the code below we create a new class called MyRouteBuilder that extends the org.apache.camel.builder.RouteBuilder from Camel.

In the configure method the Java DSL is at our disposal.

import org.apache.camel.builder.RouteBuilder; /** * A Camel Java DSL Router */ public class MyRouteBuilder extends RouteBuilder < /** * Let's configure the Camel routing rules using Java code. */ public void configure() < // here is a sample which processes the input files // (leaving them in place - see the 'noop' flag) // then performs content based routing on the message using XPath from("file:src/data?noop=true") .choice() .when(xpath("/person/city = 'London'")) .to("file:target/messages/uk") .otherwise() .to("file:target/messages/others"); >>

In the configure method we can define Camel Routes.

In the example above we have a single route, which pickup files (the from ).

Then we use the Content Based Router EIP (the choice ) to route the message whether the person is from London or not.

.choice() .when(xpath("/person/city = 'London'")) .to("file:target/messages/uk") .otherwise() .to("file:target/messages/others");

Routes using Java lambda style

Camel now supports to define Camel routes in Java DSL using Lambda style. This can be beneficial for microservices or serverless where you may want to quickly define a few routes.

For example using lambda style you can define a Camel route that takes messages from Kafka and send to JMS in a single line of code:

rb -> rb.from("kafka:cheese").to("jms:queue:foo");

There is a bit more to this as the lambda route must be coded in a Java method that returns an instance of LambdaRouteBuilder . See more at the LambdaRouteBuilder documentation.

More Details

For more details see DSL, Routes, and Processor.

The Java DSL under the hood

As mentioned in the Getting Started guide, you can use Camel’s Java DSL in a way that almost looks like a DSL. For instance:

Note: comments afterwards explain some of the constructs used in the example.

RouteBuilder builder = new RouteBuilder() < public void configure() < from("queue:a").filter(header("foo").isEqualTo("bar")).to("queue:b"); from("queue:c").choice() .when(header("foo").isEqualTo("bar")).to("queue:d") .when(header("foo").isEqualTo("cheese")).to("queue:e") .otherwise().to("queue:f"); >>; CamelContext myCamelContext = new DefaultCamelContext(); myCamelContext.addRoutes(builder);

The first line in the above example creates an object which is an instance of an anonymous subclass of RouteBuilder with the specified configure() method.

The CamelContext.addRoutes(RouterBuilder builder) method invokes builder.setContext(this) – so the RouteBuilder object knows which CamelContext object it is associated with – and then invokes builder.configure() . The body of configure() invokes methods such as from() , filter() , choice() , when() , isEqualTo() , otherwise() and to() .

The RouteBuilder.from(String uri) method invokes getEndpoint(uri) on the CamelContext associated with the RouteBuilder object to get the specified Endpoint and then puts a FromBuilder wrapper around this Endpoint . The FromBuilder.filter(Predicate predicate) method creates a FilterProcessor object for the Predicate (that is, condition) object built from the header(«foo»).isEqualTo(«bar») expression. In this way, these operations incrementally build up a Route object (with a RouteBuilder wrapper around it) and add it to the CamelContext instance associated with the RouteBuilder .

More Information

See Lambda Route Builder for creating a routing rule using the DSL, using Java lambda style.

Источник

Groovy как скриптовый язык и DSL для Java

При разработке разного рода ПО, например, задач мониторинга или обслуживания, я сталкиваюсь с необходимостью поддержки в программе выполнения сценариев, описывающих или выполняющих некоторый набор действий. И специфика такова, что добавление или изменение таких сценариев в ПО не должно требовать пересборки или перезапуска ПО.

Наверное, самыми простыми примерами таких сценариев, с которыми все сталкивались в том или ином виде, могут служить обычные пакетные файлы — bat или sh.

В своей практике, я иногда использую XML для описания сценариев. В случае, четко определенного заранее набора действий и их простого плоского набора без ветвлений — XML не плох для использования. Тут и фиксированная структура, позволяющая валидировать файл сценария, и простота самого языка XML, позволяющая с ним работать не только программистам. Однако реализовывать ветвления или условную логику в XML, на мой взгляд, очень накладно и сродни написанию своего мини языка. Расширение набора действий чаще всего возможно только при изменении исходного кода программы, да и изначально реализация поддержки XML сценария трудозатратна.

В общем, в поисках более простого и функционального инструмента, для описания сценариев, взор был обращен к скриптовым языкам. Так как платформа Java, то хотелось иметь возможности интеграции скриптового языка и Java. В итоге, выбор пал на Groovy – динамический язык на базе JVM, легко интегрируемый с Java, простой и выразительный, имеющий много полезных возможностей для нашей задачи.

Как?

Скрипты

Я не буду рассказывать основы Groovy, так как материалов в сети даже на русском уже предостаточно. Остановлюсь лишь на некоторых ключевых для нас моментах.

Groovy позволяет нам из Java кода выполнять не скомпилированный исходный Groovy код, что позволяет нам выполнять сценарии добавленные или измененные в runtime.

Рассмотрим пример выполнения Groovy скрипта в Java. Для поддержки Groovy в Вашем Java проекте нужно подключить в зависимости лишь одну библиотеку «groovy» нужной Вам версии.

Напишем следующий Groovy код в файле x:\GroovyScript.groovy:

Код для выполнения данного скрипта в Вашем Java код может быть таким:

GroovyShell shell = new GroovyShell ( ) ;
Object result = shell. evaluate ( new File ( «x:/GroovyScript.groovy» ) ) ;
System . out . println ( «result #66cc66″>+ result ) ;

В итоге выполнения, в консоль выведется 2 строки, первая из скрипта, вторая — из Java, с результатом выполнения скрипта:

Пример не несет функциональной нагрузки в скрипте, однако показывает возможности динамической загрузки и выполнения – всего две строчки кода и мы получаем возможность выполнения скриптов.

Немного о Java коде. GroovyShell – это класс предоставляемый Groovy для выполнения groovy скриптов. Существуют и другие способы выполнения groovy скриптов, подробнее смотрите тут

DSL

DSL – domain-specific language или предметно ориентированный язык. Язык, позволяющий использовать основные операции предметной области через набор простых и понятных высокоуровневых функций, скрывающий от пользователя их реализацию.

В приведенном выше примере код достаточно простой, однако в реальном сценарии он может быть очень большим и сложным. И работать с такими скриптами смогут только groovy-разработчики, избежать ошибок без тестирования будет сложно. В случае заранее известных операций в сценариях, всю бизнес логику можно вынести в код (java или groovy — неважно), и предоставить возможность использовать ее через набор функций.

Рассмотрим небольшой пример. Требуется написать скрипт, который будет выполнять архиваций, распаковку архивов, удаление, некоторые проверки и уведомления.
Один их кусочков сценария мог бы быть таким – проверить состояние процесса и в случае его завершения заархивировать каталог и послать уведомление:

//import

//check state
Process p = getProcess ( … )
int state = p. getCompleteState ( … )
if ( state == 1 ) <
//doSomeLogicForArchive
Zip z = new Zip ( … )
z. makeZip ( … )
> else <
//doAnotherLogic
return
>
//doSomeLogicForSendNotify
Smtp smtp = new Smtp ( … )
Message m = new Message ( … )
smtp. send ( to,m. )

Код получается достаточно большим, и он будет понятен, в основном, лишь программистам. Давайте его упростим и вынесем три указанных действия в класс ArchiveScript со статическими методами. Скрипт после вынесения методов:

import ArchiveScript
if ( ArchiveScript. checkState ( ) ) <
ArchiveScript. makeArchive ( .. )
> else <
//doAnotherLogic
return
>
ArchiveScript. sendNotify ( … )

Уже лучше? — Лучше, но все еще есть артефакты – импорт и названия класса, которые тоже стоило бы убрать. И в Groovy есть подобная возможность – возможность задания базового класса для скрипта вне самого скрипта. Класс ArchiveScript для этого должен наследоваться от Script и методы могут быть не статическими. Код скрипта при этом еще упрощается – исчезает импорт и префикс класса:

Уже достаточно хорошо. В случае если код внутри блока ветвления условия однострочный, можно отказаться и от фигурных скобок. А в случае Groovy часто и от скобок справа от имени метода. Код выполнения скрипта немного усложняется — нужно создать объект CompilerConfiguration, установить значение scriptBaseClass равное имени созданного нами класса ArchiveScript и передать этот объект в GroovyShell:

CompilerConfiguration conf = new CompilerConfiguration ( ) ;
conf. setScriptBaseClass ( «package.ArchiveScript» ) ;
GroovyShell shell = new GroovyShell ( conf ) ;

Далее, давайте рассмотрим, как задаются параметры методов в скрипте при вызове. В случае определения в классе ArchiveScript метода makeArchive в таком виде:

В скрипте вызов должен был бы выглядеть так:

makeArchive «x:/aaa/» , «x:/a.zip» , true

И если говорить о наглядности и даже удобстве Groovy позволяет нам сделать передачу параметров через именованные параметры, так:

Однако в этом случае параметры будут передаваться внутри HashMap и, соответственно, получение параметров в makeArchive в классе ArchiveScript должно быть таким:

def makeArchive ( params ) <
makeArchiveInternal params. sourcePath , params. destPath , params. deleteSource
>

Если применить преобразование и для других вызовов, то в конечном итоге наш скрипт мог бы выглядеть так:

if ( checkState ( ‘SomeData’ ) ) <
makeArchive sourcePath: ‘c:/1/*.*’ ,
destPath: ‘c:/testarch.zip’ ,
deleteSource: true
> else <
//doAnotherLogic
Return
>
sendNotify to: ‘aaa@gdsl.ru’ , content: ‘сообщение’

И это уже не слишком сложный и достаточно читаемый код.

Таким образом, мы получили свой мини-DSL с несколькими предопределенными функциями, специфичными для нашей задачи. А также все еще имеем возможность использовать всю мощь исходного языка.

Замечу, что я рассмотрел лишь малую толику разработки DSL. В Groovy есть более широкая поддержка разработки своего DSL, а так же DSL-поддержка для Eclipse и для IntelliJ Idea

Тестирование

Хотелось бы сказать несколько слов о тестировании сценариев. Каким бы простым не был скрипт, ошибки могут быть и в нем. Даже если Вы пишите сценарии в IDE, полноценной проверки на корректность синтаксиса Вы можете не получить. Это возможно лишь при его выполнении. Так же необходимо проверять поведение скрипта.
Так как нам не хотелось бы выполнять реальные действия при выполнении тестирования сценария, то нужно каким-то образом заменить реальную логику на имитацию. Groovy позволяет нам это сделать многими путями. Покажу несколько из них.

Замена базового скрипта

Создаем новый класс ArchiveSciptMock который имеет интерфейс аналогичный ArchiveScript, и реализующий нужное нам поведение (или ничего не делающий). При создании объекта конфигурации CompilerConfiguration передаем его имя вместо оригинала.

CompilerConfiguration conf = new CompilerConfiguration ( ) ;
conf. setScriptBaseClass ( «package.ArchiveScriptMock» ) ;

Замена методов в базовом классе скрипта

Другим вариантом без создания дополнительного mock класса может быть замена методов на mock в самом ArchiveScript. В groovy это можно сделать, например, таким способом:

Недостатком и первого и второго способа я бы считал необходимость написания дублирующей логики по проверки правильности передаваемых параметров. Так как если в ArchiveScriptMock метод makeArchive будет таким:

def makeArchive ( params ) <
//makeArchiveInternal params.sourcePath, params.destPath, params.deleteSource
>

То мы не проверим все ли параметры были переданы. Нужно будет писать что-то похожее на это:

def makeArchive ( params ) <
makeArchiveInternalMock params. sourcePath , params. destPath , params. deleteSource
>

Я бы предложил сделать небольшой рефакторинг ArchiveScript — сделать ArchiveScript фасадом и всю логику перенести в другой класс. Например, в Java класс Archive.
Рефакторинг — не только для целей тестирования, но и из других соображений, например, для отделения поведения от способа выполнения (нет зависимости от Script). В итоге, после изменения, ArchiveScript примет такой вид:

abstract class ArchiveScript extends Script <
Archive arc = new Archive ( )
def makeArchive ( params ) <
arc. archive params. sourcePath ,params. destPath , params. deleteSource
>

Теперь, можно тестировать логику и сценарий в отдельности. Заменим Archive на его mock и выполним скрипт:

//заменяем и выполняем скрипт так
Archive. metaClass . with ( mockedMethods )
runScript ( )

//или так
StubFor stub = new StubFor ( Archive ) ;
stub. demand . with <
mockedMethods
>
stub. use <
runScript ( )
>

Естественно поведение Archive можно заменить и с помощью java mock фреймворков, однако, нам пока достаточно и этого.

Итог

Я считаю, что получил достаточно гибкий инструмент для написания сценариев, без недостатков, озвученных в начале текста, а также достаточно простотой в использовании. Контроль корректности сценария так же не был потерян — использование mock поведения позволяет нам тестировать их в достаточной мере, перед реальным выполнением.

Проект с исходными кодами проекта — groovydsl. Компилируется Gradle через враппер.

Некоторые идеи взяты из книги Groovy for Domain-Specific Languiages, Fergal Dearle, 2010

Источник

Читайте также:  база данных
Оцените статью