Featured Posts

osb, testing

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?
Original blog

eai, testing

Enterprise Application Integration testing with Citrus Framework

Last week i read a thread on the Oracle forums on how to test the Oracle Service Bus processes.
A comment from one of the users lead to me this framework, Citrus Framework.

Citrus is an integration testing framework written in Java that tests your software application to fit into your customer’s environment. The tool simulates
surrounding systems across various transports (e.g. Http, JMS, TCP/IP, SOAP, …) in order to perform automatic end-to-end use case testing.

For one of my customers we had a lot of unit tests for testing all the flows in the middleware. Different scenarios, different protocols, eventually all needed to be tested.
Constructing all theses possible scenarios, we build everything from scratch to be able to send messages on different protocols and do all the correlation of the messages which go in and which eventually arrive at target destination.

To simulate a one of these scenarios i wanted to try the Citrus Framework to see what it could do for me.

For my test i used the greetings sample which ships with the citrus-1.1-SNAPSHOT distribution.

Directory structure
Create the directory structure as desribed over here.
When all done you should have a structure like this

  • src/citrus/java – location of the generated Testng testcases
  • src/citrus/resoruces – location of all the configuration needed for the framework
  • src/citrus/tests – location of all the defined testcases
  • optional – lib – location of all the dependency libraries
  • optional – log – location in which citrus framework will store logging (payload fragments)
  • build.xml – ant build script

Example of the used build.xml

<project name="greetings" basedir="." default="citrus.run.tests">

	<property file="src/citrus/resources/citrus.properties"/>

    <path id="citrus-classpath">
        <pathelement path="src/citrus/java"/>
    	<fileset dir="lib">
            <include name="*.jar"/>
        </fileset>
    </path>

	<typedef resource="citrustasks" classpath="lib/citrus-ant-tasks-1.1-SNAPSHOT.jar"/>

	<target name="compile.tests">
	    <javac srcdir="src/citrus/java" classpathref="citrus-classpath"/>
		<javac srcdir="src/citrus/tests" classpathref="citrus-classpath"/>
	</target>

	<target name="citrus.run.tests" depends="compile.tests" description="Runs all Citrus tests">
		<citrus suitename="${testsuite.name}" package="com.consol.citrus.samples.*"/>
	</target>

	<target name="citrus.run.single.test" depends="compile.tests" description="Runs a single test by name">
   	 	<touch file="test.history"/>

    	<loadproperties srcfile="test.history"/>

    	<echo message="Last test executed: ${last.test.executed}"/>

    	<input message="Enter test name:" addproperty="testclass" defaultvalue="${last.test.executed}"/>

    	<propertyfile file="test.history">
			<entry key="last.test.executed" type="string" value="${testclass}"/>
		</propertyfile>

		<citrus suitename="citrus-samples" test="${testclass}"/>
    </target>

	<target name="create.test" description="Creates a new empty test case">
		<input message="Enter test name:" addproperty="test.name"/>
        <input message="Enter test description:" addproperty="test.description" defaultvalue="TODO: Description"/>
        <input message="Enter author's name:" addproperty="test.author" defaultvalue="${default.test.author}"/>
        <input message="Enter package:" addproperty="test.package" defaultvalue="${default.test.package}"/>
		<input message="Enter framework:" addproperty="test.framework" defaultvalue="testng"/>

		<java classname="com.consol.citrus.util.TestCaseCreator">
            <classpath refid="citrus-classpath"/>
			<arg line="-name ${test.name} -author ${test.author} -description ${test.description} -package ${test.package} -framework ${test.framework}"/>
        </java>
    </target>

	<target name="create.html.doc" description="Creates test documentation in html">
        <mkdir dir="target"/>

        <java classname="com.consol.citrus.doc.HtmlTestDocGenerator">
            <classpath refid="citrus-classpath" />

            <arg value="src/citrus/tests"/>
            <arg value="target/CitrusTests.html"/>
            <arg value="Citrus Test Documentation"/>
            <arg value="logo.png"/>
            <arg value="Overview"/>
        </java>

        <copy todir="target" file="src/citrus/resources/logo.png"/>

        <zip destfile="target/CitrusTests.zip">
            <fileset dir="target">
                <include name="CitrusTests.html"/>
                <include name="logo.png"/>
            </fileset>
        </zip>
    </target>

    <target name="create.xls.doc" description="Creates test documentation in excel">
        <mkdir dir="target"/>

        <java classname="com.consol.citrus.doc.ExcelTestDocGenerator">
            <classpath refid="citrus-classpath" />

            <arg value="src/citrus/tests"/>
            <arg value="CitrusTests"/>
            <arg value="Citrus Test Documentation"/>
            <arg value="Citrus Testframework"/>
            <arg value="ConSol* Software GmbH"/>
        </java>
    </target>
</project>

Scenario
Middleware gets delivered a new message on a queue. Some process picks up the message, does some transformation/validation on it, and passes it on. Eventually a new message will get sended to some other queue.

– Pre-Requirements

  • weblogic jms queue (citrus_queue_in, citrus_queue_out)
  • weblogic connection factory (TestCF)
  • wlfullclient.jar (cd WL_HOME/server/lib, java -jar wljarbuilder.jar)
  • available process flow for testing (i used a simple Oracle Service Bus (OSB)) flow which received a message on citrus_queue_in and routes the mesage to citrus_queue_out)

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"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.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>

	<bean id="receiveGreeting" class="com.consol.citrus.jms.JmsMessageReceiver">
		<property name="destinationName" value="citrus_queue_out"/>
		<property name="connectionFactory">
			<ref bean="connectionFactory" />
		</property>
		<property name="destinationResolver">
			<ref bean="jmsDestinationResolver" />
		</property>
	</bean>
<!--
	<citrus:jms-message-sender id="getOrdersRequestSender"
						       destination-name="testJMSServer/citrus_queue_in"/>

	<citrus:jms-message-receiver id="getOrdersResponseReceiver"
								 destination-name="testJMSServer/citrus_queue_out"/>
-->
	<!-- TestSuite definition -->
	<bean name="citrus-samples-greeting" class="com.consol.citrus.TestSuite"/>
</beans>

Let’s discuss a few parts

  • bean id=”schemaRepository” class=”com.consol.citrus.xml.XsdSchemaRepository”
  • Registrate the xsd for the message payload, which eventually will be used to validate the messages
  • bean class=”com.consol.citrus.variable.GlobalVariables”
  • Setup some global settings
  • bean class=”com.consol.citrus.aop.StoreMessageInterceptorAspect”
  • Activate extra debugging
  • bean id=”jndiTemplate, connectionFactory and jmsDestinationResolver”
  • Configuration for the lookup of the weblogic resources
  • bean id=”sendGreeting” class=”com.consol.citrus.jms.JmsMessageSender”
  • used for sending the messages to the jms queue
  • bean id=”receiveGreeting” class=”com.consol.citrus.jms.JmsMessageReceiver”
  • used for receiving the messages of the jms queue

** Note. Default configuration describes to make use of :

<citrus:jms-message-sender id="getOrdersRequestSender" destination-name="testJMSServer/citrus_queue_in"/>

This gave me errors about Spring/Citrus not able to find the destination queue.
In case of weblogic resources use the “bean id-….” definition. (thanks to Christoph of the Consol team for helping me out on this one)

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="Eric"></variable>
		</variables>

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

		<receive with="receiveGreeting">
		<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>

**Notes

  • resource vs data tag. To include the payload of the test we can either use
    <message>
    	<resource file="classpath:xmlData/greeting_request.xml" />
    </message>
    

    or

    <message>
    	<data>
    		<![CDATA[
    		   <tns:GreetingRequestMessage xmlns:tns="http://www.citrusframework.org/samples/greeting">
    			   <tns:CorrelationId>${correlationId}</tns:CorrelationId>
    			   <tns:Operation>sayHello</tns:Operation>
    			   <tns:User>${user}</tns:User>
    			   <tns:Text>Hello Citrus!</tns:Text>
    		   </tns:GreetingRequestMessage>
    		]]>
    	</data>
    </message>
    
  • header tag adds/validates header information (soap headers/jms properties/etc)
  • selector tag gives us the option to let the framework only select the message we would expect there to be.
    If we were testing against a ‘running’ environment, it could be possible that other processes are also sending messages to queue. With the selector tag we only select the message from the queue which succeed the criteria.

    <selector>
    	<value>CorrelationId = '${correlationId}'</value>
    </selector>
    

    If we skip this selector, put some messages in the out-queue and do the test again we will see the next error message :

    [citrus] Caused by: java.lang.IllegalArgumentException: Values not equal for header element 'CorrelationId', expected '2243051261' but was '2791756984'
    

    So it will try to match the first message it will find in the queue.

  • the ignore tag will give us the option to select elements the framework should not validate
    elements which consist of current datetime values are an example of these
  • full validation vs partial validation
  • in the example included i validate the whole xml message. in case we only want to validate/check a few elements from the message we can use the next :

    <message>
    	<validate path="//tns:GreetingRequestMessage/tns:Operation" value="sayHello"/>
    </message>
    

    The result
    Run the ant buildfile, ant citrus.run.tests

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

    Successful result!

    What we have tested in here :
    – Are we able to put a message on the in-queue, and get a response back on the out-queue.
    – Does the response xml validates against the supplied schema definition (xsd)
    – Do the response queue message also have the correct header properties

    Other features
    Ant/Maven support
    Database access/validation
    Adapters (tibco/ws/http)
    … and more

    In a following blog item i will try to simulate some other scenario to see if the framework covers it all.
    Original blog

osb

Oracle Service Bus, invoke asynchronous webservices

In this blog i will give a short overview on how to invoke asynchronous webservices from the Oracle Service Bus, and route the callback from the service back to some other endpoint (service) on the bus.
The flow we will create will look like this

Asynchronous webservice
To communicate from the Oracle Service Bus to an asynchronous webservice i used this (http://www.ewernli.com/web/guest/12) tutorial to get a service up and running on my glassfish.
After deployment the service (wsdl) will be available at : http://localhost:8080/AsyncTestImplService/AsyncTestImpl?WSDL
In this example the response (callback) is mocked in SoapUI.

Let’s test the service first in SoapUI.

In the top left you will see the request payload of the service, and at the top right the response of it (acknowledge).
At the bottom left you will see the mockservice running which will capture the callback from the service.
The response we will get back from the mock is :

<?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
	<S:Header>
		<RelatesTo xmlns="http://schemas.xmlsoap.org/ws/2003/03/addressing">ws:uniqueAddress</RelatesTo>
	</S:Header>
	<S:Body>
		<ns2:response xmlns:ns2="http://ewe.org/">
			<arg0>hello you [http://127.0.0.1:7777/]</arg0>
		</ns2:response>
	</S:Body>
</S:Envelope>

In our scenario will will not be mocking the callback, but route it back to an other resource on the service bus. In this case a new proxy service which will deal with the callback response.

Oracle Service Bus
For this scenario we will need the next list of resources

  • requestps – start endpoint of the osb flow, based on the wsdl of the the service deployed in the glassfish.
  • requestbs – service based on the endpoint of the service running in the glassfish (this will eventually execute the service logica)
  • responseps – any soap service which will receive the callback of the asynchronous service call.
  • responsebs – any soap service with jms endpoint, which will put the received callback in the queue.
  • testwsdl – the imported wsdl of the service running in the glassfish
  • XMLSchema_738301149 – generated xsd used by the imported wsdl

We will start with a call to the requestps service.

This service will route the request 1 on 1 to the requestbs. The requestbs is a wsdl-based http service based on the wsdl of the asynchronous webservice, with ‘http://127.0.0.1:8080/AsyncTestImplService/AsyncTestImpl
‘ as endpoint (glassfish). To inform this service where to deliver the callback response we need to just edit the soap-header which we will send to the service.

The flow of the requestps will look like this :

The route will look like this :

In the assign we will assign this to the header variable :

<soap-env:Header xmlns:ns1="http://schemas.xmlsoap.org/ws/2003/03/addressing">
	<ns1:MessageID>ws:uniqueAddress</ns1:MessageID>
	<ns1:ReplyTo>
		<ns1:Address>http://localhost:7001/AsyncTest/invoke/responseps</ns1:Address>
	 </ns1:ReplyTo>
 </soap-env:Header>

In here we will assign the address to which the service can reply the callback. In our case this will be a new endpoint on the service bus (resource responseps).
This doens’t have to be a static xml payload.

The responseps proxy service will receive the callback. This proxy service is an any soap proxy service and will receive the whole soap-envelope of the callback.

This service will route 1 on 1 to the responsebs.
This is a any soap business service with jms transport (endpoint jms://localhost:7001/TestCF/TestQueue). So the whole soap-envelope will be stored in the TestQueue queue.
In weblogic we created both the TestCF connection factory and the TestQueue queue.

Test the flow
Go to the sbconsole and use the testconsole of the requestps to invoke the service on the bus

And the response of the invoke :

We received back an empty sayHelloResponse. This is because we will receive the callback on the responseps resource on the bus.
Now go the queue to see the content :

It looks like the flow ended correctly. We invoked a service on the bus, this service routed the request to the asynchronous webservice running in glassfish. This service came back with a callback and this call is routed on the service bus to the queue which resists in weblogic.

SoapUI
Create a new project in SoapUI based on the wsdl of the requestps resource on the bus (endpoint http://localhost:7001/AsyncTest/invoke/testps/?WSDL).
Request payload

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ewe="http://ewe.org/">
   <soapenv:Header/>
   <soapenv:Body>
      <ewe:sayHello>
         <!--Optional:-->
         <arg0>eric</arg0>
      </ewe:sayHello>
   </soapenv:Body>
</soapenv:Envelope>

Response

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <ns2:sayHelloResponse xmlns:ns2="http://ewe.org/"/>
   </S:Body>
</S:Envelope>

And the payload in the queue

<?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
	<S:Header>
		<RelatesTo xmlns="http://schemas.xmlsoap.org/ws/2003/03/addressing">ws:uniqueAddress</RelatesTo>
	</S:Header>
	<S:Body>
		<ns2:response xmlns:ns2="http://ewe.org/">
			<arg0>hello you [http://localhost:7001/AsyncTest/invoke/responseps]</arg0>
		</ns2:response>
	</S:Body>
</S:Envelope>

Resources
sbconfig.jar (export of oracle service bus resources)

Original blog

fusion11g

New features in Oracle SOA Suite 11gR1 patchset 2

Clemens published a list of the new features coming up in the patchset 2 of SOA Suite 11gR1.

short overview

  • SOA Infra’s Spring C&I implementation (based on weblogic sca for java) becomes full production.
  • Full and complete support for BPEL 2.0
  • Reintroduction of “BPEL domains”, now to be called “partitions”
  • Enhancements to BPEL’s transactional behavior and audit-trail
  • Oracle Mediator, support for re-sequencing
  • XML / http – get and post support on binding level
  • Support for direct bindings
  • Full BPMN 2.0 support (as part of BPM 11g which runs on top of 11g PS2 SOA core), including design time and runtime

Check his blog for details on the new features!
Original blog

osb

Oracle Service Bus, file transport, get filename from url

In Oracle Service Bus it’s relative easy to get the filename which has been processed by the proxy service.
On the $inbound context variable we can use the next xpath expression to get file uri

$inbound/ctx:transport/ctx:request/tp:headers/file:fileName

this will give us :

<file:fileName xmlns:file="http://www.bea.com/wli/sb/transports/file">c:\temp\testfile\1263968456285_test.xml</file:fileName>

Now i was struckling a bit on how to get just the filename from this url.
Since i couldn’t find any instring function in the bea xqueury function list i couldn’t use any construction like (without the sql commands)

select substr('/tmp/testfile/test.xml',instr('/tmp/testfile/test.xml','/',-1)+1)
from dual;

In this case the function tokenize can be used :

tokenize($inbound/ctx:transport/ctx:request/tp:headers/file:fileName, "/")[last()]

this will give me the last token in the list which is split up by the “/” sign, with as result “test.xml”.

In case your running it on a windows environment don’t forget to escape the backslash

tokenize($inbound/ctx:transport/ctx:request/tp:headers/file:fileName, "\\")[last()]

Original blog

fusion11g

Oracle Fusion, trigger events from within bpel

Browsing the samples of Clemence i came by a little feature didn’t knew about yet.
It was clear to me you could trigger the events from within the mediator.

route_event

To trigger an event from withing your bpel process, just add the invoke-activity, for interaction type select ‘Event’ and select the event you want to trigger.

invoke_event

So instead of invoking a partnerlink you’ll now invoke the event definition.

<invoke name="Invoke_1" bpelx:eventName="ns2:EnvironmentEvent" inputVariable="MyEventInput"/>

bpel, fusion11g

Oracle Fusion, BPEL and coordination of master- and detail processes

Because of a little illness Lucas was a bit faster with the publish of his article 🙂 but i decided to still post it on my blog.
In the new Fusion 11g, Oracle added a new extension to the bpel language for the coordination of master- and detail processes.
With this add on it is possible to relate a master process with his detail processes and give signals to each other when to start and when parts are finished.

Let’s start with building something
We created a new composite application and added 3 bpel processes to it.
The first process is the master process and this one will invoke 2 detail processes.
The idea is to let both detail process only get started when they receive a ‘signal’ from the master processes. If they receive it,
they’ll execute their own logic and when either finished or at some point they want to communicate back to the master process, they give a ‘signal’ back.

In the master process, after the the receive add the first signal

<bpelx:signal name="doDetail1" label="doDetail1" to="details"/>

This will give the first detail process a signal so it will be able to receive
Now we need to invoke the first detail process

        <invoke name="invokeDetail"
                inputVariable="invokeDetail_process_InputVariable"
                partnerLink="MyDetail.mydetail_client" portType="ns1:MyDetail"
                operation="process"
                bpelx:detailLabel="detailProcessComplete1"
                bpelx:invokeAsDetail="true">

Two things in here are needed. The first is the bpelx:invokeAsDetail=”true” and the second is the bpelx:detailLabel=”detailProcessComplete1″
The last property is used in cases in which we have multiple detail processes and the signals need to get correlated back with the master process.

Now lets see what is needed in the first detail process to receive the signal and eventually send one back to the master process.
The first activity after the receive is the receive signal

        <bpelx:receiveSignal name="WaitOnSignalFromMaster"
                             label="doDetail1" from="master"/>

This will receive the signal from its master process and after that will carry on. Without the signal the detail process won’t get started. So you will only be able to trigger this detail/sub process from it’s master process.

After receiving the signal the detail process can execute some logic and eventually send back the signal to the master process

        <bpelx:signal name="ReplyBackToMaster"
                      label="detailProcessComplete1" to="master"/>

In the master process we use the receiveSignal activity to receive it back from the detail process and go on with the process.

        <bpelx:receiveSignal name="waitForDetailProcess1"
                             label="detailProcessComplete1" from="details"/>

The same code is needed to invoke the second detail process. It’s up to you to decide wether or not the second detail process can run parallel to the first one.
If this is not the case we need to wait on the receiveSignal from the first detail process before we can start the second one.

If both processes can execute their logic and eventually send the signal back we can also receive both signals on the end of the master process. If both signals are receive we can do the callback because the whole process (master and all his details have been completed).
And in this case we can invoke the detail processes like oneway processes, but still inform the master process about the state of all these oneway (long running?) processes. At the end aggregate all the signals and do the callback or some other activity which is needed as soon all the detail processes reported back.

Testcase1
What will happen if we remove one of the receiveSignals from the Master process.

the master process will wait on the receivesignal activity until it receveid it.

Testcase2
What will happen when we do not send the signal from the master process to the detail process but leave the receiveSignal in the detail process.

The master process will wait on the receiveSignal from detail process1. Since the invoke of the both detail processes are oneway and we do the ‘check’ on the receiveSignals at the end, detail process1 will just complete and send the signal back to the master process. After this the master process will keep on waiting on the signal from detail process1.

Testcase3, when all just works



Eventually the flow of the master process can look like this.
In here i placed the receiveSignals to the end of the process. You can also place them after the invokes of the detail processes, so you’re sure they will wait on each other.

Resources
Amis weblog
Tom Hofte weblog
Oracle® Fusion Middleware Developer’s Guide for Oracle SOA Suite 11g Release 1 (11.1.1) – 15 Coordinating Master and Detail Processes

JDeveloper Project

Original blog

fusion11g

Oracle Domain Value Maps and Business Rules runtime edit with SOA Composer

Last week there was a question on the forum how to add values to the domain value maps on runtime.
In the Oracle SOA Suite 10g we could maintain those values from the console.
In Fusion 11g it’s a little bit different.

Oracle now supplies us a new tool called SOA Composer (patch set 1). This new tool can be used to maintain both rules and domain value maps at runtime.

  1. Create a new composite and add a dvm to it
  2. Create a new dvm

  3. Select the project, goto File > New > SOA Tier > Transformation > Domain Value Map

  4. Create the transformation in the mediator and connect the dvm
  5.   <xsl:template match="/">
        <inp1:CountryInfo>
          <inp1:Code>
            <xsl:value-of select="/inp1:Input/inp1:Code"/>
          </inp1:Code>
          <inp1:Fullname>
            <xsl:value-of select="dvm:lookupValue(&amp;quot;Countries.dvm&amp;quot;,&amp;quot;CODE&amp;quot;,/inp1:Input/inp1:Code,&amp;quot;Fullname&amp;quot;,'not mapped')"/>
          </inp1:Fullname>
        </inp1:CountryInfo>
      </xsl:template>
    

Now deploy the composite and do a little test to see if the dvm works.

In my case it generated the file ‘my_dvm_out_1.txt’ with the next content :

<?xml version="1.0" encoding="UTF-8" ?><inp1:CountryInfo xmlns:inp1="http://xmlns.oracle.com/singleString" xmlns="http://xmlns.oracle.com/singleString">
   <inp1:Code>NL</inp1:Code>
   <inp1:Fullname>Netherlands</inp1:Fullname>
</inp1:CountryInfo>

Test again from the console with the code DE. Content of my file :

<?xml version="1.0" encoding="UTF-8" ?><inp1:CountryInfo xmlns:inp1="http://xmlns.oracle.com/singleString" xmlns="http://xmlns.oracle.com/singleString">
   <inp1:Code>DE</inp1:Code>
   <inp1:Fullname>not mapped</inp1:Fullname>
</inp1:CountryInfo>

Time to add the dvm lookup for code DE.

SOA Composer
The SOA Composer can be found at: http://hostname:port_of_soa_managed_server/soa/composer
Login into the console and go to Open > Open DVM. Open the correct DVM.

Click Edit at top and click the green arrow to add a new lookup.


Last step is to commit the changed dvm (without redeploy).

Let’s test the process again and check the content of file.

<?xml version="1.0" encoding="UTF-8" ?><inp1:CountryInfo xmlns:inp1="http://xmlns.oracle.com/singleString" xmlns="http://xmlns.oracle.com/singleString">
   <inp1:Code>DE</inp1:Code>
   <inp1:Fullname>Deutschland</inp1:Fullname>
</inp1:CountryInfo>

Looks like it works.

Nice new tool from Oracle, available in the new patchset 1 of SOA Suite 11g.

Original blog

fusion11g

Install Oracle Fusion11g repositories on 11g db

The rcu installer does a db prerequisite check on the version of the db you’re installing on.
Since i’m using the 11.1.0.6.0 version the installer would fail with the message ‘The database you are connecting is not a supported version. Enter Database with version equal to or higher than 10.2.0.4.0 in 10g or version equal to higher than 11.1.0.7.0 in 11g. Refer to the certification matrix for supported DB versions’.

Go to the file

<rcuHome>\rcu\config\ComponentInfo.xml

and find this xml

<DBPrerequisite PREREQ_TYPE="CustomSQL" DATA_TYPE="NUMBER" COMPARE_OPERATOR="EQ">
        <ValidIf  DBTYPE="ORACLE" >
        <CustomQueryFilter DATA_TYPE="NUMBER" COMPARE_OPERATOR="EQ" VALUE="0">
            select 1 from dual where exists (select column_name from dba_tab_columns where table_name(+) like 'V_$INSTANCE' and column_name(+) = 'EDITION') union select 0 from dual where not exists (select column_name from dba_tab_columns where table_name(+) like 'V_$INSTANCE' and column_name(+) = 'EDITION')
          </CustomQueryFilter>
        </ValidIf>
        <PrereqIdentifier>select count(*) from product_component_version  where product like 'Oracle%Database%'  AND version BETWEEN '11' AND '11.1.0.6.0' </PrereqIdentifier>
        <PrereqValue>0</PrereqValue>
		<PrereqErrorMsg>
				The database you are connecting is not a supported version.  Enter Database with version equal to or higher than 10.2.0.4.0 in 10g or version equal to higher than 11.1.0.7.0 in 11g. Refer to the certification matrix for supported DB versions
		  </PrereqErrorMsg>
	</DBPrerequisite>

And change the value of ‘PrereqValue’ from 0 to 1.
Run install again and it will pass the check.

Thanks to Edwin for pointing me to the file 🙂

Original blog

osb

Jumpstart for Oracle Service Bus Development

The article which i wrote together with Ronald van Luttikhuizen is posted on the OTN site.
My first one for OTN and i’m proud of it.
otn
If you still have any questions about it or other related subjects, i will be happy to help you.

link :

Jumpstart for Oracle Service Bus Development
Download resources (OSB + Java)

Original blog

Page 7 of 11« First...56789...Last »