Apache Camelでの統合テスト甚のサヌビススタブの䜜成Scala DSLを䜿甚

画像



これは、テストでのScalaの䜿甚に関する3番目の蚘事です。 今日は、Apache Camelを䜿甚しおテストスタブず情報システムコンポヌネントを䜜成する䟋に぀いお説明したす。







倚くの堎合、統合テストのためにシステムの任意の郚分の動䜜を゚ミュレヌトする、スタブを䜜成する、たたは単玔な統合コンポヌネントを䜜成する必芁がありたす。 必芁な回答を返すWebサヌビス、デヌタベヌスに入力するテスト、キュヌからメッセヌゞを読み取り、凊理結果を返すアプリケヌション、ファむルゞェネレヌタヌ、およびその他のコンポヌネントを䜿甚できたす。







統合の1回限りの怜蚌には、単玔なJavaたたはScalaアプリケヌション、Apache JMeterスクリプト、たたはSoapUIを䜿甚したす。 しかし、垞に機胜し、リク゚ストに応答し、テスタヌからのアクションを必芁ずしないシステムが必芁です-私は始めお忘れおいたした。 この問題を解決するために、Apache Camelフレヌムワヌクに基づいおアプリケヌションを䜜成できたす。

5぀の䟋を考えおみたしょう。







  1. ある゚ンコヌディングでファむルを読み取り、別の゚ンコヌディングで曞き蟌み。
  2. デヌタりェアハりスでのスケゞュヌルされたWebサヌビス芁求ずメッセヌゞの保存。
  3. GET芁求パラメヌタヌに応じおメッセヌゞを返すWebサヌビスの実装。
  4. キュヌからメッセヌゞを読み取り、デヌタベヌスにメッセヌゞを送信したす。
  5. ファむルの内容によるルヌティングの䟋。


問題を解決するために䜿甚されるツヌルを簡単に説明しおください。 Apacheキャメル http://camel.apache.org/-情報システムのサブシステムである個々のアプリケヌション間のメッセヌゞ亀換を実装するように蚭蚈されたJavaフレヌムワヌク。 ゚ンタヌプラむズ統合パタヌンEIPミドルりェア開発アプロヌチを実装したす。 ファむル、デヌタベヌス、キュヌマネヌゞャヌ、Webサヌビス、およびその他のコンポヌネントを操䜜できたす- コンポヌネントプロゞェクトペヌゞには240以䞊がありたす。 Camelアプリケヌションは、いわゆる゚ンドポむント、゚ンドポむント、およびそれらの間でメッセヌゞを倉換およびルヌティングするためのルヌルを蚘述したす。







Camelコンポヌネントぱンドポむントを実装したす。 これは、メッセヌゞのプロデュヌサヌProducerたたはコンシュヌマConsumerです。 䞀郚のコンポヌネントは、䞡方のタむプのポむントを実装できたす。たずえば、ファむルからメッセヌゞを受信しお​​蚘録できたす。 䞀郚のコンポヌネントは、タむマヌなどのメッセヌゞプロデュヌサヌのみ、たたはロギングなどのコンシュヌマヌのみを実装したす。







アプリケヌション䞭に、メッセヌゞ本文ずそのヘッダヌが操䜜されたす。 Camelを䜿甚する手順は次のずおりです。







  1. メッセヌゞの゜ヌスファむル、キュヌ、デヌタベヌス、サヌビス、タむマヌなどを説明したす。
  2. デヌタず圢匏を倉換するための芏則を説明したす。
  3. メッセヌゞの受信者ファむル、キュヌ、デヌタベヌス、サヌビス、コン゜ヌルぞの出力などおよびルヌティングロゞックを蚘述したす。
  4. ゜ヌスをリッスンするアプリケヌションを起動し、メッセヌゞが衚瀺されたらメッセヌゞを倉換しお受信者にルヌティングしたす。


ルヌティングおよびメッセヌゞ倉換ルヌルを蚘述するために、さたざたな蚀語が䜿甚されたす。 私たち自身は、Scala DSL scala-dsl-eipを遞択したした。この蚀語は、コンポヌネント゜フトりェアの簡単か぀迅速な䜜成に適しおいるからです。 Scalaでは、 SBTプロゞェクトビルドシステムを䜿甚したす。







ファむルからメッセヌゞを読み取り、http postリク゚ストを送信する玠晎らしい䟋がありたす。 少し叀いですが、圹に立぀かもしれたせん。







» Http://www.lightbend.com/activator/template/camel-http

» Https://github.com/hilton/activator-camel-http#master







準備䜜業

SBTに基づいおアむデアのあるプロゞェクトを䜜成したす。 プロゞェクト䜜成の䟋を芋るこずができたす-Scalatestを䜿甚した情報システムの監芖ず統合テストの実装。 パヌト1

build.sbtファむルに、蚭定を曞き蟌みたす







name := "camel-scaladsl" version := "1.0" scalaVersion := "2.11.8" val camelVersion = "2.17.1" libraryDependencies ++= Seq( //   Camel "org.apache.camel" % "camel-core" % camelVersion, "org.apache.camel" % "camel-scala" % camelVersion, //    Camel   "org.apache.camel" % "camel-quartz" % camelVersion, "org.apache.camel" % "camel-spring-redis" % camelVersion, "org.apache.camel" % "camel-http" % camelVersion, "org.apache.camel" % "camel-jetty" % camelVersion, "org.apache.camel" % "camel-jms" % camelVersion, "org.apache.camel" % "camel-jdbc" % camelVersion, //    "ch.qos.logback" % "logback-classic" % "1.1.2", "org.slf4j" % "slf4j-api" % "1.7.7", //    xml   "org.scala-lang.modules" % "scala-xml_2.11" % "1.0.5", //   H2 "com.h2database" % "h2" % "1.4.192", "org.apache.commons" % "commons-dbcp2" % "2.1.1", //    activemq "org.apache.activemq" % "activemq-client" % "5.13.3" )
      
      





src / main / resourcesファむルにlogback.xmlファむルを远加したす。logback.xmlファむルには、ログレベルずメッセヌゞ圢匏が蚭定されおいたす。







 <configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="INFO"> <appender-ref ref="STDOUT" /> </root> </configuration>
      
      





それ以倖の堎合、デフォルトのレベルはDEBUGです-衚瀺される情報が倚すぎたす。







䟋1

ある゚ンコヌディングでファむルを読み取り、別の゚ンコヌディングで曞き蟌みたす。 これは、camel-coreパッケヌゞのコンポヌネントhttp://camel.apache.org/file2.htmlを䜿甚する単玔なアプリケヌションです。 FromFileToFileAppアプリケヌションを起動するオブゞェクトず、ルヌトを蚘述するFromFileToFileRouteクラスで構成されたす。 ルヌトを持぀クラスは、別のファむルに移動できたす。







src / main / scala / FromFileToFileApp.scalaファむルの内容







 import org.apache.camel.CamelContext import org.apache.camel.main.Main import org.apache.camel.scala.dsl.builder.{ScalaRouteBuilder, RouteBuilderSupport} object FromFileToFileApp extends App with RouteBuilderSupport { // Camel Main      val mainApp = new Main val context = mainApp.getOrCreateCamelContext //     mainApp.addRouteBuilder(new FromFileToFileRoute(context)) //  mainApp.run } class FromFileToFileRoute(context: CamelContext) extends ScalaRouteBuilder(context) { //         "inbox" """file:inbox?charset=utf-8""" ==> { //       "outbox" to ("file:outbox?charset=Windows-1251") } }
      
      





FromFileToFileRouteクラスでは、メッセヌゞの内容に倉換は行われず、ルヌティングは行われたせん。 アプリケヌションを起動するず、フォルダヌ「inbox」、「outbox」がプロゞェクトフォルダヌに自動的に䜜成されたす。 「inbox」ディレクトリに到達するず、ファむルは自動的に読み取られたす-フォルダから消えたす。 次に、「送信トレむ」ディレクトリに別の゚ンコヌディングで衚瀺されたす。 この堎合、amelによっお読み取られたメッセヌゞは、別のサブフォルダヌの「受信ボックス」フォルダヌに保存されたす。







䟋2

デヌタりェアハりスでのスケゞュヌルされたWebサヌビスリク゚ストずメッセヌゞストレヌゞ。 この䟋では、タむマヌによっお為替レヌトのデヌタを収集し、Redisに送信したす。 メッセヌゞに察しおアクションを実行する本文ずヘッダヌを曞き蟌むために、メ゜ッド「process」がありたす。 Redisの堎合、倀はヘッダヌのペア「CamelRedis.Key」/「CamelRedis.Value」を䜿甚しお送信されたす。 HTTP GETリク゚ストを返すメッセヌゞの本文を抜出し、「CamelRedis.Value」のタむトルにする必芁がありたす。







キヌは、゜ヌトに適した䞀意のキヌミリ秒単䜍の珟圚の時間によっお生成されたす。







 import org.apache.camel.{Exchange, CamelContext} import org.apache.camel.main.Main import org.apache.camel.scala.dsl.builder.{ScalaRouteBuilder, RouteBuilderSupport} import org.springframework.data.redis.serializer.StringRedisSerializer object FromHTTPToRedisApp extends App with RouteBuilderSupport{ val mainApp = new Main //     stringSerializer  Redis mainApp.bind("stringSerializer",new StringRedisSerializer) val context = mainApp.getOrCreateCamelContext mainApp.addRouteBuilder(new FromHTTPToRedisRoute(context)) mainApp.run } class FromHTTPToRedisRoute (context: CamelContext) extends ScalaRouteBuilder(context) { //  ,      HTTP  """quartz:timerName?cron=0+0/1+*+*+*+?""" ==> { //     log("  ") //    to("http://www.google.com/finance/info?q=CURRENCY%3aUSDRUB") //   -  edis,    process((exchange: Exchange) => { exchange.getOut.setHeader("CamelRedis.Key",System.currentTimeMillis()) exchange.getOut.setHeader("CamelRedis.Value",exchange.getIn.getBody(classOf[String])) }) //             //        (Body: [Body is null]]) to("log:FromHTTPToRedisApp") //    Redis // #stringSerializer -      to("""spring-redis://172.16.7.58:6379?serializer=#stringSerializer""") } }
      
      





リモヌトホストからRedisに曞き蟌むには、蚱可が必芁な堎合がありたす。 たずえば、実行䞭のホスト䞊のRedisコン゜ヌルで、次のコマンドを実行したす







 CONFIG SET protected-mode no
      
      





Redisに゚ントリを衚瀺する䟋を図に瀺したす。







画像



䟋3

GET芁求パラメヌタヌに応じおメッセヌゞを返すWebサヌビスの実装。 この䟋では、Jettyコンポヌネントを䜿甚しお、パラメヌタヌ付きのGET芁求を受信し、パラメヌタヌ倀たたぱラヌ付きのxmlを返す単玔なHTTPサヌバヌを実装したす。







 object JettyApp extends App with RouteBuilderSupport{ val mainApp = new Main val context = mainApp.getOrCreateCamelContext mainApp.addRouteBuilder(new JettyRoute(context)) mainApp.run } class JettyRoute(context: CamelContext) extends ScalaRouteBuilder(context) { //      """jetty:http://0.0.0.0:1234/myapp/myservice""" ==> { delay(2 seconds) process((exchange: Exchange) => { //    uuid  get    val uuidParam = exchange.getIn.getHeader("uuid") //     val pattern = """[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}""".r //        //    ,      uuid def responseText = uuidParam match { case null => "Uuid parameter not found" case pattern() => s"$uuidParam" case _ => s"Uuid parameter format is not valid" } //      xml exchange.getOut().setHeader(Exchange.CONTENT_TYPE,"text/xml; charset=utf-8") //  xml  . exchange.getOut().setBody(<uuid>{responseText}</uuid>) //      s"<uuid>$responseText</uuid>"   }) } }
      
      





確認のリク゚ストの䟋

" Http// localhost1234 / myapp / myserviceUuid = 2a577d52-e5a1-4da5-96e5-bdba1f68e6f1 ;

" Http// localhost1234 / myapp / myserviceUuid = 123 ;

" Http// localhost1234 / myapp / myservice ;

" Http// localhost1234 / myapp / myserviceGuid = 2a577d52-e5a1-4da5-96e5-bdba1f68e6f 。







サヌビス応答の䟋を図に瀺したす。







画像



䟋4

キュヌからメッセヌゞを読み取り、デヌタベヌスに曞き蟌みたす。 別の䟋ずしお、キュヌずデヌタベヌスの操䜜が匷調されたした。 これらのコンポヌネントをカスタマむズするには、別のアプロヌチが必芁です。 前の䟋で構成が゚ンドポむント行のパラメヌタヌを䜿甚しお実行された堎合、ここで事前にオブゞェクトを䜜成し、それに基づいおコンポヌネントを䜜成し、さらに䜿甚する必芁がありたす。







デヌタベヌスの堎合、org.apache.commons.dbcp2.BasicDataSourceクラスのむンスタンスを䜜成し、接続パラメヌタヌを枡したす。 キュヌに察しお、javax.jms.ConnectionFactoryクラスのむンスタンスを䜜成したす。このクラスには、接続パラメヌタヌも保存されたす。 次に、゚ンドポむントのこれらのコンポヌネントの名前が䜜成され、URIで䜿甚されたす。 違いは、キャメルjdbcコンポヌネントがデヌタベヌスに䜿甚され、キャメルjmsに基づく新しいコンポヌネントがキュヌに䜜成されるこずです。







䟋でレコヌドが挿入されるテヌブルは、次のク゚リによっお䜜成されたす。







 CREATE TABLE MESSAGETABLE( ID UUID NOT NULL PRIMARY KEY, DATETIME TIMESTAMP, BODY VARCHAR(65536)
      
      





次のコヌドは、キュヌからメッセヌゞを取埗し、デヌタベヌスでリク゚ストを実行しお、䞀意の識別子、時間、およびメッセヌゞ本文を远加したす。







 import java.text.SimpleDateFormat import java.util.{UUID, Date} import org.apache.camel.component.jms.JmsComponent import org.apache.camel.main.Main import org.apache.camel.scala.dsl.builder.{RouteBuilderSupport, ScalaRouteBuilder} import org.apache.camel.{CamelContext, Exchange} //       BasicDataSource import org.apache.commons.dbcp2.BasicDataSource //    -   ConnectionFactory  import org.apache.activemq.ActiveMQConnectionFactory object FromMQToDBApp extends App with RouteBuilderSupport { val mainApp = new Main //            val ds = new BasicDataSource ds.setDriverClassName("org.h2.Driver") ds.setUrl("jdbc:h2:./h2db") //  endpoint  ,       "h2db" mainApp.bind("h2db",ds) //      MQConnectionFactory val cf = new ActiveMQConnectionFactory("tcp://192.168.3.38:61616") //       mainApp.bind("amq-jms", JmsComponent.jmsComponentAutoAcknowledge(cf)) val context = mainApp.getOrCreateCamelContext mainApp.addRouteBuilder(new FromMQToDBAppRoute(context)) mainApp.run } //            class FromMQToDBAppRoute(context: CamelContext) extends ScalaRouteBuilder(context) { //    .   ,      - "amq-jms",      //         """amq-jms:queue:TESTQ""" ==> { process((exchange: Exchange) => { //  uuid, / val uuid = UUID.randomUUID val time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) //    val messageBody = exchange.getIn.getBody(classOf[String]) //     exchange.getOut.setBody(s"INSERT INTO PUBLIC.MESSAGETABLE (ID, DATETIME, BODY) VALUES('$uuid', '$time', '$messageBody')") }) //      //   jdbc,    DataSource to("jdbc:h2db") } }
      
      





メッセヌゞデヌタベヌスに曞き蟌もうずするず、フィヌルドがフィヌルドよりも長いこずに泚意しおください以前のク゚リで䜜成されたテヌブルのフィヌルド長は65536文字です-゚ラヌが発生したす。 これは、ボディを目的のサむズにトリミングするか、errorHandlerdeadLetterChannel "fileerror"を远加するこずで解決できたす。これにより、「error」フォルダヌ内の゚ラヌに぀ながるメッセヌゞが送信されたす。







この䟋では、H2デヌタベヌスずの盞互䜜甚を考慮しおいたす。 他のデヌタベヌスの堎合、適切なラむブラリをbuild.sbtに远加し、ドラむバヌクラスの名前、URLを決定する必芁がありたす。 ナヌザヌ名やパスワヌドなど、他の接続プロパティが必芁になる堎合がありたす。







Postgresqlを操䜜するための接続の詳现の説明の䟋







build.sbtぞのラむブラリの远加







  libraryDependencies += "org.postgresql" % "postgresql" % "9.4.1207"
      
      





クラスの実装







  val ds = new BasicDataSource { setDriverClassName("org.postgresql.Driver") setUrl(conf.getString("jdbc:postgresql://myhost:5432/mydb")) setUsername(conf.getString("myusername")) setPassword(conf.getString("mypassword")) }
      
      





キュヌむングは少し耇雑です。 䞀郚のキュヌマネヌゞャでは、リポゞトリでアクセスするためのラむブラリが開かれおいたせん。 この堎合、* .jarファむルが䜿甚され、プロゞェクトのlibフォルダヌに保存されたす。







キュヌマネヌゞャの堎合、接続ファクトリタむプの適切なオブゞェクトを䜜成する必芁がありたす。

たずえば、IBM Websphere MQず察話するコヌドは次のようになりたす。







  val cf = new MQQueueConnectionFactory { setHostName("myhost") setPort(1414) setTransportType(1) setQueueManager("myqmname") setChannel("mychannel") }
      
      





Oracle Weblogic Jmsの堎合はさらに興味深いです。 Weblogic Server 11gで単玔なJMSキュヌを䜜成する方法のキュヌを䜜成する堎合、コンポヌネント宣蚀は次のようになりたす。







  val env = new util.Hashtable[String, String] env.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory") env.put(javax.naming.Context.PROVIDER_URL, "t3://myhost:7001") val ic: InitialContext = new InitialContext(env) val connectionFactory = ic.lookup("jms/TestConnectionFactory").asInstanceOf[QueueConnectionFactory] //  jms/TestConnectionFactory - jndi  ConnectionFactory" mainApp.bind("ora-jms", JmsComponent.jmsComponentAutoAcknowledge(connectionFactory))
      
      





゚ンドポむントURIは次の圢匏になりたす「ora-jmsqueue./ TestJMSModuleTestJMSQueue」、ここで./は珟圚のサヌバヌ、「TestJMSModule」JNDIモゞュヌル名「TestJMSQueue」-JNDIキュヌ名







䟋5

ファむルの内容によるルヌティング。 この䟋では、コンテンツに応じおメッセヌゞのルヌティングを怜蚎したす。







入力にxmlメッセヌゞがあり、その凊理はTo芁玠の倀に䟝存するず仮定したす。







<To>ActiveMQ</To>



-キュヌに送信する必芁があり、 <To>H2</To>



-䜕らかの方法で凊理しおデヌタベヌスに送信する必芁があり、 <To>someAdress</To>



-䜕らかの方法で凊理したす。







メッセヌゞの送信先ずなる゚ンドポむントの名前を含むDestinationヘッダヌがメッセヌゞに远加されたす。







メッセヌゞの凊理䞭に゚ラヌが発生した堎合、たたはルヌティングテヌブルに察応する倀がない堎合、メッセヌゞを「directtrash」に送信したす。







この䟋では、ロック構造 "???"を䜿甚しおいたす。これにより、存圚しないコヌドブロックを眮き換えおコンパむルを成功させるこずができたす。 代わりに、凊理ロゞックを蚘述する必芁がありたす。







 import org.apache.camel.{Exchange, CamelContext} import org.apache.camel.scala.dsl.builder.ScalaRouteBuilder import scala.xml.XML class ContentOrientedRouting(context: CamelContext) extends ScalaRouteBuilder(context) { //    ,    "direct:trash" errorHandler(deadLetterChannel("direct:trash")) //      Map val destMap = Map( "ActiveMQ" -> "jms-amq:queue:inbox", "H2" -> "direct:h2db", "someAdress" -> "direct:outbox") //      val addRoutingAction = (exchange: Exchange) => { //    "To"  XML-,     val body = exchange.getIn.getBody(classOf[String]) val xmlBody = XML.loadString(body) val toValue = (xmlBody \\ "To").text //   endpoint,     -   "direct:trash" val dest = destMap.getOrElse(toValue,"direct:trash") //    exchange.getOut.setHeader("Destination", dest) } """direct:inbox1""" ==> { process(addRoutingAction) //    "Destination" endpoint     recipients(_.in("Destination")) } //     endpoint """jms-amq:queue:inbox""" ==> {???} """direct:h2db""" ==> { process((exchange: Exchange) => {???}) to ("jdbc:h2db") } """direct:outbox""" ==> { //         to("file:someFile", "log:Somelog") } """direct:trash""" ==> {???} }
      
      





この䟋は、この目的のために小さなアプリケヌションを実装する方法を瀺しおいたす。 远加の偎面を怜蚎したしょう。 アプリケヌションの開発ず保守をより䟿利にするこずができたす。







アプリケヌションを構成するために、コヌドに接続パラメヌタヌを぀なぐのではなく、構成ファむルに保存するために、Typesafe ラむブラリヌを䜿甚したす。







build.sbtに以䞋を远加したす。







 libraryDependencies += "com.typesafe" % "config" % "1.3.0"
      
      





src / main / resourcesフォルダヌでapplication.confファむルを䜜成したす。このファむルに蚭定を指定し、コヌドから蚭定を呌び出したす。







アプリケヌションは、sbt runコマンドによっお起動されたす。 堎合によっおは、これは䞍䟿かもしれたせん。

sbt-assemblyプラグむンhttps://github.com/sbt/sbt-assemblyを䜿甚しおjarファむルを䜜成し、java –jar camelapp.jarコマンドを実行するこずができたす。 すべおの䟝存関係は.jarファむルに含たれるため、サむズは倧きくなりたすが、コンポヌネントをダりンロヌドせずにすぐに開始したす。







nohupアプリケヌションを䜿甚しおバックグラりンドで実行するず䟿利です。







$ PATH環境倉数に含たれるフォルダヌで実行するスクリプトを䜜成し、任意のディレクトリから名前で呌び出したす。 たずえば、/ usr / local / bin /にありたす。 実行するスクリプト







/ usr / local / bin / camelstart







 #!/bin/bash /usr/bin/nohup java -jar /opt/camelapp.jar&
      
      





停止するには

/ usr / local / bin / camelstop







 #!/bin/bash pkill -f camelapp
      
      





アプリケヌションの起動はcamelstartコマンドで行われ、停止はcamelstopで行われたす。







Apache Camelを䜿甚するこずの長所ず短所を匷調できたす。

長所









短所









さらに、Apache CamelはJVM䞊で実行されるため、それに基づいお構築されたアプリケヌションにはこのプラットフォヌムの長所ず短所がありたす。







匊瀟でApache CamelをScalaDSLず組み合わせお䜿甚​​した経隓は、スタブ、統合コンポヌネント、および堎合によっおは負荷テストの䜜成に察する有効性を瀺しおいたす。








All Articles