Tuesday, November 22, 2011

Exploring Spring Integration & Spring Web Service, Part 1

I have been learning Spring Integration and Spring Web Services on the project I am on. The more I learn the more I like the framework, but I have to say it has not been easy to make progress. The number of resources available is lacking. I thought I would spend the next several blogs covering some of the information I have been learning.

I needed to do was expose four SOAP Web Services based on a set of WSDL and XSD files provided by the vendor. First thing that had to happen was setting up the web.xml file. From reading the documentation on the main Spring site, I determined to use MessageDispatcherServlet.

web.xml

<web-app>
<display-name>My Application Name</display-name>
<servlet>
<servlet-name>si-ws-gateway</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:si-ws-gateway-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>si-ws-gateway</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>


Next, let's take a look at the spring application context file.
si-ws-gateway-config.xml

<bean class="org.springframework.ws.server.endpoint.mapping.UriEndpointMapping">
<property name="mappings">
<props>
<prop key="${data.subscriber.url}">dataGateway</prop>
<prop key="${request.status.url}">statusGateway</prop>
</property>
</bean>

<si:channel id="dataArrivedChannel" />
<si-ws:inbound-gateway id="dataGateway" request-channel="dataArrivedChannel" />
<stream:stdout-channel-adapter id="stdoutAdaptorWithDefaultCharsetDataArrived" channel="dataArrivedChannel"/>

<si:channel id="statusChangedChannel" />
<si-ws:inbound-gateway id="statusChangedGateway"
request-channel="statusChangedChannel"
marshaller="marshaller"
unmarshaller="marshaller"/>
<si:service-activator input-channel="statusChangedChannel"
ref="statusServiceEndpoint"
method="processStatusMessage"/>

<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.ourvendor.StatusUpdate</value>
</list>
</property>
</bean>

<bean id="StatusDao" class="com.company.StatusDao"/>

<bean id="statusServiceEndpoint"
class="com.company.StatusServiceEndpoint">
<constructor-arg ref="statusManager"/>
</bean>

<bean id="statusManager"
class="com.company.StatusManagerImpl">
<constructor-arg ref="statusDao"/>
</bean>



Well that seems like a lot of spring configuration, but it eliminates a bunch of Java code. Let's break down this configuration a bit. First, the UriEndpointMapping bean. You might notice that there are some values provided from a properties file. I will be covering properties in more detail in a future blog. For now, just know spring replaces the ${data.subscriber.url} and ${request.status.url} with a valid URL value from a properties file. This defines which URLs will be routed to the specified spring integration web services inbound-gateway. The gateway will place the SOAP message onto the specified channel as a DOMSource object.

As seen above the dataGateway does not specify a marshaller and unmarshaller which is why the DOMSource object is placed on the channel. The statusChangedGateway does specify the marshaller and unmarshaller, so when it places the object on the channel, it is not a DOMSource object, but a StatusUpdate object. As you can see it is very easy to leave the input in it's native format or convert it to a Java Object. In one case the object was only going to be routed(eventually) to a JMS queue, and did not need to be unmarshalled, in the other case it required unmarshalling so that a service activator endpoint could save the object to the database. As we will see in a future blog, it would have been nice if there was a way to specify the output to the channel as a string, instead of DOMSource. This would be a great enhancement to spring integration.

The service activator is an endpoint that allows a specific method to be called, and passes the object from the incoming channel to the method. It can also place the object, either unmodified or modified, onto an outgoing channel to be further processed by the system. The rest of the configuration is very straightforward spring beans with dependency injection. This works very well if each of the web services have a unique URL, but what happens if all calls come into a single URL. That will be the topic of the next blog in this series.

Tuesday, November 8, 2011

Accessing External Properties Files in WebLogic

It is not only common to do, but is also a very good practice to put values that may need to change into a properties file. This creates a central location to modify values. Especially for the configuration a system based on the installation location, etc. Having the properties file inside the .war is very inconvenient when you want to change it as it causes you to recompile your code, even if it hasn't changed. I was on a project that was able to pull the property file out of the .war and put it in the JBoss AS conf directory. The application could still access it. Unfortunately the project I am on now uses WebLogic. After lots of google searching and asking a few co-workers if they new how to do it, I did find the answer.

Simply place the properties file in $WEBLOGICHOME/user_projects/domains/mydomain where mydomain is the name of he domain your app uses.

Works great and now I can change properties all day long without recompiling.