Can I use SOAP Webservices and Spring MVC together
Asked Answered
M

3

4

I have a Spring MVC project. I wrote a code something like

@Controller
@RequestMapping("CallBack")
@WebService(name = "NotificationToCP", targetNamespace = "http://SubscriptionEngine.ibm.com")
public class CallbackController {

    @RequestMapping("")
    @ResponseBody
    @WebMethod(action = "notificationToCP")
    @RequestWrapper(localName = "notificationToCP", targetNamespace = "http://SubscriptionEngine.ibm.com", className = "in.co.mobiz.airtelVAS.model.NotificationToCP_Type")
    @ResponseWrapper(localName = "notificationToCPResponse", targetNamespace = "http://SubscriptionEngine.ibm.com", className = "in.co.mobiz.airtelVAS.model.NotificationToCPResponse")
    public NotificationToCPResponse index(
            @WebParam(name = "notificationRespDTO", targetNamespace = "") CPNotificationRespDTO notificationRespDTO) {
        return new NotificationToCPResponse();
    }
}

Can I use Spring MVC + Webservices together? What I want is just as a controller that accepts a SOAP request and process it. The url needs to be /CallBack. I'm still as a sort of confused being a Newbie. Will something like above work. Else how do I get it going.

Middlebrooks answered 17/6, 2013 at 2:8 Comment(2)
This will probably work. ("Probably" because I have never tried, but since those are just annotations, they can't have side-effects: whoever doesn't recognize them will just ignore them.) Just be aware that the instances that will handle the SOAP requests are not the same instances that will handle Spring MVC requests.Lannylanolin
Do you've any sample program? Can you share ?Scrobiculate
B
5

I wouldn't mix Spring MVC and SOAP webservice (JAX-WS) together since they serve different purpose.

Better practice is to encapsulate your business operation in a service class, and expose it using both MVC controller and JAX-WS. For example:

HelloService

@Service
public class HelloService {
    public String sayHello() {
        return "hello world";
    }
}

HelloController has HelloService reference injected via autowiring. This is standard Spring MVC controller that invoke the service and pass the result as a model to a view (eg: hello.jsp view)

@Controller
@RequestMapping("/hello")
public class HelloController {
    @Autowired private HelloService helloService;

    @RequestMapping(method = RequestMethod.GET)
    public String get(Model model) {
        model.addAttribute("message", helloService.sayHello());
        return "hello";
    }
}

A JAX-WS endpoint also invoke the same service. The difference is the service is exposed as a SOAP web service

@WebService(serviceName="HelloService")
public class HelloServiceEndpoint {
    @Autowired private HelloService helloService;

    @WebMethod
    public String sayHello() {
        return helloService.sayHello();
    }
}

Note that JAX-WS style web service above isn't guaranteed to automatically work on all Spring deployment, especially if deployed on non Java EE environment (tomcat). Additional setup might be required.

Balaam answered 17/6, 2013 at 3:59 Comment(0)
S
0

Yes, there are reasons why you may want to add a web service endpoint to an existing Spring MVC app. The problem is that you will likely need to have a different path for each, which is fine.

You will need two servlets, a standard dispatcher servlet for handling HTTP/MVC and a MessageDispatcherServlet for handling SOAP calls.

The config can be tricky. First understand that you will have a dependency mismatch with Spring MVC when you add in the Spring-ws dependencies. You will need to exclude Spring-web as follows in your pom:

<dependency>
    <groupId>org.springframework.ws</groupId>
    <artifactId>spring-ws-core</artifactId>
    <version>2.2.1.RELEASE</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </exclusion>
    </exclusions>
</dependency>

Once you have taken care of that you will need to add in the two servlets, one to handle web requests through Spring MVC and one to handle SOAP.

I'm assuming no-xml config using Spring 4, SpringBoot is possible as well.

Here is the key code you will add to your web initializer:

DispatcherServlet servlet = new DispatcherServlet();

// no explicit configuration reference here: everything is configured in the root container for simplicity
servlet.setContextConfigLocation("");

/* TMT From Java EE 6 API Docs:
 * Registers the given servlet instance with this ServletContext under the given servletName.
 * The registered servlet may be further configured via the returned ServletRegistration object. 
 */

ServletRegistration.Dynamic appServlet = servletContext.addServlet("appServlet", servlet);
appServlet.setLoadOnStartup(1);
appServlet.setAsyncSupported(true);

Set<String> mappingConflicts = appServlet.addMapping("/web/*");

MessageDispatcherServlet mds = new MessageDispatcherServlet();
mds.setTransformWsdlLocations(true);
mds.setApplicationContext(context);
mds.setTransformWsdlLocations(true);

ServletRegistration.Dynamic mdsServlet = servletContext.addServlet("mdsServlet", mds);
mdsServlet.addMapping("/wsep/*");
mdsServlet.setLoadOnStartup(2);
mdsServlet.setAsyncSupported(true);

That is really all there is to it. The rest of the config is standard stuff, found in any number of examples.

For instance, you can mix the spring IO examples for Spring MVC and Spring-WS easily as a test bed. Just make sure you set up the WebMvcConfigurerAdapter and the WsConfigurerAdapter accordingly. They will be two separate classes, annotated individually with @Configuration @EnableWebMvc and @EnableWs @Configuration respectively. They will have to be added to the component scan complete with your @Endpoint classes.

Compile, run and test using a browser for the MVC stuff off the root context via /web/* and the SOAP calls using SoapUI by importing the WSDL and hitting /wsep/* off the root. Each path handled by each servlet.

Semiweekly answered 5/8, 2015 at 4:15 Comment(1)
How do you define context in your example ? What is the best way to define separate beans for each servlet ?Whitechapel
W
0

After seeing all the suggestions in stackoverflow, it made sense to me that I need to create two dispatcher servlets, one for SOAP endpoints and other for REST endpoints, so that SOAP and REST endpoints can be built into the same project. But the key point here is that dispatcher servlet should point to its own URL mapping. We can configure each servlet to have its own URL mapping. I am using Spring Boot and below seems to have worked in my case. I am hoping it might help others.

STEP1: Either you can disable Spring Boot's auto configured dispatcher servlet or we can use the default auto configured dispatcher to have its original behavior. After multiple combinations of testing I realized that it is easier to leave auto configured dispatcher to its original behavior.

But if you are interested in tweaking that dispatcher servlet, you can do below:

  1. @SpringBootApplication(exclude = DispatcherServletAutoConfiguration.class) -> In your main class.

  2. In a new class,

    @Configuration public class RestWebConfig {

     @Bean
     public DispatcherServlet dispatcherServlet() {
         DispatcherServlet dispatcherServlet = new DispatcherServlet();
         //dispatcherServlet.setThreadContextInheritable(true);
         //dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
         return dispatcherServlet;
     }       
     @Bean
     public ServletRegistrationBean dispatcherServletRegistration() {
         ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet());            
         registration.setName(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
         return registration;
     }
     @Bean
     public DispatcherServletPath dispatcherServletPath() {
         return () -> "/rest";
     }
    

    }

STEP2: I tried to customize DispatcherServlet, in such a way that my second dispatcher servlet for SOAP would point to "/", but I could not get it work. So I have defaulted to orignal behaviour and all of my REST requests would route to Spring Boot's autoconfigured DispatcherServlet. Then I created below Message DispatcherServlet, which is actually built on top of DispatcherServlet, with a different URL mapping - "/soap/".

@EnableWs
@Configuration
public class WebConfig extends WsConfigurerAdapter{ 
    
    @Bean
    public ServletRegistrationBean<MessageDispatcherServlet> messageDispatcherServlet(ApplicationContext applicationContext) {
        MessageDispatcherServlet servlet = new MessageDispatcherServlet();
        servlet.setApplicationContext(applicationContext);
        servlet.setTransformWsdlLocations(true);
        return new ServletRegistrationBean<>(servlet, "/soap/*");
    }
    
    
    @Bean(name = "app")
    public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchemaCollection appserviceSchema) {
        DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
        wsdl11Definition.setPortTypeName("AppservicePort");
        wsdl11Definition.setLocationUri("/soap/appservice");
        wsdl11Definition.setTargetNamespace("http://xmlns.ft.com/appservice");  
        wsdl11Definition.setSchemaCollection(appserviceSchema);
        return wsdl11Definition;
    }
    
    @Bean
    public XsdSchemaCollection appserviceSchema() {
        return new CommonsXsdSchemaCollection(new ClassPathResource("xsds/CommonServices.xsd"));
    }
}

With above configuration,

  1. All of my SOAP requests would route via custom messageDispatcherServlet and then to SOAP endpoints. Ofcourse, all my SOAP endpoints must beging with /soap/*
  2. All of the REST requests would route via default dispatcherServlet and then to my REST endpoints. REST endpoints can start with anything except soap
Ween answered 19/9 at 20:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.