Oracle SOA Suite 11gR1 Patch Set 2 released and other nice goodies!

A lot of new releases/patches coming from Oracle the last few days.

SOA Suite 11g

Other links

Oracle Service Bus 11g (11.1.1.3.0)

Time to fill the download queues and play with the new goodies!

Advertisements

EAI/Oracle Service Bus testing with Citrus Framework, part2

Before reading on, read my first article on the Citrus Framework.
In this second part of testing by use of the Citrus Framework i want to test a new scenario which was part of the middleware of my last customer.

Scenario

Some third party application stores messages on the Weblogic JMS queue in the middleware. My Oracle Service Bus project picks up the messages, does some transformation/enrichment/etc, and stores the data on the file share.
Storing the file on the filesystem is the trigger for starting a new process, but this will be out of scope for this article.

What we want to test is the blackbox which runs in the middleware. I want to be able to store the xml data file (fact 1), in the jms queue, and eventually read the file from filesystem (fact 2) and see if the response
matches my expected response defined in the Citrus Framework.

Requirements

See the requirements of the first article, what we need again are the inbound queues and the used connection factory. In the middleware we need a Oracle Service Bus flow.

Oracle Service Bus

The Oracle Service Bus project i want to test is a real basic one with only 2 resources.

  1. jms_ps – proxy service with service type ‘Any XML Service’ and jms protocol
    The flow consists of only a simple route action whichs routes the incoming message of the jms queue 1 on 1 to business service which stores the content on filesystem.
  2. file_bs – business service which stores the file on my local file system

Citrus configuration

First we need to config src/citrus/resources/citrus-context.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:citrus="http://www.citrusframework.org/schema/config"
       xmlns:citrus-ws="http://www.citrusframework.org/schema/ws/config"
       xmlns:citrus-http="http://www.citrusframework.org/schema/http/config"
       xmlns:context="http://www.springframework.org/schema/context"
	   xmlns:si="http://www.springframework.org/schema/integration"
	   xmlns:file="http://www.springframework.org/schema/integration/file"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	   http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
	   http://www.springframework.org/schema/integration/file http://www.springframework.org/schema/integration/file/spring-integration-file-2.0.xsd
       http://www.citrusframework.org/schema/config http://www.citrusframework.org/schema/config/citrus-config-1.1.xsd">

	<!-- Common settings -->
	<bean id="schemaRepository" class="com.consol.citrus.xml.XsdSchemaRepository">
		<property name="schemas">
			<list>
				<bean class="org.springframework.xml.xsd.SimpleXsdSchema">
					<property name="xsd" value="classpath:com/consol/citrus/samples/greeting/schema/greeting.xsd"/>
				</bean>
			</list>
		</property>
	</bean>	   

	<bean class="com.consol.citrus.variable.GlobalVariables">
		<property name="variables">
			<map>
				<entry key="project.name" value="Citrus Greeting sample"/>
			</map>
		</property>
	</bean>	

	<bean class="com.consol.citrus.aop.StoreMessageInterceptorAspect"/>	   

	<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
		<property name="environment">
			<props>
				<prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>
				<prop key="java.naming.provider.url">t3://localhost:7001</prop>
			</props>
		</property>
	</bean>	

	<bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
		<property name="jndiTemplate">
			<ref bean="jndiTemplate" />
		</property>
		<property name="jndiName">
			<value>TestCF</value>
		</property>
	</bean>	

	<bean id="jmsDestinationResolver" class="org.springframework.jms.support.destination.JndiDestinationResolver">
		<property name="jndiTemplate">
			<ref bean="jndiTemplate" />
		</property>
		<property name="cache">
			<value>true</value>
		</property>
	</bean>

	<bean id="sendGreeting" class="com.consol.citrus.jms.JmsMessageSender">
		<property name="destinationName" value="citrus_queue_in"/>
		<property name="connectionFactory">
			<ref bean="connectionFactory" />
		</property>
		<property name="destinationResolver">
			<ref bean="jmsDestinationResolver" />
		</property>
	</bean>

	<file:inbound-channel-adapter id="fileAdapter" directory="file://e:/temp/files_out" channel="fileInputChannel">
		<si:poller max-messages-per-poll="100">
			<si:interval-trigger time-unit="MILLISECONDS" interval="1" />
		</si:poller>
	</file:inbound-channel-adapter>
	
	<si:channel id="fileInputChannel">
		<si:queue capacity="25"/>
		<si:interceptors>
			<bean class="org.springframework.integration.transformer.MessageTransformingChannelInterceptor">
				<constructor-arg>
					<bean class="org.springframework.integration.file.transformer.FileToStringTransformer"/>
				</constructor-arg>
			</bean>
		</si:interceptors>
	</si:channel>

	<citrus:message-channel-receiver id="fileInputChannelReceiver"
                                    channel="fileInputChannel"
                                    receive-timeout="5000"/>	

	<!-- TestSuite definition -->
	<bean name="citrus-samples-greeting" class="com.consol.citrus.TestSuite"/>
</beans>

Again we use the ‘sendGreeting’ bean to send the message to the jms queue. For Citrus to be able to do his actions on the messages he needs to get the message from a MessageChannel.
When the file gets stored on filesystem the ‘inbound-channel-adapter’ will poll for the file and puts it on the ‘fileInputChannel’ messagechannel, eventually citrus will pick up the message from this channel.

Documentation of Citrus doesn’t give you any hints on how to integrate the file-protocol. Since Citrus makes use of Spring Integration we can easily adopt the file support from this framework and add it to the Citrus context file.
The following changes are needed on the citrus-context.xml file

  • Addition of the integration namespaces/schemalocations
  • Addition of the (file) inbound-channel-adapter
  • Addition of the channel for the file
  • Citrus Message channel polling on the file input channel
  • Copy the Spring Integration File jar to the libs folder. I used ‘org.springframework.integration.file-1.0.3.RELEASE.jar’ from the Spring Integration framework

A few notes on the fileInputChannel.
If we would just add

<si:channel id="fileInputChannel">

, Citrus will give us the next error :

[citrus] Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'fileInputChannelReceiver': Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type [org.springframework.integration.channel.DirectChannel] to required type 
[org.springframework.integration.channel.PollableChannel] for property 'channel'; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type [org.springframework.integration.channel.DirectChannel] 
to required type [org.springframework.integration.channel.PollableChannel] for property 'channel': no matching editors or conversion strategy found

So Citrus expects some other type of channel. Adding

<si:queue capacity="25"/>

, will fix this problem.

Running the sample again will give us the next error :

   [citrus] Caused by: org.w3c.dom.ls.LSException: Content is not allowed in prolog.
   [citrus]     at org.apache.xerces.parsers.DOMParserImpl.parse(Unknown Source)
   [citrus]     at com.consol.citrus.util.XMLUtils.parseMessagePayload(XMLUtils.java:333)
   [citrus]     at com.consol.citrus.validation.DefaultXMLMessageValidator.validateXMLSchema(DefaultXMLMessageValidator.java:300)
   [citrus]     at com.consol.citrus.validation.DefaultXMLMessageValidator.validateMessage(DefaultXMLMessageValidator.java:98)
   [citrus]     ... 54 more
   [citrus] Caused by: ::::1:1:0:Content is not allowed in prolog.
   [citrus]     at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
   [citrus]     at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
   [citrus]     at org.apache.xerces.impl.XMLScanner.reportFatalError(Unknown Source)
   [citrus]     at org.apache.xerces.impl.XMLDocumentScannerImpl$PrologDispatcher.dispatch(Unknown Source)
   [citrus]     at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
   [citrus]     at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
   [citrus]     at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
   [citrus]     at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
   [citrus]     ... 58 more   

The inbound-adapter passes on the file 1 on 1 to the file channel. Since the channel expects a string instead of a file object we need to transform this file-object to a string-object. Spring Framework gives us a default Transformer for this.
To intercept the File-object before it gets stored on the MessageChannel we can add an ‘interceptor’, as argument to the call of the interceptor we define the ‘FileToStringTransformer’.

Secondly we need to config src/citrus/tests/com/consol/citrus/samples/greeting/jms/GreetingJmsTest.xml

<?xml version="1.0" encoding="UTF-8"?>
<spring:beans xmlns="http://www.citrusframework.org/schema/testcase" xmlns:spring="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.citrusframework.org/schema/testcase http://www.citrusframework.org/schema/testcase/citrus-testcase.xsd">
	<testcase name="GreetingJmsTest">
		<meta-info>
			<author>Eric Elzinga</author>
			<creationdate>2010-03-25</creationdate>
			<status>FINAL</status>
			<last-updated-by>Eric Elzinga</last-updated-by>
			<last-updated-on>2010-03-28T00:00:00</last-updated-on>
		</meta-info>

		<variables>
			<variable name="correlationId" value="citrus:randomNumber(10)"></variable>
			<variable name="user" value="Christoph"></variable>
		</variables>

		<actions>
			<send with="receiveGreeting">
			<message>
				<resource file="classpath:xmlData/greeting_request.xml" />
			</message>
			<header>
				<element name="Operation" value="sayHello"/>
				<element name="CorrelationId" value="${correlationId}"/>
			</header>
		</send>

		<receive with="fileAdapter">
		<selector>
			<value>CorrelationId = '${correlationId}'</value>
		</selector>
		<message>
		  <resource file="classpath:xmlData/greeting_response.xml" />
		  <ignore path="//tns:GreetingRequestMessage/tns:Text" />
		</message>
		<header>
			<element name="Operation" value="sayHello"/>
			<element name="CorrelationId" value="${correlationId}"/>
		</header>
	</receive>
</actions>
</testcase>
</spring:beans>

Only change in here compaired to part1 is the

<receive with="fileInputChannelReceiver">

Now run the test again and see if the framework matches the generated file on filesystem with the payload we configurated.
Since i don’t do any transformations in the middleware the request payload should match the response payload.

ant citrus.run.tests

..and see the results

   [citrus] 19047  INFO  port.LoggingReporter| TEST FINISHED: GreetingJmsTest
   [citrus] 19047  INFO  port.LoggingReporter| ------------------------------------------------------------------------
   [citrus] 19063  INFO  port.LoggingReporter| FINISH TESTSUITE citrus-samples-greeting
   [citrus] 19063  INFO  port.LoggingReporter| ------------------------------------------------------------------------
   [citrus] 19063  INFO  port.LoggingReporter| ________________________________________________________________________
   [citrus] 19063  INFO  port.LoggingReporter|
   [citrus] 19079  INFO  port.LoggingReporter| CITRUS TEST RESULTS
   [citrus] 19079  INFO  port.LoggingReporter|
   [citrus] 19079  INFO  port.LoggingReporter|  GreetingJmsTest ............................................... SUCCESS
   [citrus] 19079  INFO  port.LoggingReporter|
   [citrus] 19079  INFO  port.LoggingReporter| Total number of tests: 1
   [citrus] 19079  INFO  port.LoggingReporter| Skipped: 0 (0.0%)
   [citrus] 19079  INFO  port.LoggingReporter| Failed:  0 (0.0%)
   [citrus] 19079  INFO  port.LoggingReporter| Success: 1 (100.0%)

Conclusion

I hope this gave you good overview on how to implement integration tests with the Citrus Framework. For me it’s an ideal way to test my Oracle Service Bus flows.
How do you guys think about it ? Or do you use other frameworks for testing?