Use existing http server in spring boot as camel endpoint - java

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

Related

How #Consume from Apache Camel does the mapping in Spring Boot project?

I'm learning about Apache Camel routes in Spring Boot projects and I have a project that does extension from some endpoints. The endpoints are not in this project, only the extension is done here. The extension is done using #Consume from org.apache.camel in this way:
#Consume(uri = "direct:products.create.validate.interceptor")
public void executeCreate(RequestWrapper<Product> productWrapper) {
...
}
I try to understand how this direct:products.create.validate.interceptor is mapped to an endpoint from another service. Can somebody explain me how this #Consume annotation does the mapping?
Or another example is this:
#Consume(uri = "direct:listAccountsPostHook")
public void executeCreate(RequestWrapper<Account> accountWrapper) {
...
}
Where should I look to understand how they are mapped? In the controller of the other service? I can't find any example with #Consume. Thank you!
The #Consume annotation in Apache Camel is used to subscribe to a Camel endpoint and consume messages from it. The endpoint can be either a direct endpoint or any other type of endpoint such as a JMS queue or a REST endpoint, depending on your use case.
The endpoint URI, which is specified in the uri attribute of the #Consume annotation, determines where the messages are consumed from. In your example, direct:products.create.validate.interceptor and direct:listAccountsPostHook are both direct endpoints.
In Apache Camel, direct endpoints are in-memory endpoints that allow you to send messages directly to another endpoint in the same JVM. The mapping between the endpoint and the method that consumes the messages is done by Camel's routing engine.
More on Camel Direct endpoints you can read here.
To understand how the messages are being consumed, you should look at the Camel routes that are defined in your project. In a Spring Boot project, you can define your Camel routes in a RouteBuilder class. This is where you would specify the mapping between the direct endpoint and the method that will consume the messages.
For example, if you have a RouteBuilder class that looks like this:
public class MyRouteBuilder extends RouteBuilder {
#Override
public void configure() {
from("direct:products.create.validate.interceptor")
.to("bean:myBean?method=executeCreate");
}
}
In this example, the direct endpoint direct:products.create.validate.interceptor is mapped to the executeCreate method in the myBean bean. The ?method=executeCreate part of the to URI tells Camel to call the executeCreate method on the myBean bean when a message is received at the endpoint.
So, in short, you should look for the Camel routes in your project that define the mapping between the endpoint and the method that consumes the messages.

Logging a request header before Spring Security filter chain

I want to log the contents of a given incoming request header as early as possible.
I know about approaches like CommonsRequestLoggingFilter or a logging HandlerInterceptor, however these seem to only log after Spring has executed a lot of other code, such as the Spring Security filter chain.
I want to log before Spring has done any of that, as early as possible based on a single requirement: the log message needs to be able to extract a header from the HTTP request.
Is there a way to do this?
I have found a way to do this using the embedded Tomcat. Since this receives the request before Spring does, you can capture the entire dispatched request from here.
public class CustomLoggerValve extends ValveBase {
private static final Logger logger = LoggerFactory.getLogger(CustomLoggerValve.class);
#Override
public void invoke(Request request, Response response) throws IOException, ServletException {
try {
MDC.put("requestId", UUID.randomUUID().toString());
logger.info("Received request");
getNext().invoke(request, response);
} finally {
MDC.remove("requestId");
}
}
}
Since I'm using Spring without Spring Boot, I can just add this to my Tomcat directly:
Tomcat tomcat = // ... your tomcat setup
tomcat.getService().getContainer().getPipeline().addValve(new CustomLoggerValve());
I haven't tried, but it looks like you could add this quite easily in Spring Boot too.
Presumably a similar approach would work with embedded Jetty/other JVM web servers.

Spring Boot + JAX-WS without an extra servlet?

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.

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?

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