Creating a global interceptor for all defined routes - spring-boot

I have multiple route builders within my project,
I want to define a single interceptSendToEndpoint that will affect all of the defined routes
For Example:
Public Route1 extends RouteBuilder {
public void configure() throws Exception {
from("direct:endpoint1").toD("http:\\someAddress1");
}
}
Public Route2 extends RouteBuilder {
public void configure() throws Exception {
from("direct:endpoint2").toD("http:\\someAddress2");
}
}
what I want to do here is to define a central interceptSendToEndpoint that will automatically capture all traffic sent to the camel HTTP component for all routes.
public class InterceptRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
interceptSendToEndpoint("http:*")
.process(exchange -> System.out.println("Hi from intercept"));
}
}
However due to how camel injects the intercept scope, I'm unable to do this easily,
Is there a way to tell the camel context that this intercept is for all defined routes within the context?
Note: I'm using camel 3.0.0

Camel team works for similar task in there (https://issues.apache.org/jira/projects/CAMEL/issues/CAMEL-16757?filter=allissues&orderby=cf%5B12310200%5D+ASC%2C+priority+DESC%2C+updated+DESC).
Create an abstract base class and define your in there and then extends it in main route
public abstract class BaseRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
interceptSendToEndpoint("http:*")
.process(exchange -> System.out.println("Hi from intercept"));
}
}
public class TaskRoute2 extends BaseRoute {
#Override
public void configure() throws Exception {
super.configure();
from("direct:endpoint2").toD("http:\\someAddress2");
}
}
public class TaskRoute2 extends BaseRoute {
#Override
public void configure() throws Exception {
super.configure();
from("direct:endpoint2").toD("http:\\someAddress2");
}
}

Related

How to add Global Exception Handling on RouteTemplate

I am looking for a way to add global exception handling on all routes generated by multiple RouteTemplates. I have tried the following way but the onException block is not getting added to the routes.
Can you help me understand what I am doing wrong?
Thanks
public abstract class BaseRouteBuilder extends RouteBuilder {
#Override
public void configure(){
this.onException(IllegalStateException.class)
.log("global onException")
.maximumRedeliveries(2)
.redeliveryDelay(100)
.logStackTrace(true)
.to("direct:retryChannel");
}
}
public static class SampleRouteTemplate extends BaseRouteBuilder {
#Override
public void configure() {
super.configure();
this.routeTemplate("myTemplate")
.templateParameter("parameter1")
.from("direct:start")
.setHeader("parameter1", constant("value1"))
.log("RouteCompleted");
}
}
Try to extract a separate method for route, e.g. configureRoute() and call it in parent class in configure() method. Also make a child class non-static.
public abstract class BaseRouteBuilder extends RouteBuilder {
#Override
public final void configure(){
onException(IllegalStateException.class)
.log("global onException")
.maximumRedeliveries(2)
.redeliveryDelay(100)
.logStackTrace(true)
.to("direct:retryChannel");
configureRoute();
}
public abstract void configureRoute();
}
public class SampleRouteTemplate extends BaseRouteBuilder {
#Override
public void configureRoute() {
this.routeTemplate("myTemplate")
.templateParameter("parameter1")
.from("direct:start")
.setHeader("parameter1", constant("value1"))
.log("RouteCompleted");
}
}
}

Injecting dependency inside AbstractWebSocketHandler

How to inject a dependency inside a Web Socket handler:
public class WebsocketHandler extends AbstractWebSocketHandler {
#Autowired
GreetingMap greetingMap;
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
// NullPointerException here
String greeting = greetingMap.getSampleGreetings().get("hello") + " " + message.getPayload();
session.sendMessage(new TextMessage(greeting));
}
}
The code above throws NullPointerException
What could be missing here?
Try using dependency injection with constructor instead #Autowired:
private GreetingMap greetingMap;
public WebsocketHandler(GreetingMap greetingMap){
this.greetingMap = greetingMap
}
I think the problem is that SocketHanler is not a spring bean, but is created by "new" operator:
#Configuration
#EnableWebSocket
public class WebSocketsConfiguration implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new SocketHandler(), "/socket")
.setAllowedOrigins("*");
}
}
What you need to do in this case, is to inject your dependency into WebSocketConfiguration and pass it manually to SocketHandler constructor:
#Configuration
#EnableWebSocket
public class WebSocketsConfiguration implements WebSocketConfigurer {
#Autowired
MyDependency myDependency;
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new SocketHandler(myDependency), "/socket")
.setAllowedOrigins("*");
}
}
And in the handler, you need to add constructor that receives the dependency
public class SocketHandler extends AbstractWebSocketHandler {
private MyDependency myDependency;
public SocketHandler(MyDependency myDependency) {
this.myDependency = myDependency;
}
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
System.out.println(String.format("Message from client: %s", message));
}
}

Custom AbstractEndpoint listening to "/" (root)

I've implemented a starter that configures Swagger the way I like. In addition, I'd like to redirect every call to the app's root URL (e.g. localhost:8080) to /swagger-ui.html.
Therefore, I added an own AbstractEndpoint which is instantiated in the #Configuration class as follows:
#Configuration
#Profile("swagger")
#EnableSwagger2
public class SwaggerConfig {
...
#Bean
public RootEndpoint rootEndpoint() {
return new RootEndpoint();
}
#Bean
#ConditionalOnBean(RootEndpoint.class)
#ConditionalOnEnabledEndpoint("root")
public RootMvcEndpoint rootMvcEndpoint(RootEndpoint rootEndpoint) {
return new RootMvcEndpoint(rootEndpoint);
}
}
The respective classes look like this:
public class RootEndpoint extends AbstractEndpoint<String> {
public RootEndpoint() {
super("root");
}
#Override
public String invoke() {
return ""; // real calls shall be handled by RootMvcEndpoint
}
}
and
public class RootMvcEndpoint extends EndpointMvcAdapter {
public RootMvcEndpoint(RootEndpoint delegate) {
super(delegate);
}
#RequestMapping(method = {RequestMethod.GET}, produces = { "*/*" })
public void redirect(HttpServletResponse httpServletResponse) throws IOException {
httpServletResponse.sendRedirect("/swagger-ui.html");
}
}
As stated in public RootEndpoint(), the custom Endpoint is bound to /root. Unfortunately, I can't specify super(""); or super("/"); as those values throw an exception (Id must only contains letters, numbers and '_').
How can I achieve having a custom Endpoint listening to the root URL in a starter using #Configuration files to instantiate beans?
I solved it with an easier approach by adding a WebMvcConfigurerAdapter bean in the #Configuration:
#Bean
public WebMvcConfigurerAdapter redirectToSwagger() {
return new WebMvcConfigurerAdapter() {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("").setViewName("redirect:/swagger-ui.html");
}
};
}

Apache Camel - SedaEndpoint

I'm trying to send a message to an async route but it's not working.
I have just created a projeto on github to simulate the problem
#SpringBootApplication
public class SedaQueueApplication implements CommandLineRunner {
#Autowired
#EndpointInject(uri = "direct://direct-queue")
ProducerTemplate producerTemplate;
public static void main(String[] args) {
SpringApplication.run(SedaQueueApplication.class, args);
}
#Override
public void run(String... strings) throws Exception {
producerTemplate.sendBody("Teste Direct - Async");
}
#Component
class Router extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct://direct-queue").routeId("toAsync").to("seda://async-queue?size=100");
from("seda://async-queue").routeId("toLog").log("${body}");
}
}
you have two routes. In one of the route you have specified seda://async-queue and in other seda://async-queue?size=100 make this consistent i.e. add size attribute to first route or remove from second. It will work like a peach.
The reason for this is (Not sure if it is a bug in camel code), In SedaComponent::getOrCreateQueue they are comparing for size attribute also. Hence you get an exception if the size attribute if present and doeśn't match.
Hope that helps.

How to go about Spring autowiring?

public class ProcessSchedulerServlet implements javax.servlet.Servlet {
Timer timer=new Timer();
#Override
public void init(ServletConfig arg0) throws ServletException {
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
LogProcessorService logProcessorService=new LogProcessorServiceImpl();
logProcessorService.processPageRequestsLogs();
}
}, 60*1000, 120*1000);
}
This is ugly and it doesn't work, anyway. The LogProcessorServiceImpl has properties with #Autowired annotation. These properties are not autowired when this code runs. This may be expected.
The real question is: how to make this run() method work. It seems to me that Spring wants the logProcessorService to be autowired to have properties within LogProcessorServiceImpl autowired, as well.
=== SCENARIO 1 ==============================================================
public void run() {
final LogProcessorService logProcessorService=null;
WebApplicationContextUtils.getWebApplicationContext(arg0.getServletContext()).getAutowireCapableBeanFactory().autowireBean(logProcessorService);
logProcessorService.processPageRequestsLogs();
}
Result: compile time error: Cannot refer to a non-final variable arg0 inside an inner class defined in a different method
=== SCENARIO 2 ==============================================================
#Autowired
LogProcessorService logProcessorService;
public void run() {
logProcessorService.processPageRequestsLogs();
}
Result: run time error: logProcessorService is null;
==== SOLUTION (from Boris) ======================================================
public class ProcessSchedulerServlet implements javax.servlet.Servlet {
Timer timer=new Timer();
#Autowired
LogProcessorService logProcessorService;
#Override
public void init(ServletConfig arg0) throws ServletException {
final AutowireCapableBeanFactory autowireCapableBeanFactory=WebApplicationContextUtils.getWebApplicationContext(arg0.getServletContext()).getAutowireCapableBeanFactory();
autowireCapableBeanFactory.autowireBean(this);
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
logProcessorService.processPageRequestsLogs();
}
}, 60*1000, 120*1000);
}
Why bother with servlets and Timer class if Spring has a built in scheduling support:
#Service
public class LogProcessorService {
#Scheduled(fixedRate=120*1000, initialDelay=60*1000)
public void processPageRequestsLogs() {
//...
}
}
That's it! No timers, runnables and servlets. Note: initialDelay was introduced in Spring 3.2 M1 (see SPR-7022).

Resources