Статьи

Начало работы: использование Apache Camel с помощью Groovy

Со своего сайта говорится, что Apache Camel — это универсальная интегрированная среда с открытым исходным кодом, основанная на известных корпоративных шаблонах интеграции. Это может показаться туманным определением, но я хочу сказать вам, что это очень продуктивная библиотека Java, которая может решить многие типичные ИТ-проблемы! Вы можете думать об этом как об очень легкой структуре ESB с включенными «батареями».

В каждой работе, на которой я был до сих пор, люди так или иначе пишут свои собственные решения для решения многих общих проблем (или они покупают очень дорогие корпоративные ESB-серверы, на изучение, настройку и обслуживание которых уходят месяцы и месяцы). ). Вещи, которые мы обычно решаем, — это интеграция (склеивание) кода существующих бизнес-сервисов вместе, обработка данных определенным образом, или перемещение и преобразование данных из одного места в другое и т. Д. Это очень типичная потребность во многих ИТ-средах. Apache Camel может использоваться в подобных случаях; не только это, но и очень продуктивно и эффективно!

В этой статье я покажу вам, как начать работу с Apache Camel вместе с несколькими строками скрипта Groovy . Конечно, вы также можете начать с полноценного Java-проекта, чтобы опробовать Camel, но я считаю, что Groovy даст вам самый короткий рабочий пример и кривую обучения.

Начало работы с Apache Camel с использованием Groovy

Итак, начнем. Сначала давайте посмотрим демо «Привет, мир!» С Camel + Groovy.

@Grab('org.apache.camel:camel-core:2.10.0')
@Grab('org.slf4j:slf4j-simple:1.6.6')
import org.apache.camel.*
import org.apache.camel.impl.*
import org.apache.camel.builder.*

def camelContext = new DefaultCamelContext()
camelContext.addRoutes(new RouteBuilder() {
    def void configure() {
        from("timer://jdkTimer?period=3000")
            .to("log://camelLogger?level=INFO")
    }
})
camelContext.start()

addShutdownHook{ camelContext.stop() }
synchronized(this){ this.wait() }

 

Сохраните выше в файл с именем helloCamel.groovy, а затем запустите его так:

$ groovy helloCamel.groovy
388 [main] INFO org.apache.camel.impl.DefaultCamelContext - Apache Camel 2.10.0 (CamelContext: camel-1) is starting
445 [main] INFO org.apache.camel.management.ManagementStrategyFactory - JMX enabled.
447 [main] INFO org.apache.camel.management.DefaultManagementLifecycleStrategy - StatisticsLevel at All so enabling load performance statistics
678 [main] INFO org.apache.camel.impl.converter.DefaultTypeConverter - Loaded 170 type converters
882 [main] INFO org.apache.camel.impl.DefaultCamelContext - Route: route1 started and consuming from: Endpoint[timer://jdkTimer?period=3000]
883 [main] INFO org.apache.camel.impl.DefaultCamelContext - Total 1 routes, of which 1 is started.
887 [main] INFO org.apache.camel.impl.DefaultCamelContext - Apache Camel 2.10.0 (CamelContext: camel-1) started in 0.496 seconds
898 [Camel (camel-1) thread #1 - timer://jdkTimer] INFO camelLogger - Exchange[ExchangePattern:InOnly, BodyType:null, Body:[Body is null]]
3884 [Camel (camel-1) thread #1 - timer://jdkTimer] INFO camelLogger - Exchange[ExchangePattern:InOnly, BodyType:null, Body:[Body is null]]
6884 [Camel (camel-1) thread #1 - timer://jdkTimer] INFO camelLogger - Exchange[ExchangePattern:InOnly, BodyType:null, Body:[Body is null]]
...

 

Небольшой сценарий выше прост, но в нем представлено несколько ключевых особенностей Camel Groovyness. Первый и последний раздел скрипта helloCamel.groovy — это просто отличные персонажи Groovy. Аннотация @Grab автоматически загрузит указанные вами банки зависимостей. Мы импортируем пакеты Java, чтобы использовать их классы позже. В конце мы завершим работу над Camel перед выходом из JVM через механизм Java Shutdown Hook. Программа будет сидеть и ждать, пока пользователь не нажмет CTRL + C, как обычное поведение процесса на сервере.

В средней части находится действие «Верблюд». Вы всегда должны создать контекст Camel для начала (представьте, что он сервер или менеджер для процесса.) А затем вы добавите маршрут Camel (представьте, что это рабочий или конвейерный поток), который вам нравится обрабатывать данные (любит Camel). называть эти данные «сообщения»). Маршрут состоит из начальной точки «от» (где генерируются данные) и одной или нескольких точек «до» (где будут обрабатываться данные). Верблюд называет эти пункты назначения «конечными точками». Эти конечные точки могут быть выражены в простом формате строки URI, таком как «timer: // jdkTimer? Period = 3000». Здесь мы генерируем сообщение таймера каждые 3 секунды в потоке потока, а затем обрабатываем с помощью URI регистратора, который просто выводит на консольный вывод.

После запуска контекста Camel он начнет обрабатывать данные в рабочем процессе, как вы можете видеть из приведенного выше примера вывода. Теперь попробуйте нажать CTRL + C, чтобы завершить процесс. Обратите внимание, как Верблюд очень изящно отключит все.

7312 [Thread-2] INFO org.apache.camel.impl.DefaultCamelContext - Apache Camel 2.10.0 (CamelContext: camel-1) is shutting down
7312 [Thread-2] INFO org.apache.camel.impl.DefaultShutdownStrategy - Starting to graceful shutdown 1 routes (timeout 300 seconds)
7317 [Camel (camel-1) thread #2 - ShutdownTask] INFO org.apache.camel.impl.DefaultShutdownStrategy - Route: route1 shutdown complete, was consuming from: Endpoint[timer://jdkTimer?period=3000]
7317 [Thread-2] INFO org.apache.camel.impl.DefaultShutdownStrategy - Graceful shutdown of 1 routes completed in 0 seconds
7321 [Thread-2] INFO org.apache.camel.impl.converter.DefaultTypeConverter - TypeConverterRegistry utilization[attempts=2, hits=2, misses=0, failures=0] mappings[total=170, misses=0]
7322 [Thread-2] INFO org.apache.camel.impl.DefaultCamelContext - Apache Camel 2.10.0 (CamelContext: camel-1) is shutdown in 0.010 seconds. Uptime 7.053 seconds.

Так что это наш первый вкус езды на верблюде! Тем не менее, мы назвали этот раздел «Hello World!» демо, а еще мы не видели ни одного. Но вы, возможно, также заметили, что вышеприведенный скрипт — это в основном код, который мы установили. Пользовательская логика еще не была добавлена. Даже не запись части сообщения! Мы просто настраиваем маршрут.

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

@Grab('org.apache.camel:camel-core:2.10.0')
@Grab('org.slf4j:slf4j-simple:1.6.6')
import org.apache.camel.*
import org.apache.camel.impl.*
import org.apache.camel.builder.*

def camelContext = new DefaultCamelContext()
camelContext.addRoutes(new RouteBuilder() {
    def void configure() {
        from("timer://jdkTimer?period=3000")
            .to("log://camelLogger?level=INFO")
            .process(new Processor() {
                def void process(Exchange exchange) {
                    println("Hello World!")
                }
            })
    }
})
camelContext.start()

addShutdownHook{ camelContext.stop() }
synchronized(this){ this.wait() }

 

Notice how I can simply append the process code part right after the to(«log…») line. I have added a «processor» code block to process the timer message. The logic is simple: we greet the world on each tick.

Making Camel route more concise and practical

Now, do I have you at Hello yet? If not, then I hope you will be patient and continue to follow along for few more practical features of Camel. First, if you were to put Camel in real use, I would recommend you setup your business logic separately from the workflow route definition. This is so that you can clearly express and see your entire pipeflow of route at a glance. To do this, you want to move the «processor», into a service bean.

@Grab('org.apache.camel:camel-core:2.10.0')
@Grab('org.slf4j:slf4j-simple:1.6.6')
import org.apache.camel.*
import org.apache.camel.impl.*
import org.apache.camel.builder.*
import org.apache.camel.util.jndi.*

class SystemInfoService {
    def void run() {
        println("Hello World!")
    }
}
def jndiContext = new JndiContext();
jndiContext.bind("systemInfoPoller", new SystemInfoService())

def camelContext = new DefaultCamelContext(jndiContext)
camelContext.addRoutes(new RouteBuilder() {
    def void configure() {
        from("timer://jdkTimer?period=3000")
            .to("log://camelLogger?level=INFO")
            .to("bean://systemInfoPoller?method=run")
    }
})
camelContext.start()

addShutdownHook{ camelContext.stop() }
synchronized(this){ this.wait() }

 

Now, see how compact this workflow route has become? The Camel’s Java DSL such as «from().to().to()» for defining route are so clean and simple to use. You can even show this code snip to your Business Analysts, and they would likely be able to verify your business flow easily! Wouldn’t that alone worth a million dollars?

How about another demo: FilePoller Processing

File polling processing is a very common and effective way to solve many business problems. If you work for commercial companies long enough, you might have written one before. A typical file poller would process incoming files from a directory and then process the content, and then move the file into a output directory. Let’s make a Camel route to do just that.

@Grab('org.apache.camel:camel-core:2.10.0')
@Grab('org.slf4j:slf4j-simple:1.6.6')
import org.apache.camel.*
import org.apache.camel.impl.*
import org.apache.camel.builder.*
import org.apache.camel.util.jndi.*

class UpperCaseTextService {
    def String transform(String text) {
        return text.toUpperCase()
    }
}
def jndiContext = new JndiContext();
jndiContext.bind("upperCaseTextService", new UpperCaseTextService())

def dataDir = "/${System.properties['user.home']}/test/file-poller-demo"
def camelContext = new DefaultCamelContext(jndiContext)
camelContext.addRoutes(new RouteBuilder() {
    def void configure() {
        from("file://${dataDir}/in")
            .to("log://camelLogger")
            .to("bean://upperCaseTextService?method=transform")
            .to("file://${dataDir}/out")
    }
})
camelContext.start()

addShutdownHook{ camelContext.stop() }
synchronized(this){ this.wait() }

 

Here you see I defined a route to poll a $HOME/test/file-poller-demo/in directory for text files. Once it’s found it will log it to console, and then process by a service that transform the content text into upper case. After this, it will send the file into $HOME/test/file-poller-demo/out directory. My goodness, reading the Camel route above probably express what I wrote down just as effective. Do you see the benefits here?

What’s the «batteries» included part.

If you’ve used Python programming before, you might have heard the pharase that they claim often: Python has «batteries» included. This means their interpreter comes with a rich of libaries for most of the common programming need. You can often write python program without have to download separated external libraries.

I am making similar analogies here with Apache Camel. The Camel project comes with so many ready to use components that you can find just about any transport protocals that can carry data. These Camel «components» are ones that support different ‘Endpoint URI’ that we have seen in our demos above. We have simply shown you timer, log, bean, and file components, but there are over 120 more. You will find jms, http, ftp, cfx, or tcp just to name a few.

The Camel project also has an option for you to define route in declarative xml format. The xml is just an extension of a Spring xml config with Camel’s namespace handler added on top. Spring is optional in Camel, but you can use it together in a very powerful way.