Convert SOAP to a virtual Restful service?
Is there a way to use my SOAP web service(spring-ws, java) as a XML based RESTful service virtually?
I don't want to re-write whole my SOAP web service into RESTful from scratch in java, but I need to access it through iphone using REST, which they already have easy native support.
XMLGateway, Proxys..? or some extra java code? since my SOAP request and response is simply an XML file why can't I modify it to be used by a REST service?
Or without changing changing any logic and xml parsing in my application is that easy to add jax-rs annotations and create a rest request xml?
my config spring file is like this:
<bean id="webServicePluginDescriptor"
class="com.mysite.ws.configuration.MyWebservicePluginDescriptor" />
<bean id="payloadMapping"
class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
<property name="defaultEndpoint" ref="inferenceEndPoint" />
<property name="interceptors">
<list>
<ref local="validatingInterceptor" />
<ref local="payLoadInterceptor" />
</list>
</property>
</bean>
<bean id="payLoadInterceptor"
class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor" />
<bean id="validatingInterceptor"
class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
<property name="schema"
value="classpath:/wsdl/Request.xsd" />
<property name="validateRequest" value="true" />
<property name="validateResponse" value="false" />
</bean>
<bean id="PropertyResource" class="com.mysite.ws.im.PropertyResource">
<property name="resource"
value="/WEB-INF/client-specific/InferenceMachine.properties" />
</bean>
<bean id="inferenceEndPoint" class="com.mysite.ws.web.InferenceEndPoint">
<property name="messageWebService" ref="messageWebService" />
</bean>
<bean id="messageWebService" class="com.mysite.ws.service.MessageWebService"
scope="request">
<aop:scoped-proxy />
<property name="inferenceService" ref="inferenceService" />
</bean>
<bean id="Request" class="org.springframework.xml.xsd.SimpleXsdSchema">
<property name="xsd" value="classpath:/wsdl/Request.xsd" />
</bean>
<bean id="Response" class="org.springframework.xml.xsd.SimpleXsdSchema">
<property name="xsd" value="classpath:/wsdl/Response.xsd" />
</bean>
<bean id="Error" class="org.springframework.xml.xsd.SimpleXsdSchema">
<property name="xsd" value="classpath:/wsdl/Error.xsd" />
</bean>
<bean id="mwsid"
class="org.springframework.ws.wsdl.wsdl11.SimpleWsdl11Definition">
<constructor-arg value="classpath:/wsdl/mtchwsdl.wsdl" />
</bean>
<bean id="inferenceService" class="com.mysite.ws.im.InferenceService"
scope="request">
<aop:scoped-proxy />
<property name="webServiceConfiguration" ref="wsPlayerConfiguration" />
<property name="properties">
<bean class="com.mysite.ws.im.PropertyResource">
<property name="resource"
value="/WEB-INF/client-specific/InferenceMachine.properties" />
</bean>
</property>
</bean>
<!-- ~~~~~~~ Application beans ~~~~~~~ -->
<bean id="wsPlayerConfiguration"
class="com.mysite.ws.configuration.WebServiceConfiguration"
scope="request">
<aop:scoped-proxy />
<property name="playerConfiguration" ref="playerConfiguration"></property>
<property name="configurationSetup" ref="configurationSetup"></property>
</bean>
and this is my endpoint class:
/**
* The EndPoint of the Web Service Appli开发者_JAVA技巧cation. This class gets the raw
* SOAP-body message from the Spring Payload Dispatcher and sends the message to
* the @see MessageService class. After it has gotten the response XML message
* it returns this back to the Spring Payload Dispatcher.
*/
public class InferenceEndPoint extends AbstractJDomPayloadEndpoint {
private MessageWebService messageWebService;
public InferenceEndPoint() {
}
@Override
protected Element invokeInternal(Element inferenceRequest) throws Exception {
Element ret = messageWebService.handleRequest(inferenceRequest);
return ret;
}
/**
* @param messageWebService
*/
public void setMessageWebService(MessageWebService messageWebService) {
this.messageWebService = messageWebService;
}
}
any ideas?
Spring-WS just adds some annotations to your beans and then you have Spring beans doing most of the heavy lifting. Presumably you have some classes annotated with @Endpoint, @PayloadRoot and the like. You should be able to re-use all of these in one of three ways.
If your Spring-WS endpoint classes are adapter pattern style (e.g. your Endpoint class is injected with a POJO service that does the real work), then you could do a similar adapter-style Spring MVC Controller (where REST exists in Spring 3.0).
If you have your annotations directly on your business logic class, then in theory, you should be able to just sprinkle on some more annotations (might get a little busy-looking).
If you have Spring-WS beans that are POX (and not SOAP), then you might be able to get away with some fancy URL mappings to give them more RESTful looking URLs
To migrate to the Spring 3 for REST support, add the appropriate @RequestMapping and another annotations to expose them as REST services to match specific URLs. While you're adding, you might as well remove the old @PayloadRoots and @Endpoint, but that may not be a big deal. Of course if you leave old Spring-WS annotations around, you'll still need the Spring-WS jars on your classpath, but as long as you are not using a Spring-WS servlet nor any other other beans in your Spring file - you should be okay (in theory...).
The biggest gotchas will be:
- Don't forget to remove Spring-WS beans from your Spring file
- DO remember to add the Spring MVC beans to your Spring file, and most importantly different Dispatcher servlet
- REST security in Spring will be provided by Spring Security, not the same SOAP interceptors found in Spring-WS, so that will be a complete overhaul. The good news is that Spring Security is actually pretty easy to work with
Looking at your code, it's very difficult to tell what the best approach would be. REST and SOAP are really quite different ways of imagining how a web-based service interface could work: SOAP is all about method calls, and REST is all about resources, representations and links. To convert, you have to start from your underlying abstract API.
If your basic API is one of “I give you a document, you give me a response document” and there's no exposure of anything other than that, it's a very SOAP-oriented model. You can model that in REST through POSTing a document and getting a response, but it's not at all elegant. If you can instead think of your interface in terms of “here's an overall resource, with properties I can set, and certain operations I can do” then that maps to REST far more easily (the overall resource is represented as a document full of links to individual property resources and operations, and the individual properties can be GET and PUT – and possibly DELETEd – as necessary). Which style you've got… well, it looks a lot like you've got the first, but I'm only guessing because determining it for real would require looking at more of your code than you've shown.
There's nothing stopping you from send POST filled with XML to get results filled with XML back.
The simplest thing to do is somehow capture the SOAP requests going back and forth, and simply turn the request in to a template with the blanks being your parameters, and then using XPath on the resulting XML to pull your results out.
The only nit being you might need SOAPAction header in your POST, but likely not.
It's really no big deal. If you have dozens of methods, it's more of a pain. Also, if you're using any of the encryption parts of SOAP, then it's more of a pain. But if just a few, in the end it's just XML, and a bulk of that XML is boilerplate, so looked at that way, it's pretty straightforward.
Addenda:
If you have back end logic that you want to front with a more HTTP friendly service, then JAX-RS can to that quite easily, but it would require coding on the server end.
If you have an existing SOAP web service that you wish to use, then forget the whole SOAP part of the equation, and simply treat it as an HTTP web service that uses XML payloads. It's still SOAP, but you're not using any SOAP tooling on the client side. You're simply assembling XML payloads on the client requests (from templates would be easiest, IMHO), and consuming XML payloads as a result, and trading these over HTTP.
Depending on how many different methods on the existing web service you intend to call gives you an idea of the scope of the work involved. It's simple work (once you can view the payloads easily), but it's still work. If you only have a few methods, especially if the interface is stable and not changing, it's far easier to just work with the raw XML than learn and fight some new unfamiliar framework.
This is how I solved that problem, using Spring Boot + Spring Integration.
- Having a WSDL of the SOAP WS, I used maven-jaxb2-plugin to have my Java POJO's generated at build time.
- Optionally, you could create transformations to adapt those classes and attributes to what the REST client expected.
- Using Spring Integration, I mapped every REST endpoint to a SOAP gateway like this:
@Bean
public IntegrationFlow myFlow(GenericTransformer reqTransformer, GenericTransformer resTransformer) {
return IntegrationFlows
.from(this.getRestGateway(POST, "/api/entity", MyRestResponse.class))
.transform(reqTransformer)
.handle(this.getSoapGateway("gwBean"))
.enrichHeaders(h -> h.header(HttpHeaders.STATUS_CODE, HttpStatus.OK))
.transform(resTransformer)
.logAndReply();
}
private HttpRequestHandlingMessagingGateway getRestGateway(HttpMethod method, String path, Class payloadType) {
HttpRequestHandlingMessagingGateway httpGateway = new HttpRequestHandlingMessagingGateway();
RequestMapping requestMapping = new RequestMapping();
requestMapping.setMethods(method);
requestMapping.setPathPatterns(path);
httpGateway.setRequestMapping(requestMapping);
httpGateway.setReplyTimeout(timeout);
httpGateway.setRequestPayloadTypeClass(payloadType);
httpGateway.setMessageConverters(asList(jsonConverter));
return httpGateway;
}
private MarshallingWebServiceOutboundGateway getSoapGateway(String nameBean) {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath(generatedClassesPackage);
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory();
messageFactory.setSoapVersion(SoapVersion.SOAP_12);
messageFactory.afterPropertiesSet();
MarshallingWebServiceOutboundGateway webServiceGateway = new MarshallingWebServiceOutboundGateway(soapUri, marshaller);
webServiceGateway.setMessageFactory(messageFactory);
webServiceGateway.setBeanName(nombreBean);
return webServiceGateway;
}
精彩评论