Spring Boot + JAX-WS without an extra servlet? - java

I'm working on migrating some applications from Spring to Spring Boot. Some of the applications provide SOAP web services on the root url of the application (/), which is fine with a Spring application. But on Spring Boot, this causes some issues, since setting up a servlet to listen on / will overwrite the default dispatcher-servlet, causing for instance the actuator endpoint to be overwritten. Still, I do not want to change the url to my wsdl, since this will lead to updating all the clients of the service.
Is there a way to have Spring Boot supply a JAX-WS/CXF Web Service on /, without overwriting the default servlet? Is there a way to have the default servlet supply the web service, without the need of an extra CXFServlet?

Managed to access the wsdl at least, by exposing the CXFServlet as a filter, rather than a servlet:
#Bean
public Endpoint endpoint(final SpringBus springBus, final MyServiceImpl myService) {
final EndpointImpl endpoint = new EndpointImpl(springBus, myService);
endpoint.publish("/MyService");
return endpoint;
}
#Bean
public SpringBus springBus() {
return new SpringBus();
}
#Bean
public FilterRegistrationBean<CXFServlet> cxfServletFilter(final SpringBus springBus) {
final CXFServlet cxfServlet = new CXFServlet();
cxfServlet.setBus(springBus);
final FilterRegistrationBean<CXFServlet> filterRegistrationBean = new FilterRegistrationBean<>(cxfServlet);
filterRegistrationBean.setOrder(2);
return filterRegistrationBean;
}
I also removed cxf-spring-boot-starter-jaxws from my pom.xml.
Any feedback on this solution is appreciated.

Related

Set different context paths with configuration files

Set Context path differently for both Webservice and Rest
I have an application which contains both implementation of Webservices and Rest services and I am looking for an solution to set context path for both Webservices and Rest services differently using yml/properties file
How to configure servlet dispatcher to work properly?
I would like to have:
localhost:8080/ws/* - webservice
localhost:8080/web/* - MVC components
servlet:
context-path: "/ws"
It sets globally for both webservices and rest services , How to make it independent to each other with out programming?
Using Spring Boot (with Spring Starter Web) you could achive what are you asking for with the annotation #RequestMapping.
You could put #RequestMapping(value="/ws") on the class declaration of every rest controller and #RequestMapping(value="/web") for web controllers.
For both rest and web controller than you could use other annotations to specify method path, i.e #GetMapping(value="/methodPath").
#Controller
#RequestMapping(value="/web")
public class WebController{
#GetMapping(value="/method")
public String method(){
...
}
}
#RestController
#RequestMapping(value="/ws")
public class RestController{
#GetMapping(value="method")
public String method(){
...
}
}

SpringBoot: Control Async behaviour from #RequestMapping analogous to an AsyncWebServlet?

I am working with Spring Boot 2 and I would like my requests to be handled asynchronously.
In Java EE, one can use Asynchronous Processing for Asynchronous Servlets as in this link. The following is a related example:
#WebServlet(urlPatterns={"/asyncservlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet { ... }
and the above allows to use AsyncContext.
But in Spring Boot, I have the following #RequestMapping. How do I make it handle requests in Asynchronous mode and also supporting AsyncContext? How do I leverage the use of an Asynchronous Web Servlet?
#RestController
public class myRestController {
#RequestMapping("{resource}/**")
public void resourceRequest (#PathVariable("resource") String resource) {
// example:
// ... some long running calls such as database communication
// ... convert request through AsyncContext ctx = req.startAsync();
// etc
}
}
Note that returning void is intentional.
I found the following SO answer How to register a servlet with enabled "async-supported" in Spring-Boot? saying that "Spring Boot will automatically register any Servlet beans in your application context with the servlet container. By default async supported is set to true so there's nothing for you to do beyond creating a bean for your Servlet." but I am not using any #WebServlet annotations anywhere in my program, just the #RestController annotation. So how do I ensure that I am benefitting from asyncSupported option?

Use existing http server in spring boot as camel endpoint

I have a spring boot application that uses the spring boot starter web. This creates a running Tomcat instance and sets up the http server running on a port. Within my camel route, I want to use this http server as the component for http requests, but I can't figure out how to utilize it. I see many many examples of configuring a jetty instance and consuming from it, but then wouldn't I in effect have two http servers running? I only want to have one. I assume the http server is already autowired up since I can consume from it with other spring code (such as a RestController) and I can see it started in my spring boot logs as well.
#Component
public class ExampleRoute extends RouteBuilder
{
#Override
public void configure() throws Exception
{
//#formatter:off
from( <want to take in an http request here> )
.log( LoggingLevel.INFO, log, "Hello World!" );
//#formatter:on
}
}
There is an example here: https://github.com/camelinaction/camelinaction2/tree/master/chapter7/springboot-camel
You can to register a ServletRegistrationBean that setup the Camel Servlet with Spring Boot.
#Bean
ServletRegistrationBean camelServlet() {
// use a #Bean to register the Camel servlet which we need to do
// because we want to use the camel-servlet component for the Camel REST service
ServletRegistrationBean mapping = new ServletRegistrationBean();
mapping.setName("CamelServlet");
mapping.setLoadOnStartup(1);
// CamelHttpTransportServlet is the name of the Camel servlet to use
mapping.setServlet(new CamelHttpTransportServlet());
mapping.addUrlMappings("/camel/*");
return mapping;
}
However for Camel 2.19 we plan on make this simpler and OOTB: https://issues.apache.org/jira/browse/CAMEL-10416
And then you can do
from("servlet:foo")
.to("bean:foo");
Where the HTTP url to call that Camel route will be http:localhost:8080/camel/foo

Spring 4 Security configuration runs but not active

I am quite new to Spring, so sorry if my question sounds silly.
I am trying to add basic HTTP Auth to my Spring based REST API.
I have been following quite a few tutorials and reference documentations for now, but they all seem to indicate the same thing so it seems obvious I must be missing something.
What I want is to define a simple configuration where every request is behind HTTP Auth by default, but then we can define method level security as needed using #PreAuthorize, or by modifying the HttpSecurity configuration.
I have defined a very simple configuration as such:
#Configuration
#EnableWebMvc
#EnableWebSecurity
#ComponentScan(basePackages = "com.app.rest")
public class RESTConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().hasAnyRole("USER")
.and()
.httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
}
I then have a very simple controller (no service, nothing fancy).
Here is a very basic overview:
#RestController
#RequestMapping("/images")
public class ImagesController {
#JsonView(View.Basic.class)
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ObjectXvo getImageByID(#PathVariable String id) throws IOException {
ObjectXvo result = doThings();
return result;
}
}
The only noticeable difference compared to all the online tutorials is the way we load this API. Because we already have a Jetty container running other things, we have decided to reuse it instead of using a WebAppInitializer like most do online.
Here is an extract of how the REST API is defined:
// Creating REST Servlet
ServletContextHandler restHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
restHandler.setErrorHandler(null);
restHandler.setContextPath("/rest");
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setConfigLocation("com.app.test");
WebApplicationContext webAppContext = context;
DispatcherServlet dispatcherServlet = new DispatcherServlet(webAppContext);
ServletHolder springServletHolder = new ServletHolder("REST dispatcher", dispatcherServlet);
restHandler.addServlet(springServletHolder, "/");
restHandler.addEventListener(new ContextLoaderListener(webAppContext));
// Creating handler collection
ContextHandlerCollection handlerCollection = new ContextHandlerCollection();
handlerCollection.addHandler(anotherHandler);
handlerCollection.addHandler(restHandler);
handlerCollection.addHandler(yetAnotherHandler);
// Setting server context
server.setHandler(handlerCollection);
The thing is, when running the app, is that I can still access all URLs like if I had not setup any security scheme.
When debugging the application, I can see that the RESTConfiguration is being processed as breakpoints inside my configure methods are definitely being hit.
We are quite set on avoiding the usage of XML files as long as we can, as we think annotations are better so far.
Can anyone point me in the right direction as to why this security configuration is not being activated?
EDIT:
Not sure how relevant that is, but if I try to add a #PreAuthorize to a REST method, I get the following error:
HTTP ERROR: 500
Problem accessing /rest/images/I147. Reason:
An Authentication object was not found in the SecurityContext
There is plenty of info on the internet on how to fix this, but I am wondering if this is not related.
You have to register springSecurityFilterChain, see Spring Security Reference:
The next step is to register the springSecurityFilterChain with the war. This can be done in Java Configuration with Spring’s WebApplicationInitializer support in a Servlet 3.0+ environment. Not suprisingly, Spring Security provides a base class AbstractSecurityWebApplicationInitializer that will ensure the springSecurityFilterChain gets registered for you. The way in which we use AbstractSecurityWebApplicationInitializer differs depending on if we are already using Spring or if Spring Security is the only Spring component in our application.
If you don't use AbstractSecurityWebApplicationInitializer you have to register springSecurityFilterChain manually:
webAppContext.getServletContext()
.addFilter("springSecurityFilterChain", new DelegatingFilterProxy("springSecurityFilterChain"))
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC), false, "/*");
See also:
ServletContext#addFilter
FilterRegistration#addMappingForUrlPatterns

How to use CXF soap servlet aside spring REST servlet?

I'm providing a soap webservice with java-first approach, thus using CXF for this. To make it publishing with spring-boot, I have the following dispatcher servlet:
#Bean
public ServletRegistrationBean dispatcherServletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(new CXFServlet(), "/services/*");
registration.setName(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
return registration;
}
This works fine, but I now want to offer a REST service aside. The rest service should NOT be published by cxf, but by the default spring mapping:
#RestConstroller
#RequestMapping("/rest/content")
public class MyServiceRest extends SpringBeanAutowiringSupport {
}
The result of this:
localhost:8080/app-name/rest/content results in HTTP 404.
localhost:8080/app-name/services/rest/content shows a spring message "No service was found."
So, somehow the latter is inside the context of the CXFServlet, and the REST service is not found.
What do I have to change to make this setup work?
By the way: when I remove the ServletRegistrationBean, the rest service works as expected. But that's not an option as I have to offer the soap service alongside.
Your bean named dispatcherServletRegistration is replacing Spring Boot's default DispatcherServlet so your left with just a CXFServlet and no DispatcherServlet in your application.
Update your bean to register the CXFServlet to something like this:
#Bean
public ServletRegistrationBean cxfServletRegistration() {
return new ServletRegistrationBean(new CXFServlet(), "/services/*");
}

Categories