I recently faced a situation where I had to communicate with a legacy server (which only knows TCP – Sockets as communication channel). The client side application was entirely Spring (v4.0) based and I had to lookout for a standard way from the Spring arena to make this communication happen.
Read many articles over the net regarding Spring Integration module and finally came up with the following spring context, configuration and java code to establish the communication –
geocodeViaTCPCommunicationContext.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:int="http://www.springframework.org/schema/integration" xmlns:int-ip="http://www.springframework.org/schema/integration/ip" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd http://www.springframework.org/schema/integration/ip http://www.springframework.org/schema/integration/ip/spring-integration-ip.xsd"> <context:property-placeholder /> <!-- Client side --> <int:gateway id="tcpGateway" service-interface="com.geocode.Gateway" default-request-channel="input"/> <int-ip:tcp-connection-factory id="client" type="client" host="${tcp.server.host}" port="${tcp.server.port}" single-use="true" so-timeout="100000" serializer="connectionSerializeDeserialize" deserializer="connectionSerializeDeserialize"/> <int:channel id="input" /> <int-ip:tcp-outbound-gateway id="outGateway" request-channel="input" reply-channel="clientBytes2StringChannel" connection-factory="client" request-timeout="100000" reply-timeout="100000"/> <int:object-to-string-transformer id="clientBytes2String" input-channel="clientBytes2StringChannel" /> <bean id="connectionSerializeDeserialize" class="org.springframework.integration.ip.tcp.serializer.ByteArrayLengthHeaderSerializer"/> </beans>
GeocodeServiceImpl.java –
import java.io.StringReader; import java.io.StringWriter; import java.util.List; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.transform.Source; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.input.SAXBuilder; import org.jdom2.transform.JDOMSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import geocode.Gateway; import geocode.GeocodeRequest; import geocode.GeocodeResponse; import service.GeocodeService; @Service("geocodeService") public class GeocodeServiceImpl implements GeocodeService { @Autowired Gateway tcpGateway; @Override public GeocodeResponse geocode(GeocodeRequest request) { try { JAXBContext jaxbContext = JAXBContext.newInstance(GeocodeRequest.class); Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); // output pretty printed jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); jaxbMarshaller.marshal(request, System.out); StringWriter w = new StringWriter(); jaxbMarshaller.marshal(request, w); String responseString = tcpGateway.send(w.toString()); //send the marshalled request to legacy server and get response SAXBuilder builder = new SAXBuilder(); Document responseDocument = builder.build(new StringReader(responseString)); //convert response into JDOM document Element responseEle = responseDocument.getRootElement().getChild("RESPONSE"); if ("FAILED".equals(responseEle.getChildText("STATUS")) || responseEle.getChild("NOMATCHFOUND") != null || responseDocument.getRootElement().getChild("RESPONSE").getChild("STATUS").getText().equalsIgnoreCase("FAILED")) { GeocodeResponse geocodeResponse = new GeocodeResponse(); geocodeResponse.setStatus(GeocodeResponse.UNMATCHED); return geocodeResponse; } else { List candidates = responseDocument.getRootElement().getChild("RESPONSE").getChildren("CANDIDATE"); Element candidate1 = (Element) candidates.get(0); Source src = new JDOMSource(candidate1); JAXBContext jaxbResponseContext = JAXBContext.newInstance(GeocodeResponse.class); //Un-marshall the response into java object Unmarshaller u = jaxbResponseContext.createUnmarshaller(); JAXBElement element = u.unmarshal(src, GeocodeResponse.class); GeocodeResponse geocodeResponse = (GeocodeResponse) element.getValue(); geocodeResponse.setStatus(GeocodeResponse.MATCHED); return geocodeResponse; } } catch (Exception e) { e.printStackTrace(); } return null; } }
The pom.xml additional dependencies include –
<dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-test</artifactId> <version>${org.springframework-version}</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${org.springframework-version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-ip</artifactId> <version>${org.springframework-version}</version> <scope>compile</scope> </dependency></pre> <pre>
Hi ,
Thanks for this article. I have to the solve the problem you just described.
I have just started using spring ( Java actually and specifically springboot).
I wish you could help me whip-up a spring boot version. Just the bare bones to get me started.
Thanks.