How are Camel routes launched manually? - java

I have a Camel route that is set to run every five minutes
#Component
public class CamelRoute extends RouteBuilder{
private final String comment = "Cron"
#Override
public void setup() {
from("quartz2://myGroup/myTimerName?cron=0+0/5+12-18+?+*+MON-FRI")
.log("Processing from"+comment)
.to("activemq:Totally.Rocks");
}
}
And I want to force it to run manually, from Spring http request, and change comment field in CamelRoute
#RequestMapping(value = "/ex/foos", method = RequestMethod.GET)
#ResponseBody
public String getFoosBySimplePath() {
//TODO: Start Camel route
//change camel log "comment" from "Cron" to "HTTP request"
}

To run a Camel route manually you can use FluentProducerTemplate. You can autowire an instance like a normal bean.
Examples: 1, 2
To be honest, I am not sure if it will work with quartz endpoints, but I am sure it is working pretty well with "direct:" endpoints. Anyway, it could be a good start for your findings.

Solution for my task was easy, although not straightforward if using Camel documentation:
startRoute(String routeId) Starts the given route if it has been
previously stopped
I added another route
from("timer://manualRestart?repeatCount=1")
.routeId("manualRestart")
.noAutoStartup()
.to("activemq:Totally.Rocks");
and use startRoute() to launch it when needed
public String getFoosBySimplePath() {
camelContext.startRoute("manualRestart");
}
Why do I find it "not straightforward"? Because documentation says, that startRoute() can start previously stopped routes. Route was never stopped, it was configured not to start by default.

Related

How do I change only the status code on a Spring MVC error with Boot?

I'm writing a Web application that makes downstream calls using RestTemplate. If the underlying service returns a 401 Unauthorized, I want to also return a 401 to the calling application; the default behavior is to return a 500. I want to keep the default Spring Boot error response as provided by BasicErrorController; the only change I want is to set the status code.
In custom exceptions, I'd just annotate the exception class with #ResponseStatus, but I can't do that here because HttpClientErrorException.Unauthorized is provided by Spring. I tried two approaches with #ControllerAdvice:
#ExceptionHandler(HttpClientErrorException.Unauthorized.class)
#ResponseStatus(UNAUTHORIZED)
public void returnsEmptyBody(HttpClientErrorException.Unauthorized ex) {
}
#ExceptionHandler(HttpClientErrorException.Unauthorized.class)
#ResponseStatus(UNAUTHORIZED)
public void doesNotUseBasicErrorController(HttpClientErrorException.Unauthorized ex) {
throw new RuntimeException(ex);
}
How can I configure MVC to continue to use all of the built-in Boot error handling except for explicitly overriding the status code?
The below code works for me -- in an app consisting of a #RestController whose one method consisted of throw new HttpClientException(HttpStatus.UNAUTHORIZED), running on an embedded Tomcat. If you're running on a non-embedded Tomcat (or, I suspect, on an embedded non-Tomcat) odds are you'll have to do something at least somewhat different, but I hope this answer is at least somewhat helpful anyway.
#ControllerAdvice
public class Advisor {
#ExceptionHandler(HttpClientException.class)
public String handleUnauthorizedFromApi(HttpClientException ex, HttpServletRequest req) {
if (/* ex instanceof HttpClientException.Unauthorized or whatever */) {
req.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, 401);
}
return "forward:/error";
}
}
Explanation: when a HttpClientException is thrown while we're processing request X (in an embedded servlet), what normally happens is that it bubbles all the way up to some org.apache class. (I might fire the debugger up again and work out which one, but this is a pretty high-level explanation so it doesn't matter much.) That class then sends request X back to the application, except this time the request goes to "/error", not to wherever it was originally going. In a Spring Boot app (as long as you don't turn some autoconfiguration off), that means that request X is ultimately processed by some method in BasicErrorController.
OK, so why does this whole system send a 500 to the client unless we do something? Because that org.apache class mentioned above sets something on request X which says "processing this went wrong". It is right to do so: processing request X did, after all, result in an exception which the servlet container had to catch. As far as the container is concerned, the app messed up.
So we want to do a couple of things. First, we want the servlet container to not think we messed up. We achieve this by telling Spring to catch the exception before it reaches the container, ie by writing an #ExceptionHandler method. Second, we want the request to go to "/error" even though we caught the exception. We achieve this by the simple method of sending it there ourselves, via a forward. Third, we want the BasicErrorController to set the correct status and message on the response it sends. It turns out that BasicErrorController (working in tandem with its immediate superclass) looks at an attribute on the request to determine what status code to send to the client. (Figuring this out requires reading the class's source code, but that source code is on github and perfectly readable.) We therefore set that attribute.
EDIT: I got a bit carried away writing this and forgot to mention that I don't think using this code is good practice. It ties you to some implementation details of BasicErrorController, and it's just not the way that the Boot classes are expected to be used. Spring Boot generally assumes that you want it to handle your error completely or not at all; this is a reasonable assumption, too, since piecemeal error handling is generally not a great idea. My recommendation to you -- even if the code above (or something like it) does wind up working -- is to write an #ExceptionHandler that handles the error completely, meaning it sets both status and response body and doesn't forward to anything.
You can customize the error handler of the RestTemplate to throw your custom exception, and then handle that exception with the #ControllerAdvice as you mentioned.
Something like this:
#Configuration
public class RestConfig {
#Bean
public RestTemplate restTemplate(){
// Build rest template
RestTemplate res = new RestTemplate();
res.setErrorHandler(new MyResponseErrorHandler());
return res;
}
private class MyResponseErrorHandler extends DefaultResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse response) throws IOException {
if (HttpStatus.UNAUTHORIZED.equals(response.getStatusCode())) {
// Throw your custom exception here
}
}
}
}

Camel jetty rest methods on 2 ports and protocols

is that possible to do this in camel:
2 rest services standing on jetty, first by http (for example on port 1234) and second https (for example on port 4321), how can I configure it? Is that possible?
Effect which i need to receive (example urls):
http://localhost:1234/firstHttpMethod
http://localhost:1234/secondHttpMethod
https://localhost:4321/firstHttpsMethod
https://localhost:4321/secondHttpsMethod
For this moment when I'm trying to add 2 routes, only second is working. How to solve that problem (I have a think to do 2 rest services - first on jetty, second on something else, but its not good conception).
code looks like this:
camelContext.addRoutes(firstJettyBuilder());
camelContext.addRoutes(secondJettyBuilder());
protected RouteBuilder firstJettyBuilder()
{
return new RouteBuilder()
{
#Override
public void configure()
throws Exception
{
restConfiguration()
.component("jetty")
.host("localhost")
.port(42300)
.scheme("https")
.bindingMode(RestBindingMode.json)
.dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES")
.dataFormatProperty("json.in.enableFeatures", "FAIL_ON_NULL_FOR_PRIMITIVES");
configureSSL();
}
private void configureSSL()
{
final JettyHttpComponent jettyComponent = camelContext.getComponent("jetty", JettyHttpComponent.class);
final Map<String, Object> sslSocketConnectorProperties = new HashMap<>();
sslSocketConnectorProperties.put("keyStorePath", KEYSTORE);
sslSocketConnectorProperties.put("trustStorePath", KEYSTORE);
sslSocketConnectorProperties.put("keyStorePassword", KEYSTORE_PASSWORD);
sslSocketConnectorProperties.put("trustStorePassword", KEYSTORE_PASSWORD);
jettyComponent.setSslSocketConnectorProperties(sslSocketConnectorProperties);
}
};
}
protected RouteBuilder createPosJettyBuilder()
{
return new RouteBuilder()
{
#Override
public void configure()
throws Exception
{
restConfiguration()
.component("jetty")
.host("localhost")
.port(42302)
.scheme("http")
.bindingMode(RestBindingMode.json)
.dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES")
.dataFormatProperty("json.in.enableFeatures", "FAIL_ON_NULL_FOR_PRIMITIVES");
}
};
}
Short answer: I don't thnik this is possible in the same Camel context, because of reasons that I can call bugs. It might be possible with different contexts.
Here are some observations after debugging this.
1st try: as in the question.
Camel uses the same Jetty endpoint for both configurations. The second RouteBuilder overwrites the endpoint configuration of the first one. Hence, the expected first server is not running at all.
2nd try: multiple Jetty endpoints.
One may try something like (after creating Jetty endpoint(s) and adding them to the context):
this.restConfiguration("jetty")....
this.rest("/path").... // good
...
this.restConfiguration("jetty-tls")....
this.rest("/path").... // produces exception!
It looks like the rest definitions are added to the Camel context. On creating routes for the second RouteBuilder, the definition from the first one is already there. Camel wants to create 2 routes with the same path and throws exception:
Failed to start route ... because of Multiple consumers for the same endpoint is not allowed: jetty:...
Unfortunately, it is not an option to skip the rest definition in one of the builders.
Bonus try: multiple Jetty endpoints and different paths.
One expects at least this to work:
this.restConfiguration("jetty")....
this.rest("/path1").... // good
...
this.restConfiguration("jetty-tls")....
this.rest("/path2").... // good
No exceptions here, but Camel starts 3 routes!

Invoke route from Processor

I'm using Camel to integrate 2 systems. I have defined different routes and one of the routes consumes from a specific rabbitmq queue and send it to a REST service. Nothing fancy here, the route looks like this:
public class WebSurfingRabbitToRestRoute extends RouteBuilder{
#Override
public void configure() throws Exception {
from("rabbitmq://rabbit_host:port/Rabbit_Exchange").
setHeader("CamelHttpMethod", constant("POST")).
setHeader("Content-Type", constant("application/json")).
bean(TransformResponse.class, "transform").
to("http4://rest_service_host:port/MyRestService).
}
}
As you can see, i process every message before sending it to the rest service since i need to adjust some things. The problem comes when i find out that sometimes (i dont know how or when), the system that publish into rabbit, send 2 messages concatenated at once.
What i expect to get is a simple json like this:
[{field1:value1, field2:value2}]
What i sometimes get is:
[{field1:value1, field2:value2},{field1:value3, field2:value4}]
So when i face this scenario, the rest service im routing the message to, fails (obviously).
In order to solve this, i would like to know if there is a way to invoke a route from inside a processor. From the previous snippet of code you can see that Im calling the transform method, so the idea will be to do something like the following pseudo-code, because after the route is already fired, i cant split the events and send them both within the same route "instance", so i thought about invoking a different route that i can call from here which will send the message2 to the very same rest service.
public class TransformRabbitmqResponse {
public String transform(String body) throws Exception {
// In here i do stuff with the message
// Check if i got 2 messages concatenated
// if body.contains("},{") {
// split_messages
// InvokeDifferentRoute(message2)
//}
}
}
Do you guys think this is possible?
One option (though I am not sure this is the best option) would be to split this up into two different routes using a direct endpoint.
public class WebSurfingRabbitToRestRoute extends RouteBuilder{
#Override
public void configure() throws Exception {
from("rabbitmq://rabbit_host:port/Rabbit_Exchange")
.setHeader("CamelHttpMethod", constant("POST"))
.setHeader("Content-Type", constant("application/json"))
.bean(TransformResponse.class, "transform");
from("direct:transformedResponses")
.to("http4://rest_service_host:port/MyRestService");
}
}
And then in your transform bean, you can use camel Producer Template to publish the transformed payload(s) to your new direct endpoint (assuming you are using json?).
producerTemplate.sendBody("direct:transformedResponses", jsonString);

Spring Non-blocking Rest "Send and forget"

I'm writing a non-blocking Spring Rest controller. My client should send a request and doesn't care for the response and doesn't need to wait.
This is my server code:
#RestController
#EnableAsync
public class testController {
#RequestMapping(value = "test", method = RequestMethod.GET)
public ResponseEntity<String> test() throws InterruptedException {
timeConsumingMethod();
System.out.println("I'm should be first");
return new ResponseEntity<String>("the server is processing your request", HttpStatus.OK);
}
#Async
private void timeConsumingMethod() throws InterruptedException {
Thread.sleep(1000*5);
System.out.println("I'm should be second!");
}
However, When I call http://localhost:8181/test using(POSTMAN, Chrome, etc...)
I get the following on the server log:
I'm should be second!
I'm should be first
AND only after waiting 5 seconds my browser shows:
the server is processing your request
Is that the correct way for a "send and forget" Behavior?
According to the doc page the #EnableAsync should be added on configuration class.
Enables Spring's asynchronous method execution capability, similar to
functionality found in Spring's XML namespace.
To be used on #Configuration classes as follows, where MyAsyncBean is
a user-defined type with one or more methods annotated with either
Spring's #Async annotation, the EJB 3.1 #javax.ejb.Asynchronous
annotation, or any custom annotation specified via the annotation()
attribute.
why don't you use this:
https://www.baeldung.com/spring-webclient-resttemplate
Webflux client seems to do the same. I was searching for a similar solution where 1 microservice calls multiple microservices async and this fits the model

Jersey #ManagedAsync and copying data between HTTP thread and Worker thread

I am working on a project that works in two flavors with and without multi tenancy.
The project exposes a REST service which I would like to be asynchronous.
So my basic service looks like
#Component
#Path("/resouce")
#Consumes(MediaType.APPLICATION_JSON)
public class ResouceEndpoint {
#POST
#ManagedAsync
public void add(final Event event, #Suspended final AsyncResponse asyncResponse) {
resouce.insert (event);
asyncResponse.resume( Response.status(Response.Status.NO_CONTENT).build());
}
}
That works fine without multi tenancy and I get the benefits of the internal Jersey executor service for free. See #ManagedAsync
When I switch to multi tenancy I add a filter on the request that resolve the tenant id and place it on the thread local (in our case the HTTP thread).
When the processing chain hits the "add()" method above the current thread is the one provided by the Jersey executor service, so it does not include my tenant id.
I could think only on the following options to work around this issue.
Extend the ResouceEndpoint to MutliTenantResouceEndpoint and drop the #ManagedAsync
Using my own thread executor
public class MutliTenantResouceEndpoint extends ResouceEndpoint {
#POST
public void add(final Event event, #Suspended final AsyncResponse asyncResponse) {
final String tenantId = getTeantIdFromThreadLocal();
taskExecutor.submit(new Callable<Void>() {
#Override
public Void call() throws Exception {
setTeantIdToThreadLocal(tenantId);
browserEventsAnalyzer.insertEvent(event);
Response response = Response.status(Response.Status.NO_CONTENT).build();
asyncResponse.resume(response);
return null;
}
});
}
}
But this way I need to manage my own thread executor and it feel's like I am missing something here.
Any suggestion on a different approach?
Here are a handful of recommendations, in order.
For context, I've been using Jersey for 2 years, and faced this exact problem 18 months ago.
1. Stop using #ManagedAsync
If you have control over the http server that Jersey is running on, I would recommend you stop using #ManagedAsync.
Instead of setting up Jersey to return it's http handling thread immediately and offload real request work to a managed executor service thread, use something like Grizzly for your http server, and configure it to have a larger worker thread pool. This accomplishes the same thing, but pushes the async responsibility down a layer, below Jersey.
You'll run into many pain points over the course of a year if you use #ManagedAsync for any medium-to-large project. Here are some of them off the top of my head:
If any ContainerRequestFilter's hits an external service (e.g. an auth filter hits your security module, which hits the database) you will lose the benefits you thought you were gaining
If your DB chokes and that auth filter call takes 5 seconds, Jersey hasn't offloaded work to the async thread yet, so your main thread needed to receive a new conn is blocked
If you set up logback's MDC in a filter, and you want that context throughout your request, you'll need to set up the MDC again on the managed async thread
Resource methods are cryptic to new comers and ugly to read because:
they need an extra parameter
they return void, hiding their real response type
they can "return" anywhere, without any actual return statements
Swagger or other API doc tools cannot automatically document async resource endpoints
Guice or other DI frameworks may have trouble dealing with certain scope bindings and/or providers in async resource endpoints
2. Use #Context and ContainerRequest properties
This would involve involved calling requestContext.setProperty("tenant_id", tenantId) in your filter, then calling calling requestContext.getProperty("tenant_id") in your resource with a #Context injected request.
3. Use HK2 AOP instead of Jersey filters
This would involve setting up an HK2 binding of InterceptionService which has a MethodInterceptor that checks for managed async resource methods and manually executes all RequestScoped bound ContainerRequestFilters. Instead of your filters being registered with Jersey, you'd register them with HK2, to be run by the method interceptor.
I can add more detail and code samples to options 2/3 if you'd like, or give additional suggestions, but it would first be helpful to see more of your filter code, and I again suggest option 1 if possible.

Categories