Archive for the ‘ SOA ’ Category

The ColdFusion ServiceRunner - Part I

I’ve been meaning to share this for many reasons, but I’ve been bogged down with projects lately. However, it has allowed the code to mature and get streamlined a bit, so it’s good that some time has passed.

This is a pattern that we’ve come up with at work to handle remote calls to ColdFusion. It is one part of our SOA initiative that allows ColdFusion and Flex developers to make service calls to our core business components using a common approach.

I’ll start at the high level and work my way down. The reason I started this project is because early on I started to notice that there were many common entities used throughout the gamut of application used by my business unit - Facilities, Teammates, Drugs, Treatments, Patients, Doctors, etc…

Each application developer had written their own code to handle retrieving information on each of these entities. I’m sure you can see where the problem is in that, so I won’t belabor the point. To stop the fragmentation, I began diagramming how to create a common service application that could be accessed by any client in the company using a simple interface.

The Remote Call Service - RemotingService.cfc

Let’s start off with the first part of the puzzle: the simple interface for calling ColdFusion Component methods in our service application.

<cfcomponent output="false">

	<cffunction name="invokeService" access="remote" returntype="Any">
		<cfargument name="eventName" type="string" required="true" />
		<cfargument name="eventData" type="struct" required="false" default="#structNew()#" />

		<cfreturn application.beanFactory.getBean("ServiceRunner").run(argumentCollection=arguments) />
	</cffunction>

</cfcomponent>

Can’t get more simple than this. Using any remoting technology supporting SOAP, any client can invoke this component’s invokeService() method. This is the one entry point into our service application. As I’m sure you noticed by the application.beanFactory code, we’re using ColdSpring to manage all of our dependencies, AOP features, and event maps (more on this later).

The Service Response Value Object - ResponseVO.cfc

The next concept we tackled was not returning complex objects; we wanted a simple and logical structure to what gets returned to the client. Also we didn’t want the response object sitting around in memory sucking up RAM on the machine. We wanted this object to be created and destroyed on each remote request (shows below in the ServiceRunner code).

What got created was a simple Value Object that defines the keys of the response: name of the event, a success boolean, the data object, and an error structure (which is only populated if an error occurs).

<cfcomponent output="false"
	hint="Value object that is returned for every response">

	<cfproperty name="eventName"  type="string" />
	<cfproperty name="success"    type="boolean" />
	<cfproperty name="data"       type="any" />
	<cfproperty name="error"      type="Struct"/>

	<cfset this.eventName = "" />
	<cfset this.success = true />
	<cfset this.data = "" />
	<cfset this.error = structNew() />

</cfcomponent>

The Service Runner - ServiceRunner.cfc

Once we’d defined how services would be called and what the response would be, we then had to write the mechanism to actually execute the method and populate the response VO. The service runner is defined as a bean via ColdSpring (shown below) and is instantiated from the RemotingService component (shown above).

The RemotingService then calls the public run() method which creates a VO, executes the requested event using the private runEvent() method, and then returns the populated VO.

<cfcomponent output="false">

	<cffunction name="run" access="public" returntype="Any" output="false" description="Responds to remote request">
		<cfargument name="eventName" type="string" required="true" />
		<cfargument name="eventData" type="Struct" required="false" default="#structNew()#" />

		<cfset var local = StructNew() />
		<cfset var vo = createObject("component", "utility.remoting.ResponseVO") />

		<cfset vo.success 	= true />
		<cfset vo.eventName = arguments.eventName />

		<cftry>
			<cfset local.eventData = arguments.eventData />

			<cfif application.beanFactory.containsBean("event.#arguments.eventName#")>
				<cfset vo.data = runEvent(eventName=arguments.eventName, eventData=arguments.eventData) />
			<cfelse>
				<cfthrow detail="Event definition not found" message="event.#arguments.eventName# was not found." />
			</cfif>

			<cfcatch type="any">
				<cfset vo.success 	= false />
				<cfset vo.data 		= "" />
				<cfset vo.error 	= cfcatch />
			</cfcatch>
		</cftry>

		<cfreturn vo />
	</cffunction>

	<cffunction name="runEvent" access="private" returntype="any" output="false" hint="Calls the event defined in the EventMaps definition file">
		<cfargument name="eventName" type="string" required="true" />
		<cfargument name="eventData" type="Struct" required="true" />

		<cfset var local = structNew() />
		<cfset local.response = "" />

		<cfset local.eventInfo = application.beanFactory.getBean("event.#arguments.eventName#") />
		<cfset local.eventHandler = application.beanFactory.getBean(local.eventInfo.getBeanName()) />

		<cfinvoke component="#local.eventHandler#" method="#local.eventInfo.getMethodName()#" returnvariable="local.response" argumentcollection="#arguments.eventData#" />

		<cfreturn local.response />
	</cffunction>

</cfcomponent>

Defining Events as Beans - EventMaps.xml

To define our events, we create a bean for each one using the RemoteEventMap.cfc as the template for each one. The RemoteEventMap (code shown below) has two properties: beanName and methodName. These are used in the runEvent() method of the ServiceRunner (shown above) to obtain the properties needed for the CFINVOKE tag that executes the actual method we need.

<!DOCTYPE beans SYSTEM "ColdSpring.dtd"
[
   <!ENTITY appName "yourAppName">
   <!ENTITY appCFCMapping "&appName;.model">
   <!ENTITY eventCFCMap "utility.remoting.RemoteEventMap">
]>
<beans>
   <bean id="ServiceRunner" class="coldspring.aop.framework.ProxyFactoryBean">
      <property name="target">
         <bean class="utility.remoting.ServiceRunner" />
      </property>
      <property name="interceptorNames">
         <list>
            <value>metricsAdvisor</value>
         </list>
      </property>
   </bean>

   <bean id="event.getFacilities" class="&eventCFCMap;">
      <property name="beanName">
         <value>FacilityService</value>
      </property>
      <property name="methodName">
         <value>getHeldFacilities</value>
      </property>
   </bean>
</beans>

The Event Bean - RemoteEventMap.cfc

This is the simple RemoteEventMap that each event creates as a bean.

<cfcomponent output="false">

	<cffunction name="init" access="public" output="false" returntype="RemoteEventMap">
		<cfset variables.beanName = "" />
		<cfset variables.methodName = "" />

		<cfreturn this />
	</cffunction>

	<cffunction name="getBeanName" access="public" output="false" returntype="string">
		<cfreturn variables.beanName />
	</cffunction>

	<cffunction name="setBeanName" access="public" output="false" returntype="void">
		<cfargument name="beanName" type="string" required="true" />

		<cfset variables.beanName = arguments.BeanName />
	</cffunction>

	<cffunction name="getMethodName" access="public" output="false" returntype="string">
		<cfreturn variables.methodName />
	</cffunction>

	<cffunction name="setMethodName" access="public" output="false" returntype="void">
		<cfargument name="methodName" type="string" required="true" />

		<cfset variables.methodName = arguments.methodName />
	</cffunction>

</cfcomponent>

In Part II - which I will hopefully finish up tomorrow - I will show implementation examples of this pattern for ColdFusion and for Flex.

Setting up a private UDDI on JBoss

As part of the Service Oriented Architecture (SOA) that I’m implementing at work, I wanted to investigate setting up a private UDDI so that developers across the country could publish and find any core services. The first hit on Google when searching on the subject is Apache jUDDI, so I started to investigate that (since I love ASF). First thing I notice is that it’s a WAR file, but our platform here is based on ColdFusion, and the standalone installation used in the company couldn’t support implementing it. First thing that pops into my head, of course, is “Let’s look at my old friend, JBoss”.

jUUDI and JBoss Intro

After a quick install of JBoss, I drop in the jUDDI application and immediately run into configuration problems that I won’t bore you with now. However, as I start my research, I very quickly discover that jUDDI is integrated into JBoss! So at this point, the folks at JBoss go up a notch in my esteem.

I then drop the standard jUDDI application from JBoss and copy over the juddi-service.sar to my implemented server and fire it up. First thing I notice is that it’s looking for a JNDI datasource, which I had never set up before, but discover it’s quite simple.

Installing a MySQL UDDI Datastore

  1. Download and install MySQL
  2. Download the 0.9RC4 version of jUDDI
  3. Extract the jUDDI zip and look in the sql directory to get the script for setting up the juddi database
  4. Make sure you have the jboss-local-jdbc.rar package in your JBoss deploy directory
  5. Download the MySQL connector (mysql-connector-java-5.1.5-bin.jar) and put it in the server\lib directory
  6. Create a file named juddi-ds.xml in your deploy directory and create your JNDI datasource.
    Listing 1.1

    <datasources>
       <local-tx-datasource>
          <jndi-name>juddiDB</jndi-name>
          <connection-url>jdbc:mysql://{mysql server ip}:3306/juddi</connection-url>
          <driver-class>com.mysql.jdbc.Driver</driver-class>
          <user-name>{username}</user-name>
          <password>{password}</password>
          <min-pool-size>5</min-pool-size>
          <max-pool-size>20</max-pool-size>
       </local-tx-datasource>
    </datasources> 
  7. Open up the jboss\server\default\deploy\juddi-service.sar\juddi.war\WEB-INF\jboss-web.xml file and change the jndi-name property to refer to the name you just created
    <?xml version="1.0" encoding="ISO-8859-1"?>
    
    <!DOCTYPE jboss-web PUBLIC
            "-//JBoss//DTD Web Application 2.3V2//EN"
        "http://www.jboss.org/j2ee/dtd/jboss-web_4_0.dtd">
    
    <jboss-web>
        <context-root>juddi</context-root>
        <resource-ref>
            <res-ref-name>jdbc/juddiDB</res-ref-name>
            <jndi-name>java:/juddiDB</jndi-name>
        </resource-ref>
    </jboss-web>

Ok, so now I’ve got a database set up to store all of the UDDI information… now what? Let’s see if jUDDI started up correctly. I hit http://localhost/juddi and - SHAZAM! - it works: I get a standard “Welcome to JBoss JUDDI” screen. Now I try to publish a service to it. How do I do that?

Some more Googling gets me to the Eclipse Web Services Explorer, which is part of the Web Tools Platform. I install all of the packages and launch the explorer. I test a sample WSDL and it works great, so far so good. Then I try to publish a service to the UDDI and I start getting exceptions in the interface. I check the logs and see the following exceptions …

ERROR [JUDDIServlet] java.lang.IllegalStateException: Failed to load javax.xml.soap.MessageFactory: org.jboss.ws.core.soap.MessageFactoryImpl
ERROR [JUDDIServlet] A serious error has occured while assembling the SOAP Fault.

Back to Google I go and 30 minutes later I finally find some relevant discussions and possible resolutions. What it boils down to is you have to override the org.jboss.ws.core.soap.MessageFactoryImpl class with com.sun.xml.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl.

How do you do this?

Implementing SOAP with Attachments API for Java (SAAJ)

Go to the SAAJ project site and download the latest SAAJ release. Extract the zip file and copy the two files in the lib directory…

  • saaj-api.jar
  • saaj-impl.jar

to your deploy\lib directory.

Open the jboss\server\default\deploy\properties-service.xml file and set some system properties.

<mbean code="org.jboss.varia.property.SystemPropertiesService"
	 name="jboss:type=Service,name=SystemProperties">

    
    <attribute name="Properties">
      javax.xml.soap.MessageFactory=com.sun.xml.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl
      javax.xml.soap.SOAPFactory=com.sun.xml.messaging.saaj.soap.ver1_1.SOAPFactory1_1Impl
    </attribute>
</mbean>

Now restart JBoss and try to publish your service again via the Web Services Explorer (or whatever tool you want to use) and it should work.