Posts Tagged 'ws'

Simplifying kSOAP2 clients

kSOAP2 is a SOAP web service client library for constrained Java environments such as J2ME apps. Ksoap2-android is a fork of kSOAP2 for the Android platform. A typical ksoap2-android client code looks like this.

Inspired by the Spring JDBC template classes, there is below a template for simplifying your kSOAP2 clients:

KSoap2Template.java:

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

public class KSoap2Template {

	private static final ResponseMapper<Object> NULL_RESPONSE_MAPPER = new ResponseMapper<Object>() {
		public Object mapResponse(Object response) {
			return response;
		}
	};

	private static final RequestPropertiesSetter NULL_REQUEST_PROPERTIES_SETTER = new RequestPropertiesSetter() {
		public void setValues(SoapObject request) {
			// do nothing
		}
	};

	private String namespace;
	private String url;

	private String getUrl() {
		return url;
	}

	private String getNamespace() {
		return namespace;
	}

	public KSoap2Template(String url, String namespace) {
		this.url = url;
		this.namespace = namespace;
	}

	public void call(String methodName, String soapAction)
			throws ServiceAccessException {
		call(methodName, soapAction, NULL_REQUEST_PROPERTIES_SETTER);
	}

	public void call(String methodName, String soapAction,
			RequestPropertiesSetter requestPropertiesSetter)
			throws ServiceAccessException {
		call(methodName, soapAction, requestPropertiesSetter,
				NULL_RESPONSE_MAPPER);
	}

	public <T> T call(String methodName, String soapAction,
			ResponseMapper<T> responseMapper) throws ServiceAccessException {
		return call(methodName, soapAction, NULL_REQUEST_PROPERTIES_SETTER,
				responseMapper);
	}

	public <T> T call(String methodName, String soapAction,
			RequestPropertiesSetter requestPropertiesSetter,
			ResponseMapper<T> responseMapper) throws ServiceAccessException {
		try {
			SoapObject request = new SoapObject(getNamespace(), methodName);
			SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
					SoapEnvelope.VER11);
			envelope.setOutputSoapObject(request);
			HttpTransportSE transport = new HttpTransportSE(getUrl());
			requestPropertiesSetter.setValues(request);
			transport.call(soapAction, envelope);
			return responseMapper.mapResponse(envelope.getResponse());
		} catch (Exception e) {
			throw new ServiceAccessException(e);
		}
	}

}

ServiceAccessException.java:

public class ServiceAccessException extends RuntimeException {

	private static final long serialVersionUID = 1L;

	public ServiceAccessException(Throwable throwable) {
		super(throwable);
	}

}

RequestPropertiesSetter.java:

import org.ksoap2.serialization.SoapObject;

public interface RequestPropertiesSetter {

	void setValues(SoapObject request);

}

ResponseMapper.java:

public interface ResponseMapper<T> {

	T mapResponse(Object response);

}

AbstractResponseMapper.java:

package jbaris.wordpress.com.client.ksoap;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.ksoap2.serialization.PropertyInfo;
import org.ksoap2.serialization.SoapObject;

public abstract class AbstractResponseMapper<T> implements ResponseMapper<T> {

	protected <U> U[] getArray(Collection<Object> collection,
			ResponseMapper<U> itemMapper, Class<U> resultType) {
		final U[] result = (U[]) Array.newInstance(resultType,
				collection.size());
		final Iterator<Object> iterator = collection.iterator();
		for (int i = 0; i < result.length; i++) {
			result[i] = itemMapper.mapResponse(iterator.next());
		}
		return result;
	}

	protected String[] getArray(Object response) {
		String[] result = new String[0];
		if (response instanceof Collection) {
			final Collection<Object> collection = (Collection<Object>) response;
			result = new String[collection.size()];
			final Iterator<Object> iterator = collection.iterator();
			for (int i = 0; i < result.length; i++) {
				result[i] = iterator.next().toString();
			}
		} else if (response != null) {
			result = new String[1];
			result[0] = response.toString();
		}
		return result;
	}

	protected char[] getCharArray(Object response) {
		char[] result = new char[0];
		if (response instanceof Collection) {
			final Collection<Object> collection = (Collection<Object>) response;
			result = new char[collection.size()];
			final Iterator<Object> iterator = collection.iterator();
			for (int i = 0; i < result.length; i++) {
				final String string = iterator.next().toString();
				result[i] = Character.valueOf((char) Integer.parseInt(string));
			}
		} else if (response != null) {
			result = new char[1];
			result[0] = Character.valueOf((char) Integer.parseInt(response
					.toString()));
		}
		return result;
	}

	protected List<String> getList(Object response) {
		final List<String> result = new ArrayList<String>();
		if (response instanceof Collection) {
			final Collection<Object> collection = (Collection<Object>) response;
			for (final Object item : collection) {
				result.add(item.toString());
			}
		} else if (response != null) {
			result.add(response.toString());
		}
		return result;
	}

	protected <U> List<U> getList(Object response, Class<U> resultType) {
		final List<U> result = new ArrayList<U>();
		if (response instanceof Collection) {
			final Collection<Object> collection = (Collection<Object>) response;
			for (final Object item : collection) {
				result.add(valueOf(resultType, item.toString()));
			}
		} else if (response != null) {
			result.add(valueOf(resultType, response.toString()));
		}
		return result;
	}

	protected <U> List<U> getList(Object response, ResponseMapper<U> itemMapper) {
		final List<U> result = new ArrayList<U>();
		if (response instanceof Collection) {
			final Collection<Object> responseCollection = (Collection<Object>) response;
			for (final Object soapPrimitive : responseCollection) {
				result.add(itemMapper.mapResponse(soapPrimitive));
			}
		} else if (response != null) {
			result.add(itemMapper.mapResponse(response));
		}
		return result;
	}

	protected <U> List<U> getList(SoapObject response, String key,
			ResponseMapper<U> itemMapper) {
		final List<U> result = new ArrayList<U>();
		final int propCount = response.getPropertyCount();
		for (int i = 0; i < propCount; i++) {
			final PropertyInfo propInfo = new PropertyInfo();
			response.getPropertyInfo(i, propInfo);
			if (propInfo.getName().equals(key)) {
				result.add(itemMapper.mapResponse(propInfo.getValue()));
			}
		}
		return result;
	}

	protected String getProperty(SoapObject soapObject, String key) {
		return soapObject.getProperty(key).toString();
	}

	protected <U> U getProperty(SoapObject soapObject, String key,
			Class<U> resultType) {
		return valueOf(resultType, getProperty(soapObject, key));
	}

	protected <U> U getProperty(SoapObject soapObject, String key,
			ResponseMapper<U> propertyMapper) {
		return propertyMapper.mapResponse(soapObject.getProperty(key));
	}

	protected <U> U valueOf(Class<U> resultType, Object object) {
		U result;
		if (object == null) {
			result = null;
		} else {
			result = valueOf(resultType, object.toString());
		}
		return result;
	}

	private <U> U valueOf(Class<U> resultType, String valueAsString) {
		try {
			return (U) resultType.getMethod("valueOf",
					new Class[] { String.class }).invoke(resultType,
					new Object[] { valueAsString });
		} catch (final Exception e) {
			throw new IllegalStateException(e);
		}
	}

}

Here are some examples of using the template. Look how simple it becomes your client code:

public class SampleClient {

	private static final String URL = "http://localhost:8080/ws/SampleService";
	private static final String NAMESPACE = "http://sample.org.ar/";
	private KSoap2Template template = new KSoap2Template(URL, NAMESPACE);

	public void callWithVoidResponse() {
		RequestPropertiesSetter rps = new RequestPropertiesSetter() {
			public void setValues(SoapObject request) {
				request.addProperty("aCar", new Car(200L, "A car name"));
			}
		};
		template.call("addCar", "http://impl.service.sample.org.ar/addCar", rps);
	}

	public void callWithoutParams() {
		template.call("getCarNames", "http://impl.service.sample.org.ar/getCarNames",
			new AbstractResponseMapper<List<String>>() {
				public List<String> mapResponse(Object response) {
					return getList(response);
				}
			});
	}

	public Car callWithParamsAndObjectResult() {
		RequestPropertiesSetter rps = new RequestPropertiesSetter() {
			public void setValues(SoapObject request) {
				request.addProperty("carId", 200L);
			}
		};
		return template.call("getCar", "http://impl.service.sample.org.ar/getCar", rps,
			new AbstractResponseMapper<Car>() {
				public Car mapResponse(Object response) {
					Car result = new Car();
					result.setId(getProperty((SoapObject) response, "id", Long.class));
					result.setName(getProperty((SoapObject) response, "name"));
					return result;
				}
			});
	}
}

Obviously this code may be incomplete and may be improved, so comments are welcome.


Enhanced Links