2024年12月10日火曜日

Camel Karaf から Camel Spring Boot もしくは Camel Quarkus への移行

 原文: Migrating from Camel Karaf to Camel Spring Boot or Quarkus By Claus lbsen(@davsclaus) Dec 9, 2024

この記事は、Camel 4 への移行支援に関するブログシリーズ第2段です。
第1段の記事は、移行に関する一般原則に注目して書かれています。
今回の記事は、Apache Karaf OSGi Blueprint から Camel 4 への移行に注目しています。


移行作業


Camel Karaf および OSGi Blueprint から Camel 4 への移行は、些細な移行作業ではありません。
移行は、以下の主要な作業から構成されます:
  1. Camel 2.x/3.x から Camel 4.x へのアップグレード
  2. Java 17 か 21 へのアップグレード
  3. Kafka(OSGi) と Spring Boot、Quarkus、Standalone Camel などへの置き換え
  4. あたなの Camel インテグレーションの移行
はじめはこれらの作業量を膨大に感じるかもしれませんが、慌てないでください。


Camel Karaf から Camel 4 への移行


came-karaf ベースの Camel インテグレーションを移行するには、以下の移行が必要です:
  1. Javaコードを、Camel 3.x/3.x から Camel 4.x へ
  2. OSGi Blueprint XML ファイルを、Camel 4 XML/YAML DSL へ 
移行作業を支援する為、camel-jbang にサポート機能を組み込みました。
camel-kafka の例を使い、どのような作業が必要かご紹介します。


最初の例題を移行


最初に、例題 camel-example-sql-blueprint がどういったものか確認します。この例題は、カスタム Java ソースと OSGi blueprint XML に Camel ルートが記述されています。

どれほど状況が悪いか簡単に確認する為、camel-jbang を使い例題を実行し、実行結果を確認します:

cd examples/camel-example-sql-blueprint
camel run pom.xml

camel-jbangpom.xml を使い、既存 Mavenプロジェクトにて Camel がベストエフォートで実行するようなっています。この方法は、Mavenや Camelの実行方法の代替にはなりません。しかし、移行作業においては有用です。

例題を実行した際、何もエラーが出力されず動作することに気がつくでしょう。
何が起きたかというと、camel-jbang が OSGi blueprint XML ファイルを読み込むことができ、<bean><camelContext> を解析し、モダンな Camel 4 上で実行しています。これは、例題の移行作業が最小限であることを意味します。


Blueprint XML ファイルの移行


OSGi Blueprint XML ファイルを、XML か YAML DSL へ移行する必要があります。以下のように camel-jbangtransform コマンドで実施可能です:

camel transform route pom.xml

UI Designer


YAML DSL は、以下のスクリーンショットにある Apache Camel Karavan や Kaoto といった Camel designer で編集可能です。


XML へダンプ


この例題の移行には XMLを使うので、以下のように --format=xml を指定します:

camel transform route pom.xml --format=xml


ダンプをファイルへ書き出す


コンソールへプリントするのではなく、camel transform route コマンドを使いファイルやディレクトリーへ書き出すこともできます。

camel transform route pom.xml --format=xml --output=code

このコマンドで、code ディレクトリー配下に移行されたファイルが出力されます。
camel transform route --help を使い、このコマンドの詳細についてご確認ください。
次の移行作業は、生成されたファイルをご利用予定の runtime (Spring Boot、Quarkus、Camel Main など)へ出力します。

重要: 出力機能は、1回で移行および変換作業をサポートしません。つまり、変換された OSGi Blueprint ファイルを、適切なディレクトリーへ手動でコピーする必要があります。


Spring Boot へのエクスポート


以下のように、Spring Boot へエクスポートできます:

camel export pom.xml --gav=com.mycompany:myproject:1.0 --runtime=spring-boot --dir=code

Quarkus へのエクスポートはこのようになります:

camel export pom.xml --gav=com.mycompany:myproject:1.0 --runtime=quarkus --dir=code


移行の完了


エクスポート機能は Camel Karafベースのプロジェクトをフルサポートしていませんので、エクスポート終了後、プロジェクトを少し掃除する必要があります。

cd code

変換したルート(前回の作業)を、src/main/resources/camel ディレクトリー配下へコピーし、古い OSGi blueprint ファイルを削除する必要があるでしょう。
PS: 変換作業を忘れた場合、変換をやり直して以下のようなフォルダーへ直接出力できます:

cd ..
camel transform route pom.xml --format=xml --output=code/src/main/resources/camel
cd code

また、karaf feature.xml ファイルも削除すべきです。

その他、エクスポート ツールは application.properties で定義されている値のみをサポートするため、sql.properties から値をコピーする必要があります。つまり、値を sql.properties から code/src/main/resources/application.properties にコピーします。

Spring Boot で実行する場合は、 Web サーバーを使用するのがベスト プラクティスであるため、次のように pom.xml に依存関係を追加する必要があります:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

前述した作業後、プロジェクトをコンパイルし Spring Boot を実行できます:

mvn package spring-boot:run

アプリケーションは Spring Boot 上で実行し、無事に移行できました。

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.4.0)

2024-12-04T08:24:19.113+01:00  INFO 45359 --- [           main] c.mycompany.myproject.CamelApplication   : Starting CamelApplication using Java 17.0.11 with PID 45359 (/Users/davsclaus/workspace/camel-karaf-examples/examples/camel-example-sql-blueprint/code/target/classes started by davsclaus in /Users/davsclaus/workspace/camel-karaf-examples/examples/camel-example-sql-blueprint/code)
2024-12-04T08:24:19.114+01:00  INFO 45359 --- [           main] c.mycompany.myproject.CamelApplication   : No active profile set, falling back to 1 default profile: "default"
2024-12-04T08:24:19.679+01:00  INFO 45359 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2024-12-04T08:24:19.685+01:00  INFO 45359 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-12-04T08:24:19.685+01:00  INFO 45359 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.33]
2024-12-04T08:24:19.708+01:00  INFO 45359 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-12-04T08:24:19.709+01:00  INFO 45359 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 574 ms
2024-12-04T08:24:19.897+01:00  WARN 45359 --- [           main] o.a.c.i.e.DefaultCamelBeanPostProcessor  : No CamelContext defined yet so cannot inject into bean: startupConditionStrategy
2024-12-04T08:24:20.160+01:00  INFO 45359 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 1 endpoint beneath base path '/actuator'
2024-12-04T08:24:20.231+01:00  INFO 45359 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
2024-12-04T08:24:20.289+01:00  INFO 45359 --- [           main] o.apache.camel.example.sql.DatabaseBean  : Creating table orders ...
2024-12-04T08:24:20.509+01:00  INFO 45359 --- [           main] o.apache.camel.example.sql.DatabaseBean  : ... created table orders
2024-12-04T08:24:20.613+01:00  INFO 45359 --- [           main] o.a.c.impl.engine.AbstractCamelContext   : Apache Camel 4.9.0 (camel-example-sql-blueprint) is starting
2024-12-04T08:24:20.619+01:00  INFO 45359 --- [           main] o.a.c.impl.engine.AbstractCamelContext   : Routes startup (total:2)
2024-12-04T08:24:20.619+01:00  INFO 45359 --- [           main] o.a.c.impl.engine.AbstractCamelContext   :     Started generateOrder-route (timer://foo)
2024-12-04T08:24:20.619+01:00  INFO 45359 --- [           main] o.a.c.impl.engine.AbstractCamelContext   :     Started processOrder-route (sql://select%20*%20from%20orders%20where%20processed%20=%20false)
2024-12-04T08:24:20.619+01:00  INFO 45359 --- [           main] o.a.c.impl.engine.AbstractCamelContext   : Apache Camel 4.9.0 (camel-example-sql-blueprint) started in 6ms (build:0ms init:0ms start:6ms boot:715ms)
2024-12-04T08:24:20.620+01:00  INFO 45359 --- [           main] c.mycompany.myproject.CamelApplication   : Started CamelApplication in 1.653 seconds (process running for 1.782)
2024-12-04T08:24:21.685+01:00  INFO 45359 --- [2 - timer://foo] generateOrder-route                      : Inserted new order 0
2024-12-04T08:24:22.187+01:00  INFO 45359 --- [sed%20=%20false] processOrder-route                       : Processed order id 0 item 222 of 10 copies of ActiveMQ in Action

それでは、次にもう少し複雑で追加作業が必要な例題を移行します。


より困難な移行


今回は、camel-example-openapi-osgi example を移行します。DSLの修正も必要になります。
まずはこちらを実行しましょう:

cd camel-example-openapi-osgi
camel run pom.xml

実行すると、次のようなエラーが表示されます。

org.apache.camel.xml.io.XmlPullParserLocationException: Unexpected attribute '{}apiContextListing'
in file:src/main/resources/OSGI-INF/blueprint/camel.xml, line 42, column 42:
                       enableCORS="true">
                                        ^

Camel 4 への移行には DSL の変更が必要になる可能性があるため、これは予想されます。この例では、apiContextListing が削除されているため、src/main/resources/OSGI-INF/blueprint/camel.xml ファイル内の行を手動で修正する必要があります:

-                        apiContextPath="api-docs" apiContextListing="true"
+                        apiContextPath="api-docs"

次に、camel run pom.xml を使用して再度実行してみます。
次のエラーは、description に関するものです:

org.apache.camel.xml.io.XmlPullParserLocationException: Unexpected element '{http://www.osgi.org/xmlns/blueprint/v1.0.0}description'
in file:src/main/resources/OSGI-INF/blueprint/camel.xml, line 58, column 20:
      <description>User rest service</description>

<description> は属性に移行する必要があります。したがって、src/main/resources/OSGI-INF/blueprint/camel.xml 内のすべての <description> に対して修正を行う必要があります。この後、再度実行すると、別のエラーが発生します。

org.apache.camel.xml.io.XmlPullParserLocationException: Unexpected attribute '{}uri'
in file:src/main/resources/OSGI-INF/blueprint/camel.xml, line 60, column 99:
            <get uri="/{id}" outType="org.apache.camel.example.rest.User" description="Find user by id">

GET、PUT、POST などのすべての REST 動詞については、次に示すように uri 属性の名前を path に変更する必要があります:

-       <get uri="/{id}" outType="org.apache.camel.example.rest.User" description="Find user by id">
+       <get path="/{id}" outType="org.apache.camel.example.rest.User" description="Find user by id">

もうすぐそこまで来ましたが、最後のエラーが 1 つあります。

org.apache.camel.xml.io.XmlPullParserLocationException: Unexpected element '{http://www.osgi.org/xmlns/blueprint/v1.0.0}route'
in file:src/main/resources/OSGI-INF/blueprint/camel.xml, line 64, column 16:
        <route>

Rest DSL では、インライン化された <route> はサポートされなくなり、これらのインライン化されたルートを別のルートに移動し、direct エンドポイント経由で呼び出す必要があります。

修正前:

<get path="/{id}" outType="org.apache.camel.example.rest.User" description="Find user by id">
    <param name="id" type="path" description="The id of the user to get" dataType="integer"/>
    <responseMessage message="The user that was found"/>
    <responseMessage code="404" message="User not found"/>
    <route>
        <to uri="bean:userService?method=getUser(${header.id})"/>
        <filter>
            <simple>${body} == null</simple>
            <setHeader name="Exchange.HTTP_RESPONSE_CODE">
                <constant>404</constant>
            </setHeader>
        </filter>
    </route>
</get>

修正後:

<get path="/{id}" outType="org.apache.camel.example.rest.User" description="Find user by id">
    <param name="id" type="path" description="The id of the user to get" dataType="integer"/>
    <responseMessage message="The user that was found"/>
    <responseMessage code="404" message="User not found"/>
    <to uri="direct:getUser"/>
</get>

<route>
    <from uri="direct:getUser"/>
    <to uri="bean:userService?method=getUser(${header.id})"/>
    <filter>
        <simple>${body} == null</simple>
        <setHeader name="Exchange.HTTP_RESPONSE_CODE">
            <constant>404</constant>
        </setHeader>
    </filter>
</route>

ついにアプリケーションが  Camel 4 で実行できるようになりました。

$ camel run pom.xml
2024-12-04 08:43:40.809  INFO 45721 --- [           main] org.apache.camel.main.MainSupport        : Apache Camel (JBang) 4.9.0 is starting
2024-12-04 08:43:40.917  INFO 45721 --- [           main] org.apache.camel.main.MainSupport        : Using Java 17.0.11 with PID 45721. Started by davsclaus in /Users/davsclaus/workspace/camel-karaf-examples/examples/camel-example-openapi-osgi
2024-12-04 08:43:43.599  INFO 45721 --- [           main] .main.download.MavenDependencyDownloader : Resolved: org.apache.camel:camel-spring:4.9.0 (took: 2s678ms)
2024-12-04 08:43:47.734  INFO 45721 --- [           main] .main.download.MavenDependencyDownloader : Resolved: org.apache.camel:camel-openapi-java:4.9.0 (took: 2s219ms)
2024-12-04 08:43:47.761  INFO 45721 --- [           main] org.apache.camel.main.BaseMainSupport    : Properties location: file:src/main/resources/log4j2.properties
2024-12-04 08:43:47.794  INFO 45721 --- [           main] org.apache.camel.main.ProfileConfigurer  : The application is starting with profile: dev
2024-12-04 08:43:53.110  INFO 45721 --- [           main] .main.download.MavenDependencyDownloader : Resolved: org.apache.camel:camel-xml-io-dsl:4.9.0 (took: 2s79ms)
2024-12-04 08:43:53.540  INFO 45721 --- [           main] n.xml.blueprint.BlueprintXmlBeansHandler : Discovered 1 OSGi  XML beans
2024-12-04 08:43:53.649  INFO 45721 --- [           main] he.camel.cli.connector.LocalCliConnector : Camel JBang CLI enabled
2024-12-04 08:43:54.710  INFO 45721 --- [           main] e.camel.impl.engine.AbstractCamelContext : Apache Camel 4.9.0 (myCamel) is starting
2024-12-04 08:43:54.982  INFO 45721 --- [           main] org.eclipse.jetty.server.Server          : jetty-12.0.15; built: 2024-11-05T19:44:57.623Z; git: 8281ae9740d4b4225e8166cc476bad237c70213a; jvm 17.0.11+9-LTS
2024-12-04 08:43:55.001  INFO 45721 --- [           main] ipse.jetty.server.handler.ContextHandler : Started oeje10s.ServletContextHandler@10a064bd{ROOT,/,b=null,a=AVAILABLE,h=oeje10s.ServletHandler@2de7fe0e{STARTED}}
2024-12-04 08:43:55.003  INFO 45721 --- [           main] jetty.ee10.servlet.ServletContextHandler : Started oeje10s.ServletContextHandler@10a064bd{ROOT,/,b=null,a=AVAILABLE,h=oeje10s.ServletHandler@2de7fe0e{STARTED}}
2024-12-04 08:43:55.010  INFO 45721 --- [           main] g.eclipse.jetty.server.AbstractConnector : Started ServerConnector@26f11b40{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
2024-12-04 08:43:55.011  INFO 45721 --- [           main] org.eclipse.jetty.server.Server          : Started oejs.Server@3883b5e9{STARTING}[12.0.15,sto=0] @15508ms
2024-12-04 08:43:55.092  INFO 45721 --- [           main] e.camel.impl.engine.AbstractCamelContext : Routes startup (total:3 rest-dsl:2)
2024-12-04 08:43:55.092  INFO 45721 --- [           main] e.camel.impl.engine.AbstractCamelContext :     Started route2 (direct://getUser)
2024-12-04 08:43:55.092  INFO 45721 --- [           main] e.camel.impl.engine.AbstractCamelContext :     Started route1 (rest-api://api-docs)
2024-12-04 08:43:55.092  INFO 45721 --- [           main] e.camel.impl.engine.AbstractCamelContext :     Started route3 (rest://get:/echo:/ping)
2024-12-04 08:43:55.092  INFO 45721 --- [           main] e.camel.impl.engine.AbstractCamelContext : Apache Camel 4.9.0 (myCamel) started in 381ms (build:0ms init:0ms start:381ms boot:13s900ms)


Camel Quarkus への移行


この例題では、Spring Boot ではなく Camel Quarkus に移行します。

camel export pom.xml --gav=com.mycompany:myproject:1.0 --runtime=quarkus --dir=code

異なるエラーが出力されます:

Generating fresh run data
Running using Quarkus v3.17.2 (preparing and downloading files)
org.apache.camel.FailedToStartRouteException: Failed to start route route3 because of Component jetty is not a RestConsumerFactory

これは、古い Karaf の例が、Rest DSL サービスをホストするための HTTP サーバーとして Jetty を使用しているからです。これを削除し、Quarkus (または Spring Boot) に付属するデフォルトの platform-http を Camel で使用できるようにする必要があります。

したがって、次のように XML ファイルを修正します:

<restConfiguration bindingMode="json"
                   contextPath="rest" port="8080"
                   apiContextPath="api-docs"
                   enableCORS="true"
                   inlineRoutes="false">

重要: Rest DSL を使用する場合、Camel JBang がルートを変換できるようにルートを分離する必要があるため、inlineRoutes="false" を設定する必要があります。この問題は、Camel 4.10 以降では自動的に修正される可能性があります。

export を再度実行します。
次に、Blueprint XML ファイルを移行し、以下のように src/main/resources/camel ディレクトリーへ直接出力します:

camel transform route pom.xml --format=xml --output=code/src/main/resources/camel

次に、Camel 4 on Quarkus へ完全に移行するため、プロジェクトを掃除する必要があります。
pom.xml で、camel-jettyCamel-spring の依存関係を削除します。
また、src/main/resources/camel.xml ファイルに <restConfiguration> を追加する必要があります。これらは、camel transform route コマンドに自動で含まれないためです。
よって、ファイル先頭の <camel> ルート タグ配下に、これを追加します:

<restConfiguration bindingMode="json"
                   contextPath="rest" port="8080"
                   apiContextPath="api-docs"
                   enableCORS="true"
                   inlineRoutes="false">

    <!-- we want json output in pretty mode -->
    <dataFormatProperty key="prettyPrint" value="true"/>

    <!-- setup openapi api descriptions -->
    <apiProperty key="base.path" value="rest"/>
    <apiProperty key="api.version" value="1.2.3"/>
    <apiProperty key="api.title" value="User Services"/>
    <apiProperty key="api.description" value="Camel Rest Example with OpenApi that provides an User REST service"/>
    <apiProperty key="api.contact.name" value="The Apache Camel team"/>

</restConfiguration>

この作業後、次のように移行したサンプルをコンパイルし、 Camel Quarkus で実行できるようになります:

$ mvn package quarkus:run
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2024-12-04 11:09:03,974 INFO  [org.apa.cam.qua.cor.CamelBootstrapRecorder] (main) Apache Camel Quarkus 3.16.0 is starting
2024-12-04 11:09:03,979 INFO  [org.apa.cam.mai.MainSupport] (main) Apache Camel (Main) 4.8.1 is starting
2024-12-04 11:09:04,066 INFO  [org.apa.cam.mai.BaseMainSupport] (main) Auto-configuration summary
2024-12-04 11:09:04,066 INFO  [org.apa.cam.mai.BaseMainSupport] (main)     [MicroProfilePropertiesSource] camel.main.routesIncludePattern = camel/camel.xml
2024-12-04 11:09:04,066 INFO  [org.apa.cam.mai.BaseMainSupport] (main)     [MicroProfilePropertiesSource] camel.main.name = camel-example-openapi-osgi
2024-12-04 11:09:04,158 INFO  [org.apa.cam.imp.eng.AbstractCamelContext] (main) Apache Camel 4.8.1 (camel-example-openapi-osgi) is starting
2024-12-04 11:09:04,463 INFO  [org.apa.cam.imp.eng.AbstractCamelContext] (main) Routes startup (total:5 rest-dsl:4)
2024-12-04 11:09:04,463 INFO  [org.apa.cam.imp.eng.AbstractCamelContext] (main)     Started route2 (direct://getUser)
2024-12-04 11:09:04,463 INFO  [org.apa.cam.imp.eng.AbstractCamelContext] (main)     Started route1 (rest-api://api-docs)
2024-12-04 11:09:04,464 INFO  [org.apa.cam.imp.eng.AbstractCamelContext] (main)     Started route3 (rest://get:/user:/%7Bid%7D)
2024-12-04 11:09:04,464 INFO  [org.apa.cam.imp.eng.AbstractCamelContext] (main)     Started route4 (rest://put:/user)
2024-12-04 11:09:04,464 INFO  [org.apa.cam.imp.eng.AbstractCamelContext] (main)     Started route5 (rest://get:/user:/findAll)
2024-12-04 11:09:04,465 INFO  [org.apa.cam.imp.eng.AbstractCamelContext] (main) Apache Camel 4.8.1 (camel-example-openapi-osgi) started in 305ms (build:0ms init:0ms start:305ms)
2024-12-04 11:09:04,567 INFO  [io.quarkus] (main) myproject 1.0 on JVM (powered by Quarkus 3.17.2) started in 0.989s. Listening on: http://0.0.0.0:8080
2024-12-04 11:09:04,567 INFO  [io.quarkus] (main) Profile prod activated.
2024-12-04 11:09:04,568 INFO  [io.quarkus] (main) Installed features: [camel-attachments, camel-bean, camel-core, camel-direct, camel-jackson, camel-microprofile-health, camel-openapi-java, camel-platform-http, camel-rest, camel-xml-io-dsl, cdi, smallrye-context-propagation, smallrye-health, vertx]

そして、次のような Rest サービスを呼び出すことができるはずです:

curl http://0.0.0.0:8080/rest/user/123
{
  "id" : 123,
  "name" : "John Doe"
}

ふー、これは少し大変でした。


まとめ


Camel JBang ツールは、OSGi Blueprint XML ファイルを最新の Camel へ移行するのに役立つツールです。特殊な OSGi 機能を使用するより複雑な OSGi アプリケーションを構築している場合は、手動で移行する必要があります。

Camel 4 では、従来の Apache Karaf よりもはるかに大きなコミュニティとユーザー ベースを持つ Spring Boot または Quarkus の最新スタック上で動作するので、将来のアップグレードは、はるかに簡単になります。

Rest DSL から YAML DSL への変換はまだサポートされていないことに注意してください。たとえば、Rest を使用して移行した最後の例では、YAML DSL の REST 要素を自動生成することはできません。これは将来的に実装される予定です。

詳細については、Camel 3.x/4.x の移行およびアップグレード ガイドを参照してください。移行に関するほとんどの問題は、ガイド (上記) に記載されています。

Camel JBang ツールは、このブログ投稿に示されているように、既存プロジェクトの移行をすぐ試すのに役立ちます。

移行ツールに関するフィードバックは大歓迎です。頑張ってください。モダンな Camel 4 の世界で会いましょう。

0 件のコメント:

コメントを投稿