Suresh Rohan's Blog

This blog is all about Java, J2EE,Spring, Angular, React JS, NoSQL, Microservices, DevOps, BigData, Tutorials, Tips, Best practice, Interview questions, Views, News, Articles, Techniques, Code Samples, Reference Application and much more

Tuesday, April 16, 2013

Web Services and XML Marshalling in Spring Integration


Spring Integration supports supplying a marshaller and unmarshaller for the inbound and outbound web services gateways. This allows using domain objects instead of working directly with the XML documents. This technology is also known as object/XML mapping (OXM). The marshalling library maps the domain object properties to the XML elements.


To implement gateway endpoints using XML marshalling, specify the marshaller and unmarshaller attributes on the inbound-gateway and outbound-gateway. 


Spring supports a variety of marshallers that leverage different XML-marshalling APIs


we will use Castor (www.castor.org) as the marshaller. Using other XML marshalling
APIs with Spring is very similar. Castor supports working from existing data models or generating the domain objects from the XML schema.


Castor provides a Maven plug-in that will generate the domain classes to support a marshaller between the object and XML representation. The Maven plug-in configuration
The location of the XML schema and the target package name are configured for the plug-in.

Castor Source Generation Plug-In pom.xml


<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>castor-maven-plugin</artifactId>
<version>2.0</version>
<configuration>
<schema>src/main/resources/Ticket.xsd</schema>
<packaging>com.apress.prospringintegration.webservice.domain</packaging>
<marshal>false</marshal>
</configuration>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>


Castor has a number of options for specifying the mappings between the domain objects and the XML document. In this example, we will be using the descriptor class to define the mappings. The generated domain classes for the TicketRequest and TicketResponse 

TicketRequest Domain Class Created by Castor (Comments Removed for Brevity)


package com.apress.prospringintegration.webservice.domain;
@SuppressWarnings("serial")
public class TicketRequest implements java.io.Serializable {
private java.lang.String _description;
private com.apress.prospringintegration.webservice.domain.types.PriorityType _priority;
public TicketRequest() {
super();
}
public java.lang.String getDescription(
) {
return this._description;
}
public com.apress.prospringintegration.webservice.domain.types.PriorityType getPriority(
) {
return this._priority;
}

public void setDescription(
final java.lang.String description) {
this._description = description;
}
public void setPriority(
final com.apress.prospringintegration.webservice.domain.types.PriorityType 􀀁
priority) {
this._priority = priority;
}
}

TicketResponse Domain Class Created by Castor (Comments Removed for Brevity)


package com.apress.prospringintegration.webservice.domain;
@SuppressWarnings("serial")
public class TicketResponse implements java.io.Serializable {
private com.apress.prospringintegration.webservice.domain.Ticket _ticket;
public TicketResponse() {
super();
}
public com.apress.prospringintegration.webservice.domain.Ticket getTicket(
) {
return this._ticket;
}
public void setTicket(
final com.apress.prospringintegration.webservice.domain.Ticket ticket) {
this._ticket = ticket;
}
}

An example of a generated Castor descriptor class for the TicketResponse domain class

Example of a Castor Descriptor Class (Some Comments Removed for Brevity)


package com.apress.prospringintegration.webservice.domain.descriptors;
import com.apress.prospringintegration.webservice.domain.TicketResponse;
public class TicketResponseDescriptor extends org.exolab.castor.xml.util􀀁
.XMLClassDescriptorImpl {
private boolean _elementDefinition;
private java.lang.String _nsPrefix;
private java.lang.String _nsURI

private java.lang.String _xmlName;
private org.exolab.castor.xml.XMLFieldDescriptor _identity;
public TicketResponseDescriptor() {
super();
_nsURI = "http://prospringintegration.com/tk/schemas";
_xmlName = "TicketResponse";
_elementDefinition = true;
//-- set grouping compositor
setCompositorAsSequence();
org.exolab.castor.xml.util.XMLFieldDescriptorImpl desc = null;
org.exolab.castor.mapping.FieldHandler handler = null;
org.exolab.castor.xml.FieldValidator fieldValidator = null;
//-- initialize attribute descriptors
//-- initialize element descriptors
//-- _ticket
desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
com.apress.prospringintegration.webservice.domain.Ticket.class, "_ticket", 􀀁
"ticket",
org.exolab.castor.xml.NodeType.Element);
handler = new org.exolab.castor.xml.XMLFieldHandler() {
@Override
public java.lang.Object getValue(java.lang.Object object)
throws IllegalStateException {
TicketResponse target = (TicketResponse) object;
return target.getTicket();
}
@Override
public void setValue(java.lang.Object object, java.lang.Object value)
throws IllegalStateException, IllegalArgumentException {
try {
TicketResponse target = (TicketResponse) object;
target.setTicket((
com.apress.prospringintegration.webservice.domain.Ticket) value);
} catch (java.lang.Exception ex) {
throw new IllegalStateException(ex.toString());
}
}
@Override
@SuppressWarnings("unused")
public java.lang.Object newInstance(java.lang.Object parent) {
return new com.apress.prospringintegration.webservice.domain.Ticket();
}
};
desc.setSchemaType("com.apress.prospringintegration.webservice.domain.Ticket");
desc.setHandler(handler);
desc.setNameSpaceURI("http://prospringintegration.com/tk/schemas");
desc.setRequired(true);
desc.setMultivalued(false);

addFieldDescriptor(desc);
addSequenceElement(desc);
//-- validation code for: _ticket
fieldValidator = new org.exolab.castor.xml.FieldValidator();
fieldValidator.setMinOccurs(1);
{ //-- local scope
}
desc.setValidator(fieldValidator);
}
@Override()
public org.exolab.castor.mapping.AccessMode getAccessMode(
) {
return null;
}
@Override()
public org.exolab.castor.mapping.FieldDescriptor getIdentity(
) {
return _identity;
}
@Override()
public java.lang.Class getJavaClass(
) {
return com.apress.prospringintegration.webservice.domain.TicketResponse.class;
}
@Override()
public java.lang.String getNameSpacePrefix(
) {
return _nsPrefix;
}
@Override()
public java.lang.String getNameSpaceURI(
) {
return _nsURI;
}
@Override()
public org.exolab.castor.xml.TypeValidator getValidator(
) {
return this;
}
@Override()
public java.lang.String getXMLName(
) {
return _xmlName;
}
public boolean isElementDefinition(

) {
return _elementDefinition;
}
}


With the domain objects and mappings created, the Spring Integration gateway can be configured to use Castor as the marshaller. The Spring configuration for the gateway example using Castor as the marshalling library


Spring Configuration File for the Web Services Example Using Marshalling ticket-wsservlet.xml


<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:ws="http://www.springframework.org/schema/integration/ws"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/integration/ws
http://www.springframework.org/schema/integration/ws/spring-integration-ws-2.0.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-2.0.xsd">

<context:component-scan
base-package="com.apress.prospringintegration.webservice.web"/>
<context:component-scan
base-package="com.apress.prospringintegration.webservice.service"/>

<int:channel id="inboundOXMTicketRequest"/>

<ws:inbound-gateway id="wsInboundGateway"
request-channel="inboundOXMTicketRequest"
marshaller="castorMarshaller"
unmarshaller="castorMarshaller"/>

<int:service-activator input-channel="inboundOXMTicketRequest"
ref="ticketIssuerMarshallingEndpoint"/>

</beans>














The Castor marshaller will be used to handle the marshalling and unmarshalling processes. The org.springframework.oxm.castor.CastorMarshaller reference is created using Java configuration

Java Configuration for Creating a Castor Marshaller Instance


package com.apress.prospringintegration.webservice.web;
import com.apress.prospringintegration.webservice.domain.TicketRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.castor.CastorMarshaller;
@Configuration
public class CastorConfiguration {
@Bean
public CastorMarshaller castorMarshaller() {
CastorMarshaller castorMarshaller = new CastorMarshaller();
castorMarshaller.setTargetClass(TicketRequest.class);
return castorMarshaller;
}
}


The key property for the inbound gateway is setting the targetClass property
with the base domain object that needs to be parsed TicketRequest. Once Castor knows the base object, it will find the child domain objects and descriptor files needed for parsing and mapping the XML request.


The inbound gateway uses the Castor marshaller to obtain the TicketRequest instance and forwards the instance on as a message payload to the inboundOXMTicketRequest channel. The message is then picked up by the service activator

Gateway Service Activator That Issues Tickets


package com.apress.prospringintegration.webservice.web;
import com.apress.prospringintegration.webservice.domain.Ticket;
import com.apress.prospringintegration.webservice.domain.TicketRequest;
import com.apress.prospringintegration.webservice.domain.TicketResponse;
import com.apress.prospringintegration.webservice.service.TicketIssuerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.stereotype.Component;
@Component
public class TicketIssuerMarshallingEndpoint {
@Autowired
private TicketIssuerService ticketIssuerService;
@ServiceActivator
public TicketResponse handleRequest(TicketRequest tr) throws Exception {
System.out.println("TicketRequest: " + tr);
TicketResponse ticketResponse = new TicketResponse();
Ticket t = ticketIssuerService.issueTicket(tr.getDescription(),
tr.getPriority().name());
ticketResponse.setTicket(t);
return ticketResponse;
}
}


Note that the incoming message to the service activator is the TicketRequest instance. Because of the Castor marshaller, there is no need to parse any XML document.

The ticketIssuerService component is used to generate the ticket with a
unique ID and issue data time. The service activator then returns a TicketResponse object back to the gateway, and it is forwarded back to the client.

Ticket Issuer Service Component


package com.apress.prospringintegration.webservice.service;
import com.apress.prospringintegration.webservice.domain.Ticket;
import com.apress.prospringintegration.webservice.domain.types.PriorityType;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.Random;
@Component
public class TicketIssuerService {
public Ticket issueTicket(String description, String priority) throws Exception {
Ticket ticket = new Ticket();
ticket.setDescription(description);
ticket.setPriority(PriorityType.valueOf(priority));
ticket.setTicketId(new Random().nextLong() * 1000);
ticket.setIssueDateTime(new Date());
return ticket;
}
}











No comments:

Post a Comment