Handling Oracle Stored Procedures with Arrays or Table Types in Spring

Hello guys, in this post lets discuss how to handle oracle procedure that take custom or array types as input params and return array type or table type as out param.

this example has been tested with spring 3.2.x and with oracle 10g xe.
note : run the below scripts in oracle to create objects prior to coding java.

-- custom type
create or replace TYPE "KEYVALUEOBJECT"
as object(name varchar(255),
value varchar(255));
================================================================

-- array of KeyValueObject
create or replace
TYPE "MY_OBJECT_ARRAY"
as table of KeyValueObject;

================================================================
-- this proc, doesn't do too much
create or replace
procedure Sample_Proc(
i_array in MY_OBJECT_ARRAY,
o_array out MY_OBJECT_ARRAY)
as
begin
o_array := MY_OBJECT_ARRAY();
for i in 1 .. i_array.count loop

o_array.extend;
o_array(i) := KeyValueObject(i_array(i).name, i_array(i).value);
end loop;
end;

applicationContext.java

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:jms="http://www.springframework.org/schema/jms" 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/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<bean id="propertyPlaceholderConfigurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>jdbc.properties</value>
			</list>
		</property>
	</bean>

	<bean id="springDataSource"
		class="org.springframework.jdbc.datasource.SingleConnectionDataSource">
		<property name="driverClassName" value="${db.driver}" />
		<property name="url" value="${db.url}" />
		<property name="username" value="${db.user}" />
		<property name="password" value="${db.password}" />
	</bean>

	<bean id="keyValueDao" class="com.spring.jdbc.dao.tab.type.impl.KeyValueDaoImpl">
		<constructor-arg name="dataSource" ref="springDataSource"></constructor-arg>
	</bean>

</beans>

KeyValueDao.java

package com.spring.jdbc.dao;

import com.sparing.jdbc.pojo.KeyValueObject;

/**
 * @author Sony
 *
 */
public interface KeyValueDao {

	public KeyValueObject[] getKeyValueObjects(KeyValueObject[] keyValueObjects);
	
}

KeyValueDaoImpl.java

/**
 * 
 */
package com.spring.jdbc.dao.tab.type.impl;

import javax.sql.DataSource;

import com.sparing.jdbc.pojo.KeyValueObject;
import com.spring.jdbc.dao.KeyValueDao;

/**
 * @author Sony
 *
 */
public class KeyValueDaoImpl extends TableTypeBaseProcedure implements KeyValueDao{

	public KeyValueDaoImpl(DataSource dataSource) {
		super(dataSource);
	}

	@Override
	public KeyValueObject[] getKeyValueObjects(KeyValueObject[] keyValueObjects) {
		
		return execute(keyValueObjects);
		
	}

}

the main business logic exist in this class.
TableTypeBaseProcedure.java

/**
 * 
 */
package com.spring.jdbc.dao.tab.type.impl;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import oracle.sql.ARRAY;

import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.SqlReturnType;
import org.springframework.jdbc.object.StoredProcedure;

import com.sparing.jdbc.pojo.KeyValueObject;

/**
 * @author Sony
 * 
 */
public class TableTypeBaseProcedure extends StoredProcedure {

	private static final String PROC_NAME = "Sample_Proc";
	private static final String TAB_TYPE = "KEYVALUEOBJECT";
	
	private static final String MY_ARRAY = "MY_OBJECT_ARRAY";
	private static final String I_ARRAY = "input_array";
	private static final String O_ARRAY = "output_array";


	public TableTypeBaseProcedure(DataSource dataSource) {
		
		super(dataSource, PROC_NAME);
		
		declareParameter(new SqlParameter(I_ARRAY, Types.ARRAY, MY_ARRAY));
		declareParameter(new SqlOutParameter(O_ARRAY, Types.ARRAY, MY_ARRAY,
				new SqlReturnType() {

					@Override
					public Object getTypeValue(CallableStatement cs,
							int paramIndex, int sqlType, String typeName)
							throws SQLException {
						Connection connection = cs.getConnection();
						Map<String, Class<?>> typeMap = connection.getTypeMap();
						typeMap.put(TAB_TYPE, KeyValueObject.class);
						return cs.getObject(paramIndex);
					}
				}));
		compile();
	}

	public KeyValueObject[] execute(KeyValueObject[] keyValueObjects) {
		Map<String, Object> params = new HashMap<>();
		params.put(I_ARRAY, new KeyValueSqlType(keyValueObjects));

		Map<?, ?> result = execute(params);

		if ((!result.containsKey(O_ARRAY) || result.get(O_ARRAY) == null)) {
			return null;
		}

		try {
			Object[] resultArray = (Object[]) ((ARRAY) result.get(O_ARRAY))
					.getArray();

			return Arrays.copyOf(resultArray, resultArray.length,
					KeyValueObject[].class);
		} catch (SQLException e) {
			throw new DataRetrievalFailureException("Unable to retrieve array",
					e);
		}
	}

}

this is a helper class
KeyValueSqlType.java

/**
 * 
 */
package com.spring.jdbc.dao.tab.type.impl;

import java.sql.Connection;
import java.sql.SQLException;

import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;

import org.springframework.jdbc.core.support.AbstractSqlTypeValue;

import com.sparing.jdbc.pojo.KeyValueObject;

/**
 * @author Sony
 *
 */
public class KeyValueSqlType extends AbstractSqlTypeValue{

	private KeyValueObject[] keyValueObjects;
	
	public KeyValueSqlType(KeyValueObject[] keyValueObjects) {
		this.keyValueObjects = keyValueObjects;
	}
	
	@Override
	protected Object createTypeValue(Connection connection, int sqlType,
			String typeName) throws SQLException {
		
		ArrayDescriptor arrayDescriptor = new ArrayDescriptor(typeName, connection);
		return new ARRAY(arrayDescriptor,connection,keyValueObjects);
	}

}

The main domain object that will flow in and out to database.
KeyValueObject.java

/**
 * 
 */
package com.sparing.jdbc.pojo;

import java.sql.SQLData;
import java.sql.SQLException;
import java.sql.SQLInput;
import java.sql.SQLOutput;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

/**
 * @author Sony
 * 
 */
public class KeyValueObject implements SQLData {

	private String name;
	private String value;

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name
	 *            the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the value
	 */
	public String getValue() {
		return value;
	}

	/**
	 * @param value
	 *            the value to set
	 */
	public void setValue(String value) {
		this.value = value;
	}

	@Override
	public String getSQLTypeName() throws SQLException {
		return "KeyValueObject";
	}

	@Override
	public void readSQL(SQLInput stream, String typeName) throws SQLException {

		name = stream.readString();
		value = stream.readString();
	}

	@Override
	public void writeSQL(SQLOutput stream) throws SQLException {

		stream.writeString(name);
		stream.writeString(value);
	}

	@Override
	public String toString() {
		return ToStringBuilder.reflectionToString(this,
				ToStringStyle.SHORT_PREFIX_STYLE);
	}

}

Happy Coding πŸ™‚
Happy Oracle Stored Procedures with Arrays or Table Types in Spring πŸ™‚

Advertisements

MTOM With JAX-WS

Hi All, what if you want to send large binary data using web services we have SOAP with attachments i.e. MTOM(Message Transmission Optimization Mechanism).SOAP Message Transmission Optimization Mechanism (MTOM) specifies an optimized method for sending binary data as part of a SOAP message. Unlike SOAP with Attachments, MTOM requires the use of XML-binary Optimized Packaging (XOP) packages for transmitting binary data. Using MTOM to send binary data does not require you to fully define the MIME Multipart/Related message as part of the SOAP binding. It does, however, require that you do the following:

1. Annotate the data that you are going to send as an attachment.
2. You can annotate either your WSDL or the Java class that implements your data.
3. Enable the runtime’s MTOM support.
4. This can be done either programatically or through configuration.
5. Develop a DataHandler for the data being passed as an attachment.

below is the code snippet for MTOM implementation, follow the comments closely in the code.

/**
 * 
 */
package com.webservice.endpoint.interfaces;

import java.awt.Image;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;

/**
 * @author Sony
 *
 */
@WebService
@SOAPBinding(style = Style.RPC)
public interface BinaryDataDistribution {

	@WebMethod
	public Image downloadBinaryData(String name);
	
	@WebMethod
	public String uploadBinaryData(Image data);
}
/**
 * 
 */
package com.webservice.endpoint.interfaces.impl;

import java.awt.Image;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.soap.MTOM;

import com.webservice.endpoint.interfaces.BinaryDataDistribution;

/**
 * @author Sony
 * 
 */
@MTOM
@WebService(endpointInterface = "com.webservice.endpoint.interfaces.BinaryDataDistribution")
public class BinaryDataDistributionImpl implements BinaryDataDistribution {

	
	 // (non-Javadoc)
	  
	 // @see
	 // com.webservice.endpoint.interfaces.BinaryDataDistribution#downloadBinaryData
	 // (java.lang.String)
	 
	@Override
	@WebMethod
	public Image downloadBinaryData(String name) {
		try {

			File image = new File("c:\\images\\" + name);
			return ImageIO.read(image);

		} catch (IOException e) {

			e.printStackTrace();
			return null;

		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.webservice.endpoint.interfaces.BinaryDataDistribution#uploadBinaryData
	 * (java.awt.Image)
	 */
	@Override
	@WebMethod
	public String uploadBinaryData(Image data) {
		if (data != null) {
			// saving logic goes here !
			return "Upload Successful";
		}

		throw new WebServiceException("Upload Failed!");
	}

}
/**
 * 
 */
package com.webservice.publish;

import javax.xml.ws.Endpoint;

import com.webservice.endpoint.interfaces.impl.BinaryDataDistributionImpl;

/**
 * @author Sony
 * 
 */
public class PublishService {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Endpoint.publish("http://localhost:9999/ws/image",
				new BinaryDataDistributionImpl());

		System.out.println("Server is published!");
	}

}

Now the Wsdl should look as below.

  <?xml version="1.0" encoding="UTF-8" ?> 
- <!--  Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.4-b01. 
  --> 
- <!--  Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.4-b01. 
  --> 
- <definitions xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://impl.interfaces.endpoint.webservice.com/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://impl.interfaces.endpoint.webservice.com/" name="BinaryDataDistributionImplService">
  <import namespace="http://interfaces.endpoint.webservice.com/" location="http://localhost:9999/ws/image?wsdl=1" /> 
- <wsp:Policy wsu:Id="BinaryDataDistributionImplPortBinding_MTOM_Policy">
  <ns1:OptimizedMimeSerialization xmlns:ns1="http://schemas.xmlsoap.org/ws/2004/09/policy/optimizedmimeserialization" wsp:Optional="true" /> 
  </wsp:Policy>
- <binding xmlns:ns2="http://interfaces.endpoint.webservice.com/" name="BinaryDataDistributionImplPortBinding" type="ns2:BinaryDataDistribution">
  <wsp:PolicyReference URI="#BinaryDataDistributionImplPortBinding_MTOM_Policy" /> 
  <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc" /> 
- <operation name="downloadBinaryData">
  <soap:operation soapAction="" /> 
- <input>
  <soap:body use="literal" namespace="http://interfaces.endpoint.webservice.com/" /> 
  </input>
- <output>
  <soap:body use="literal" namespace="http://interfaces.endpoint.webservice.com/" /> 
  </output>
  </operation>
- <operation name="uploadBinaryData">
  <soap:operation soapAction="" /> 
- <input>
  <soap:body use="literal" namespace="http://interfaces.endpoint.webservice.com/" /> 
  </input>
- <output>
  <soap:body use="literal" namespace="http://interfaces.endpoint.webservice.com/" /> 
  </output>
  </operation>
  </binding>
- <service name="BinaryDataDistributionImplService">
- <port name="BinaryDataDistributionImplPort" binding="tns:BinaryDataDistributionImplPortBinding">
  <soap:address location="http://localhost:9999/ws/image" /> 
  </port>
  </service>
  </definitions>

Happy coding πŸ™‚ “MTOM

Calling Oracle stored procedure using Spring JDBC

In this post lets discuss how to use spring jdbc api and Spring StoredProcedure classes to call Oracle Stored proc. please take a look at the below code snippet
declaring package

create or replace 
PACKAGE MY_PACKAGE
AS
-- ref cursor to return procedure results with an unlimited number of records
TYPE CUR_DATA IS REF CURSOR;
PROCEDURE MY_PROCEDURE(OUT_DATA OUT CUR_DATA);
END MY_PACKAGE;

implementing package body as follows

create or replace 
PACKAGE BODY MY_PACKAGE
AS
PROCEDURE MY_PROCEDURE(OUT_DATA OUT CUR_DATA)
IS
/* this procedure returns a Ref Cursor with all the requested parameters
calling the stored procedure from an asp page (and anywhere else)
does not require posting a predefined number of records */
BEGIN
OPEN OUT_DATA FOR
SELECT empno, ename, hiredate, job, sal, deptno
FROM EMP
ORDER BY empno;
END MY_PROCEDURE;
END MY_PACKAGE;

Now lets code the applicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:jms="http://www.springframework.org/schema/jms" 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/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<bean id="propertyPlaceholderConfigurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>jdbc.properties</value>
			</list>
		</property>
	</bean>

	<bean id="springDataSource"
		class="org.springframework.jdbc.datasource.SingleConnectionDataSource">
		<property name="driverClassName" value="${db.driver}" />
		<property name="url" value="${db.url}" />
		<property name="username" value="${db.user}" />
		<property name="password" value="${db.password}" />
	</bean>

	<bean id="employeeDao" class="com.spring.jdbc.dao.impl.EmployeeDaoImpl">
		<constructor-arg name="datasource" ref="springDataSource"></constructor-arg>
	</bean>

</beans>

here we code an interface and its implementation class called EmployeeDAO and EmployeeDAOImpl which has the business methods as follows.

/**
 * 
 */
package com.spring.jdbc.dao;

import java.util.Map;

/**
 * @author Sony
 *
 */
public interface EmployeeDAO {

	public  Map<String,Object> getEmployeeDetails();
}

EmployeeDaoImpl.java

package com.spring.jdbc.dao.impl;

import java.util.Map;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;

import com.spring.jdbc.dao.EmployeeDAO;

/**
 * @author Sony
 * 
 */
public class EmployeeDaoImpl extends BaseProcedure implements EmployeeDAO {

	/**
	 * @param datasource
	 */
	public EmployeeDaoImpl(DataSource datasource) {
		super(datasource);
	}
	
	/* (non-Javadoc)
	 * @see com.spring.jdbc.dao.EmployeeDAO#getEmployeeDetails(int)
	 */
	@Override
	public  Map<String,Object> getEmployeeDetails() {
		
		return execute();
	}

}

The above class extends a class “BaseProcedure.java” which is the actual logic for calling Oracle Stored Proc.

/**
 * 
 */
package com.spring.jdbc.dao.impl;

import java.util.Map;

import javax.sql.DataSource;

import oracle.jdbc.driver.OracleTypes;

import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.object.StoredProcedure;

/**
 * @author Sony
 *
 */
public class BaseProcedure extends StoredProcedure{

	 private static final String SPROC_NAME = "MY_PACKAGE.MY_PROCEDURE";
	 
     public BaseProcedure( DataSource datasource ){
             super( datasource, SPROC_NAME );
             declareParameter( new SqlOutParameter( "OUT_DATA", OracleTypes.CURSOR,new EmployeeRowMapper())); //declaring sql out parameter
             compile();
     }
    
     public  Map<String,Object> execute(){
             Map<String,Object> results = super.execute();
             return results; //reading output of stored procedure using out parameters
                                    
     }
}

Here now we discuss the EmployeeRowMapper.java which will map single rows with the Employee Object Properties as show below.
Employee.java

/**
 * 
 */
package com.sparing.jdbc.pojo;

import java.io.Serializable;
import java.sql.Date;

/**
 * @author Sony
 * 
 */
public class Employee implements Serializable {

	private int empId;
	private String eName;
	private Date hireDate;
	private String job;
	private long sal;
	private int deptNo;

	/**
	 * @return the empId
	 */
	public int getEmpId() {
		return empId;
	}

	/**
	 * @param empId
	 *            the empId to set
	 */
	public void setEmpId(int empId) {
		this.empId = empId;
	}

	/**
	 * @return the eName
	 */
	public String geteName() {
		return eName;
	}

	/**
	 * @param eName
	 *            the eName to set
	 */
	public void seteName(String eName) {
		this.eName = eName;
	}

	/**
	 * @return the hireDate
	 */
	public Date getHireDate() {
		return hireDate;
	}

	/**
	 * @param hireDate
	 *            the hireDate to set
	 */
	public void setHireDate(Date hireDate) {
		this.hireDate = hireDate;
	}

	/**
	 * @return the job
	 */
	public String getJob() {
		return job;
	}

	/**
	 * @param job
	 *            the job to set
	 */
	public void setJob(String job) {
		this.job = job;
	}

	/**
	 * @return the sal
	 */
	public long getSal() {
		return sal;
	}

	/**
	 * @param sal
	 *            the sal to set
	 */
	public void setSal(long sal) {
		this.sal = sal;
	}

	/**
	 * @return the deptNo
	 */
	public int getDeptNo() {
		return deptNo;
	}

	/**
	 * @param deptNo
	 *            the deptNo to set
	 */
	public void setDeptNo(int deptNo) {
		this.deptNo = deptNo;
	}

}

EmployeeRowMapper.java

/**
 * 
 */
package com.spring.jdbc.dao.impl;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

import com.sparing.jdbc.pojo.Employee;

/**
 * @author Sony
 *
 */
public class EmployeeRowMapper implements RowMapper<Object>{

	@Override
	public Object mapRow(ResultSet resultSet, int row) throws SQLException {
		Employee employee = new Employee();
		employee.setEmpId(resultSet.getInt(1));
		employee.seteName(resultSet.getString(2));
		employee.setHireDate(resultSet.getDate(3));
		employee.setJob(resultSet.getString(4));
		employee.setSal(resultSet.getLong(5));
		employee.setDeptNo(resultSet.getInt(6));
		return employee;
	}

}

now lets write a main class to test the call for stored proc as follows.

/**
 * 
 */
package com.spring.jdbc.dao.test;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.sparing.jdbc.pojo.Employee;
import com.spring.jdbc.dao.EmployeeDAO;

/**
 * @author Sony
 * 
 */
public class SpringJdbcTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
				"applicationContext.xml");
		EmployeeDAO employeeDAO = (EmployeeDAO) applicationContext
				.getBean("employeeDao");

		Map<String, Object> results = employeeDAO.getEmployeeDetails();

		Set<String> keys = results.keySet();
		Iterator<String> iterator = keys.iterator();
		List<Employee> employees = null;
		
		while (iterator.hasNext()) {
			String key = (String) iterator.next();
			employees = (List<Employee>) results.get(key);
		}

		for (Employee employee : employees) {
			System.out.println(employee.geteName());
		}

	}
}

finally let me expose my jdbc.properties as follows.

db.url=jdbc:oracle:thin:@localhost:1521:xe
db.driver=oracle.jdbc.driver.OracleDriver
db.user=xxxx
db.password=xxxx

Happy Coding and calling procs with Spring πŸ™‚

Spring Web Services using JAXB part-2

After the part-1 this is the contd part of it, now we are going to write the endpoint implementations and mapping these end points to the request from the web. so lets not waste time just jumping into the implementation part here we go.

Lets write AbstractPersonEndPoint class which will extend AbstractMarshallingPayloadEndpoint of Spring framework. Here we are going to just create PersonService object and define an abstract method invokeInternal of AbstractMarshallingPayloadEndpoint class.

/**
 * 
 */
package com.spark.springws.abstrct.endpoint;

import org.springframework.ws.server.endpoint.AbstractMarshallingPayloadEndpoint;

import com.spark.springws.service.PersonService;

/**
 * @author Sony
 *
 */
public abstract class AbstractPersonEndPoint extends AbstractMarshallingPayloadEndpoint {

	protected PersonService personService;
	
	/**
	 * @param personService the personService to set
	 */
	public void setPersonService(PersonService personService) {
		this.personService = personService;
	}


	/* (non-Javadoc)
	 * @see org.springframework.ws.server.endpoint.AbstractMarshallingPayloadEndpoint#invokeInternal(java.lang.Object)
	 */
	@Override
	protected abstract Object invokeInternal(Object request) throws Exception;

}

Now its time to create concrete endpoint classes which will have exact business logic implementation and will extend AbstractPersonEndPoint. I will show for one of the service and other implementations are left for you.

/**
 * 
 */
package com.spark.springws.endpoints;

import org.springws.beans.schema.AddPersonRequest;

import com.spark.springws.abstrct.endpoint.AbstractPersonEndPoint;

/**
 * @author Sony
 *
 */
public class AddPersonEndPoint extends AbstractPersonEndPoint {

	/* (non-Javadoc)
	 * @see com.spark.springws.abstrct.endpoint.AbstractPersonEndPoint#invokeInternal(java.lang.Object)
	 */
	@Override
	protected Object invokeInternal(Object request) throws Exception {

		AddPersonRequest addPersonRequest = (AddPersonRequest) request;
		personService.addPerson(addPersonRequest.getPerson());
		return null;
	}

}

now lets take a look at the spring-ws-servlet.xml which we already defined in this part-1 section and modify it a little bit as shown below.

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

	<bean id="personService" class="com.spark.springws.service.impl.PersonServiceImpl"
		init-method="initialize" />
	<bean id="Person"
		class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
		<property name="schema" ref="schema" />
		<property name="portTypeName" value="Person" />
		<property name="locationUri" value="/services" />
		<property name="targetNamespace" value="http://www.example.org/person/schema" />
	</bean>
	<bean id="schema" class="org.springframework.xml.xsd.SimpleXsdSchema">
		<property name="xsd" value="/WEB-INF/xsd/schemas/Person.xsd" />
	</bean>

	<bean id="validationInterceptor"
		class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
		<property name="xsdSchema" ref="schema"></property>
		<property name="validateRequest" value="true" />
		<property name="validateResponse" value="true" />
	</bean>

	<bean id="loggingInterceptor"
		class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor" />

	<oxm:jaxb2-marshaller id="marshaller"
		contextPath="org.springws.beans.schema" />
	<oxm:jaxb2-marshaller id="unmarshaller"
		contextPath="org.springws.beans.schema" />

	<bean id="addPersonEndpoint" class="com.spark.springws.endpoints.AddPersonEndPoint"
		autowire="byName" />
	<bean id="getAllPersonsEndpoint" class="com.spark.springws.endpoints.GetAllPersonsEndPoint"
		autowire="byName" />
	<bean id="getPersonEndpoint" class="com.spark.springws.endpoints.GetPersonEndPoint"
		autowire="byName" />

	<bean name="endpointMapping"
		class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
		<property name="interceptors">
			<list>
				<ref local="loggingInterceptor" />
				<ref local="validationInterceptor" />
			</list>
		</property>
		<property name="mappings">
			<props>
				<prop key="{http://www.example.org/person/schema}AddPersonRequest">addPersonEndpoint</prop>
				<prop key="{http://www.example.org/person/schema}GetAllPersonsRequest">getAllPersonsEndpoint</prop>
				<prop key="{http://www.example.org/person/schema}GetPersonRequest">getPersonEndpoint</prop>
				<prop key="{http://www.example.org/person/schema}UpdatePersonRequest">updatePersonEndpoint</prop> 
					<prop key="{http://www.example.org/person/schema}DeletePersonRequest">deletePersonEndpoint</prop>
			</props>
		</property>
	</bean>

	<bean id="exceptionResolver"
		class="org.springframework.ws.soap.server.endpoint.SoapFaultMappingExceptionResolver">
		<property name="defaultFault" value="SERVER" />
		<property name="exceptionMappings">
			<props>
				<prop key="org.springframework.oxm.ValidationFailureException">CLIENT,Invalid request</prop>
				<prop key="com.spark.springws.custom.exception.PersonException">SERVER</prop>
			</props>
		</property>
	</bean>
</beans>

once this is done just deploy the project and test the request and response from SOAP UI as shown below.
add-request

get-req-res

Happy Spring-Web services πŸ™‚

Spring Web Services using JAXB part-1

Hi all its long time i put pen on my blog, its now time to write some thing about Spring web services implementation. Web services implementation of Spring is the best product and now a days service driven projects are in more demand, spring web services are of two types 1.RESTful Services and SOAP services. Some of the key features of spring web services are as follows

a) XML API support: Incoming XML messages can be handled in standard JAXP APIs such as DOM, SAX, and StAX, but also JDOM, dom4j, XOM, or even marshalling technologies.

b) Flexible XML Marshalling: The Object/XML Mapping module in the Spring Web Services distribution supports JAXB 1 and 2, Castor, XMLBeans, JiBX, and XStream. And because it is a separate module, you can use it in non-Web services code as well.

c) Also, the architecture of Spring-WS resembles that of Spring-MVC.

d) Apache license. You can confidently use Spring-WS in your project.

Before moving into the coding part, the essential jars required by the project as as below.
jars

Now its time for us to define the service contract.
Service Contract

Here’s where we start our contract first approach, the Service contract. The wsdl contract can be happily generated by Spring’s DefaultWsdl11Definition later. so lets start with simple schema as below.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
	targetNamespace="http://www.example.org/person/schema" xmlns:per="http://www.example.org/person/schema"
	elementFormDefault="qualified">

	<xs:element name="Person">
		<xs:complexType>
			<xs:sequence>
				<xs:element name="FirstName" type="xs:string" />
				<xs:element name="LastName" type="xs:string" />
			</xs:sequence>
			<xs:attribute ref="per:Id" use="required" />
		</xs:complexType>
	</xs:element>

	<xs:attribute name="Id" type="xs:int" />

	<xs:element name="AddPersonRequest">
		<xs:complexType>
			<xs:sequence>
				<xs:element ref="per:Person" />
			</xs:sequence>
		</xs:complexType>
	</xs:element>

	<xs:element name="GetPersonRequest">
		<xs:complexType>
			<xs:attribute ref="per:Id" use="required" />
		</xs:complexType>
	</xs:element>

	<xs:element name="GetPersonResponse">
		<xs:complexType>
			<xs:sequence>
				<xs:element ref="per:Person" />
			</xs:sequence>
		</xs:complexType>
	</xs:element>

	<xs:element name="GetAllPersonsRequest">
		<xs:complexType>
			<xs:sequence />
		</xs:complexType>
	</xs:element>

	<xs:element name="GetAllPersonsResponse">
		<xs:complexType>
			<xs:sequence>
				<xs:element ref="per:Person" maxOccurs="unbounded" />
			</xs:sequence>
		</xs:complexType>
	</xs:element>

	<xs:element name="UpdatePersonRequest">
		<xs:complexType>
			<xs:sequence>
				<xs:element ref="per:Person" />
			</xs:sequence>
		</xs:complexType>
	</xs:element>

	<xs:element name="DeletePersonRequest">
		<xs:complexType>
			<xs:attribute ref="per:Id" use="required" />
		</xs:complexType>
	</xs:element>
</xs:schema>

once our schema is ready now lets write simple ant script to generate the jaxb classes and compile them using jaxb’s xjc command using as follows.

<?xml version="1.0" encoding="UTF-8"?>
<project name="Spring-Ws Project With JaxB,XMLBeans and Jibx Capability"
	default="all" basedir=".">
	
	<property file="./build.properties" />
	
	<property name="javac.source" value="1.6" />
	<property name="javac.target" value="1.6" />
	
	<path id="classpath" description="Classpath to web application">
		<fileset dir="${dir.webapp.lib}" includes="**/*.jar" />
		<pathelement location="bin" />
	</path>
	
	<target name="clean">
        <delete dir="${base.dir}/classes"/>
        <mkdir dir="${base.dir}/classes"></mkdir>
    </target>
    
	<target name="compile">
        <javac srcdir="${dir.src}/org/springws/beans/schema" destdir="${base.dir}/classes"/>
    </target>
	
	<target name="clean-compile" description="java xml binding tool" depends="clean,compile">
		<echo message="compiling JAXB classes from xsd" />
		<exec executable="${jdk.home}/bin/xjc.exe">
			<arg value="-d" />
			<arg value="${dir.src}" />
			<arg value="-p" />
			<arg value="org.springws.beans.schema" />
			<arg value="${xsd.dir}/Person.xsd" />
		</exec>
	</target>
</project>

build.properties

base.dir=.

jdk.home=C\:\\Oracle\\Middleware\\jdk160_24
dir.src=../../src
xsd.dir=${base.dir}/xsd/schemas

dir.webapp.lib=${base.dir}/lib

Now lets design our own custom exception class as follows.
PersonException.java

/**
 * 
 */
package com.spark.springws.custom.exception;

/**
 * @author Sony
 *
 */
public class PersonException extends Exception {

	private static final long serialVersionUID = 1L;

	public PersonException(String message) {
		super(message);
	}
}

now lets start with the service class and its implementation class as follows.
PersonService.java, PersonServiceImpl.java

/**
 * 
 */
package com.spark.springws.service;

import java.util.List;

import org.springws.beans.schema.Person;

import com.spark.springws.custom.exception.PersonException;

/**
 * @author Sony
 * 
 */
public interface PersonService {
	public void addPerson(Person person) throws PersonException;
	public Person getPerson(Integer id) throws PersonException;
	public List<Person> getAllPersons();
	public void updatePerson(Person person) throws PersonException;
	public void deletePerson(Integer id) throws PersonException;
}

/**
 * 
 */
package com.spark.springws.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springws.beans.schema.Person;

import com.spark.springws.custom.exception.PersonException;
import com.spark.springws.service.PersonService;

/**
 * @author Sony
 * 
 */
public class PersonServiceImpl implements PersonService {

	private Map dataSource;

	@Override
	public void addPerson(Person person) throws PersonException {
		if (idExists(person.getId())) {
			throw new PersonException("Cannot add person. Id already exists.");
		}
		dataSource.put(person.getId(), person);
	}

	
	@Override
	public Person getPerson(Integer id) throws PersonException {
		if (!idExists(id)) {
			throw new PersonException("Cannot get person. ID does not exist.");
		}
		return (Person) dataSource.get(id);
	}

	@Override
	public List<Person> getAllPersons() {
		return new ArrayList(dataSource.values());
	}

	@Override
	public void updatePerson(Person person) throws PersonException {
		if (!idExists(person.getId())) {
			throw new PersonException(
					"Cannot update person. ID does not exist.");
		}
		dataSource.put(person.getId(), person);
	}

	@Override
	public void deletePerson(Integer id) throws PersonException {
		if (!idExists(id)) {
			throw new PersonException(
					"Cannot delete person. Id does not exists.");
		}
		dataSource.remove(id);
	}

	public void initialize() {
		dataSource = new HashMap();
		putPerson(1, "Clark", "Kent");
		putPerson(2, "Bruce", "Wayne");
		putPerson(3, "Harold", "Jordan");
	}

	private void putPerson(Integer id, String firstName, String lastName) {
		Person person = new Person();
		person.setId(id);
		person.setFirstName(firstName);
		person.setLastName(lastName);
		dataSource.put(id, person);
	}

	private boolean idExists(Integer id) {
		return dataSource.containsKey(id);
	}

}

Now lets write web.xml file as follows.

<?xml version="1.0" encoding="ISO-8859-1"?> 

<!DOCTYPE web-app PUBLIC 
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" 
    "http://java.sun.com/dtd/web-app_2_4.dtd">

<web-app>
	<display-name>Person Service</display-name>
	<description>Simple CRUD like Application</description>

	<!-- Defines the Spring-WS MessageDispatcherServlet -->
	<servlet>
		<servlet-name>spring-ws</servlet-name>
		<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
		<init-param>
			<!-- Transform the location attributes in WSDLs -->
			<param-name>transformWsdlLocations</param-name>
			<param-value>true</param-value>
		</init-param>
	</servlet>

	<!-- Map all requests to this servlet -->
	<servlet-mapping>
		<servlet-name>spring-ws</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>
</web-app>

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

	<bean id="personService" class="com.spark.springws.service.impl.PersonServiceImpl"
		init-method="initialize" />
	<bean id="Person"
		class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
		<property name="schema" ref="schema" />
		<property name="portTypeName" value="Person" />
		<property name="locationUri" value="/services" />
		<property name="targetNamespace" value="http://www.example.org/person/schema" />
	</bean>
	<bean id="schema" class="org.springframework.xml.xsd.SimpleXsdSchema">
		<property name="xsd" value="/WEB-INF/xsd/schemas/Person.xsd" />
	</bean>
</beans>

my project structure looks as below.
project-struct

once comleted please deploy the project onto server and run this url we should see the contract wsdl in the browser.
http://localhost:8080/Spring-WS-Ant/Person.wsdl

lets see how to define the service-endpoint-implementation,endpoint-mapping,mapping request to beans in the next post
until then keep coding
Happy Java and web services πŸ™‚