Convert SOAP to a virtual Restful service? - java

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 Application. 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;
}

Related

The exclude-mapping of interceptor is not working in spring mvc

I want to make an interceptor that will intercept every request except that related to login. The problem that I have is the interceptor still intercepts the requests that I provided with exclude-mapping. But the exclude-mapping is not working.
Here is the configuration,spring 4.3:
<mvc:interceptors>
<beans:bean class="com.knowledge.filter.GlobalInterceptor" />
<mvc:interceptor>
<mvc:mapping path="/back" />
<mvc:exclude-mapping path="/back/login" />
<beans:bean class="com.knowledge.filter.LoginInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
In my opinion, the "/back/login" should not be intercepted. Actually still get into the class of the interceptor.So, do i make some mistakes?
I recommend writing separate mapping for every individual path instead of grouping it until and unless you have common implementation for all the services. Excluding will be possible if login and back are separate rest controllers, Not one being child of other.
You can refer this example for spring 4.
http://www.kscodes.com/spring-mvc/spring-mvc-interceptor-example/

#Cacheable not working

I am using #Cacheable for caching the result of a method at Service layer in Spring 3.2. following method code is used inside service Class:
#Cacheable("questions")
public List<ABClassObject> getSecutityQuestionsList(){
List<ABClassObject> list = new ArrayList<ABClassObject>();
----------------
list = ----[DAO call]
return list;
}
xml Configuration
<cache:annotation-driven />
<!-- Generic cache manager based on the JDK ConcurrentMap -->
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="questions" />
</set>
</property>
</bean>
Can't use EhCache because of using jdk 1.6.
By using the above code pattern i am unable to cache the List result.DAO is called all the time when i call the above method.
So, Suggest me whats wrong with the code.
Thanks in advance.
Some things you should check:
The class of getSecutityQuestionsList method is a spring bean, i.e, you don´t use a new operator anyway.
The method getSecutityQuestionsList is called from another bean
In your xml configuration put a context:component-scan base-package="xxxxx"
Put a break point inside your method. In the stack trace you should see some spring proxy stuff. When you call this method of your service, you should actually be calling a spring proxy.

using form-login in a Multi-tenant webapp using Spring MVC and Spring Security

I created a small and simple webapp using Spring Security and SpringMVC and I'm trying to convert it to be a multi-tenant application.
The concept I want is to re-use actual JSPs I have and alter their contents based on configuration which I determine based on the path of the URL.
Example:
Customer #1 (abc) - URL: http://mydomain.com/abc/login.html
Customer #2 (xyz) - URL: http://mydomain.com/xyz/login.html
So the name of the "tenant" is a prefix to the page's path.
I modified my controller to be like this:
#Controller
#RequestMapping("/{customer:[a-zA-Z0-9]+}/login.htm")
public class LoginController
{
private static final Logger logger = Logger.getLogger(LoginController.class);
#RequestMapping
#ReadOnlyRequest
public String login(#PathVariable("customer") String customer, HttpServletRequest request)
{
// Do some 'customer' related actions here
return "login"; // Map to the 'login.jsp' view
}
}
My view resolver configuration is:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
Until now, I had the following form-login configuration:
<form-login
login-page="/login.htm"
authentication-failure-url="/login.htm?error=true"
login-processing-url="/login_process"
default-target-url="/index.jsp"
always-use-default-target="true"
/>
But I do not know how to convert it to support my changes.
Is there a way to convert it to something like:
<form-login
login-page="/${customer}/login.htm"
authentication-failure-url="/${customer}/login.htm?error=true"
login-processing-url="/${customer}/login_process"
default-target-url="/index.jsp"
always-use-default-target="true"
/>
One possible idea is to use URL rewriting instead of manual handling of tenant identifiers. This way you can completely decouple tenant handling logic from your code, for example, as follows:
You define an inbound rewriting rule that converts /abc/login.html to /login.html and saves tenant identifier as a request attribute.
You define an outbound rule that appends the current tenant identifier to URLs being written into response. I think Spring Security should respect such a rule when sending redirects (if it doesn't, you can define a custom RedirectStrategy).
Though I have not tested this idea and cannot be sure that it would work.
See also:
OCPSoft Rewrite
UrlRewriteFilter

Why isn't my spring component accessible to the WebApplicationContext?

I have a spring component which was used as a flex-blazeds endpoint (using #RemotingDestination) and I now need to reuse it as a REST endpoint.
What I did was create an additional rest servlet (of type DispatcherServlet of courser) in addition to the existing blaze-ds servlet I had.
I then wanted to access the same components using REST (hence my previous question) and I found that I'm getting a 404.
My rest-servlet.xml configuration file looked something like:
<mvc:annotation-driven />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonConverter" />
</list>
</property>
</bean>
<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json" />
</bean>
And my ContextLoaderListener uses all my spring context files, which included the compontent-scan of those components.
Since the flex-servlet had no problem accessing these #Component beans which were scanned by the global context I assumed that the rest-servlet will also had access to them and I just need to add the annotations to the components.
The strange part was that when I explicitely added the component-scan for the package where these components reside then the rest call worked.
This meant that these component beans were being created twice, once for the global context (as it scans a config file containing this scan for the flex servlet) and one for the rest-servlet context (I verified this with a simple static counter and a lock on the class).
My question is why can't the rest-servlet see the beans the flex-servlet can?
While it's true that the servlet appcontext can access the beans from the ContextLoaderListener appcontext, those beans will not be consulted when mapping HTTP calls to controllers. All controller beans must be declared (or scanned) directly in the servlet's appcontext, else they will be ignored.
I suggest that you separate the REST entry point annotations (i.e. #RequestMapping) from your BlazeDS ones. For example, take your UserService class from your other post: create a UserController class, put the REST annotations on that, and delegate from UserController to UserService. UserController would be declared in the servlet app context, and injected with the UserService from the ContextLoaderListener context.
You need to use Spring Web context and define a DispatcherServlet, which will be a child context for the one loaded by ContextLoaderListener.
It is DistpatcherServlet that should load your rest-servlet.xml, not ContextLoaderListener. Otherwise, the guys whom you call "servlets" and who, in fact, I assume, are controllers, just won't get the requests from your client.
You can read about all this stuff here: http://static.springsource.org/spring/docs/3.1.0.RC1/spring-framework-reference/html/mvc.html
This is a standard way of doing Web-related things in Spring, and you definetely need to follow it.

Inrecept requests to specific beans

I'm developing webapp using spring 3 mvc and using annotation-based controllers. I want to define an interceptor, that will intercept requests to specific beans not for all. How I can do that?
Now i use following config, but it intercept requests to all beans
<bean id="annotationMapper" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<ref bean="sessionInterceptor"/>
</list>
</property>
</bean>
<bean id="sessionInterceptor" class="app.interceptors.SessionCheckInterceptor"/>
Thanks!
In Spring 3 you can bind interceptors to specific URLs using mvc namespace:
<mvc:interceptors>
<mvc:interceptor>
<bean class="app.interceptors.SessionCheckInterceptor" />
<mvc:mapping path = "/somePath/**" />
</mvc:interceptor>
</mvc:interceptors>
For more complex cases you may also use AOP to intercept controller method calls.
I think the best choice for intercepting calls to your bean is using Spring AOP.
#Aspect
public class AroundExample {
#Around("com.xyz.myapp.SystemArchitecture.businessService()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
Object retVal = pjp.proceed();
// stop stopwatch
return retVal;
}
}
I have found a solution using springplugins project (http://code.google.com/p/springplugins/)
It define two own mappers:
SelectedAnnotationHandlerMapping handle only defined url's
IgnoreSelectedAnnotationHandlerMapping handle all urs's except defined
If you know a better way to solve this problem, you a welcome

Categories