I am trying to create a simple spring boot app which takes list of routes and process it parallelly in routebuilder. I am using proceduretemplate to call my routes by defining my startroute: direct start. When i hit i am getting org.apache.camel.component.direct.DirectConsumerNotAvailableException: No consumers available on endpoint: Endpoint[direct://start].Exchange.[]. I am unable to figure out the issue here.below is my code.
TestController.java
#RestController
#RequestMapping(value = "/service")
#Component
public class TestController {
#EndpointInject(uri = "direct:start")
private ProducerTemplate template;
#RequestMapping(value = "/test",method =RequestMethod.GET)
public void getAccountDetails(){
ArrayList<String> callList = new ArrayList<String>();
callList.add("direct:phone");
callList.add("direct:sms");
callList.add("direct:email");
template.sendBody(callList);
}
CamelRoute.java
#Component
public class CamelRoute extends RouteBuilder {
final String BASE_ROUTE = "direct:start";
public void configure() throws Exception {
from(BASE_ROUTE).recipientList(body()).setParallelProcessing(true);
from("direct:phone").log("customer call made");
from("direct:sms").log("phone call made");
from("direct:email").log("email call made");
}
version
camel-spring-boot-starter', version: '2.17.0'
Thanks in advance.
Related
I have a Spring Boot project that I'm using to receive events from an Amazon SQS queue. I've been using the Spring Cloud AWS project to make this easier.
The problem is this: the Spring Boot application starts up just fine, and appears to instantiate all the necessary beans just fine. However, when the method that is annotated with SqsListener is invoked, all the event handler's dependent beans are null.
Another thing that's important to note: I have two methods of propagating the event: 1) thru a POST web service call, and 2) thru the Amazon SQS. If I choose to run the event as a POST call with the same data in the POST body, it works just fine. The injected dependencies are only ever null whenever the SQSListener method is invoked by the SimpleMessageListenerContainer.
Classes:
#Service("systemEventsHandler")
public class SystemEventsHandler {
// A service that this handler depends on
private CustomService customService;
private ObjectMapper objectMapper;
#Autowired
public SystemEventsHandler(CustomService customService, ObjectMapper objectMapper) {
this.matchStatusSvc = matchStatusSvc;
this.objectMapper = objectMapper;
}
public void handleEventFromHttpCall(CustomEventObject event) {
// Whenever this method is called, the customService is
// present and the method call completes just fine.
Assert.notNull(objectMapper, "The objectMapper that was injected was null");
customService.handleEvent(event);
}
#SqsListener(value = "sqsName", deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS)
private void handleEventFromSQSQueue(#NotificationMessage String body) throws IOException {
// Whenever this method is called, both the objectMapper and
// the customService are null, causing the invocation to
// fail with a NullPointerException
CustomEventObject event = objectMapper.readValue(body, CustomEventObject.class);
matchStatusSvc.scoresheetUploaded(matchId);
}
}
The controller (for when I choose to run the event as a POST). As stated above, it works just fine whenever I run it as a POST call.
#RestController
#RequestMapping("/events")
public class SystemEventsController {
private final SystemEventsHandler sysEventSvc;
#Autowired
public SystemEventsController(SystemEventsHandler sysEventSvc) {
this.sysEventSvc = sysEventSvc;
}
#RequestMapping(value = "", method = RequestMethod.POST)
public void handleCustomEvent(#RequestBody CustomEventObject event) {
sysEventSvc.handleEventFromHttpCall(event);
}
}
Pertinent config:
#Configuration
public class AWSSQSConfig {
#Bean
public SimpleMessageListenerContainer simpleMessageListenerContainer(AmazonSQSAsync amazonSQS) {
SimpleMessageListenerContainer msgListenerContainer = simpleMessageListenerContainerFactory(amazonSQS).createSimpleMessageListenerContainer();
msgListenerContainer.setMessageHandler(queueMessageHandler(amazonSQS));
return msgListenerContainer;
}
#Bean
public SimpleMessageListenerContainerFactory simpleMessageListenerContainerFactory(AmazonSQSAsync amazonSQS) {
SimpleMessageListenerContainerFactory msgListenerContainerFactory = new SimpleMessageListenerContainerFactory();
msgListenerContainerFactory.setAmazonSqs(amazonSQS);
msgListenerContainerFactory.setMaxNumberOfMessages(10);
msgListenerContainerFactory.setWaitTimeOut(1);
return msgListenerContainerFactory;
}
#Bean
public QueueMessageHandler queueMessageHandler(AmazonSQSAsync amazonSQS) {
QueueMessageHandlerFactory queueMsgHandlerFactory = new QueueMessageHandlerFactory();
queueMsgHandlerFactory.setAmazonSqs(amazonSQS);
QueueMessageHandler queueMessageHandler = queueMsgHandlerFactory.createQueueMessageHandler();
return queueMessageHandler;
}
#Bean(name = "amazonSQS", destroyMethod = "shutdown")
public AmazonSQSAsync amazonSQSClient() {
AmazonSQSAsyncClient awsSQSAsyncClient = new AmazonSQSAsyncClient(new DefaultAWSCredentialsProviderChain());
return awsSQSAsyncClient;
}
}
Other info:
Spring boot version: Dalston.RELEASE
Spring cloud AWS version:
1.2.1.RELEASE
Both the spring-cloud-aws-autoconfigure and spring-cloud-aws-messaging packages are on the classpath
Any thoughts?
As spencergibb suggested in his comment above, changing the method's visibility from private to public worked.
I defined a Spring Boot App as a Verticle as follows:
#SpringBootApplication
public class SpringAppVerticle extends AbstractVerticle {
private Vertx myVertx;
#Override
public void start() {
SpringApplication.run(SpringAppVerticle.class);
System.out.println("SpringAppVerticle started!");
this.myVertx = vertx;
}
#RestController
#RequestMapping(value = "/api/hello")
public class RequestController {
#RequestMapping(method = RequestMethod.GET, produces = "application/json")
public void getEcho() {
JsonObject message = new JsonObject()
.put("text", "Hello world!");
myVertx.eventBus().send(EchoServiceVerticle.ADDRESS, message, reply -> {
JsonObject replyBody = (JsonObject) reply.result().body();
System.out.println(replyBody.encodePrettily());
});
}
}
}
I have a second non-Spring Verticle that is basically a echo service:
public class EchoServiceVerticle extends AbstractVerticle {
public static final String ADDRESS = "echo-service";
#Override
public void start() {
System.out.println("EchoServiceVerticle started!");
vertx.eventBus().consumer(EchoServiceVerticle.ADDRESS, message -> {
System.out.println("message received");
JsonObject messageBody = (JsonObject) message.body();
messageBody.put("passedThrough", "echo-service");
message.reply(messageBody);
});
}
}
The problem is that I get a nullpointer at line myVertx.eventbus().send in SpringAppVerticle class as the myVertx variable is null.
How do I properly instantiate a Vertx variable in a Spring context in order that I can exchange message between my both verticles?
My project can be found here: https://github.com/r-winkler/vertx-spring
The reason of the exception is the following:
SpringAppVerticle bean that is created during spring init is another object than starts the spring boot application. So you have two objects, one that has start() method invoked and another one that doesn't. Second one actually handles requests. So what you need is to register verticles as spring beans.
For samples of vertx/spring interoperability please refer to vertx examples repo.
P.S. I've created a pull request to your repo to make your example work.
We are facing a problem with Hystrix Command in a Spring Boot / Cloud microservice. We have a Spring Component containing a method annotated with #RabbitListener. When a new message arrives, the method delegates the invocation to NotificationService::processNotification().
The NotificationService is a bean annotated with #Service. The method processNotification() can request third party applications. We want to wrap the invocation of third party applications using #HystrixCommand to provide fault tolerance, but due to some reasons the Hystrix Command annotated method is not working.
If we invoke a Controller and the Controller delegates the invocation to a Service method, which in turns have a Hystrix Command , everything works perfectly. The only problem with Hystrix Command arises when the microservices consume a messages and it seems to be Hystrix Command doesn’t trigger the fallback method.
Here is the non-working code:
#Component
public class MessageProcessor {
#Autowired
private NotificationService notificationService;
#RabbitListener(queues = "abc.xyz-queue")
public void onNewNotification(String payload) {
this.notificationService.processNotification(payload);
}
}
#Service
public class NotificationService {
public void processNotification(String payload) {
...
this.notifyThirdPartyApp(notificationDTO);
...
}
#HystrixCommand(fallbackMethod = "notifyThirdPartyAppFallback")
public void notifyThirdPartyApp(NotificationDTO notificationDTO) {
//Do stuff here that could fail
}
public void notifyThirdPartyAppFallback(NotificationDTO notificationDTO) {
// Fallbacl impl goes here
}
}
#SpringBootApplication
#EnableCaching
#EnableCircuitBreaker
#EnableDiscoveryClient
#EnableRabbit
public class NotificationApplication {
public static void main(String[] args) {
SpringApplication.run(NotificationApplication.class, args);
}
}
I'm not sure about your problem without looking at the code.
As another approach you can take: instead of describing this calls with annotations in your service, just extend HystrixCommand and implement api calling logic in it (read more):
public class CommandHelloWorld extends HystrixCommand<String> {
private final String name;
public CommandHelloWorld(String name) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.name = name;
}
#Override
protected String run() {
// a real example would do work like a network call here
return "Hello " + name + "!";
}
}
I am using a spring boot application with #EnableZuulProxy annotation. But I would like to add custom routes during runtime. How is this possible?
Existing documentation only shows static examples, in which routes are defined in the application.yml. Could you point me to code snippets of my use case.
In the ZuulConfiguration I found a possibility to add routes routeLocator().getRoutes().add(route); but they are not applied to the runtime. What am I missing?
Thanks a lot. Cheers
Gerardo
What I did was subclass the SimpleRouteLocator class with my own RouteLocator class. Here is sample of what I did:
public class RouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {
#Autowired
private ZuulHandlerMapping zuulHandlerMapping;
private Map<String, ZuulRoute> routes = new ConcurrentHashMap<>();
public RouteLocator(TaskExecutor executor, String servletPath, ZuulProperties properties) {
super(servletPath, properties);
executor.execute(new ServiceWatcher());
}
#Override
public Map<String, ZuulRoute> locateRoutes() {
return this.routes;
}
#Override void refresh() {
this.doRefresh();
}
private class ServiceWatcher implements Runnable {
#Override
public void run(){
// Add your routes to this.routes here.
ZuulRoute route1 = new ZuulRoute("/somePath", "http://someResourceUrl:8080");
ZuulRoute route2 = new ZuulRoute("/someOtherPath", "some-service-id");
routes.put("/somePath", route1);
routes.put("/someOtherPath", route2);
zuulHandlerMapping.setDirty(true);
}
}
}
I'm not exactly sure when the ServiceWatcher gets called since in my actual code the ServiceWatcher wraps around a Kubernetes Watcher (since I am running Zuul in an OpenShift environment), but this should provide the gist of how to get started.
Here my Rest WS:
#Path("/personService")
#Service
public class PersonRestService {
Logger logger = LoggerFactory.getLogger(PersonRestService.class);
#Autowired
private PersonService personService;
#GET
#Path("{id}")
#Produces({ MediaType.APPLICATION_JSON })
public Person getPersonByID(#PathParam("id") String id) {
logger.debug("getItemByID with id {}", id);
return personService.getPersonById(id);
}
Here is the unit test:
public class PersonServiceRestTest extends JerseyTest {
public PersonServiceRestTest() throws Exception {
super(new WebAppDescriptor.Builder("com.intesasanpaolo.web.rest.service").
contextPath("test")
.contextParam("contextConfigLocation", "classpath*:application-context/web-test-context.xml")
.contextListenerClass(ContextLoaderListener.class)
.build());
}
#Test
public void testGetPerson() {
Client client = Client.create();
WebResource webResource = client.resource("http://localhost:9998/test/personService/1");
ClientResponse response = webResource.type(MediaType.APPLICATION_JSON).get(ClientResponse.class);
System.out.println(response);
}
}
When I run the test I receive and exception on personService.getPersonById(id) since personService is not autowired.
It seems that all the rest services created by grizzly do not share the spring context defined above:
INFO: Scanning for root resource and provider classes in the packages:
com.intesasanpaolo.web.rest.service
10-feb-2014 19.10.01 com.sun.jersey.api.core.ScanningResourceConfig logClasses
INFO: Root resource classes found:
class com.intesasanpaolo.web.rest.service.PersonRestService
class com.intesasanpaolo.web.rest.service.MyResource
I read a lot of discussion but still no solution.
Any idea?
Kind regards.
Massimo
Ok, I found the problem.
The context was not shared because the WebAppDescriptor was not configured with the SpringServlet.
The working configuration is the following.
super(new WebAppDescriptor.Builder("com.intesasanpaolo.web.rest.service")
.contextPath("test")
.contextParam("contextConfigLocation", "classpath*:application-context/web-test-context.xml")
.servletClass(SpringServlet.class)
.initParam("com.sun.jersey.api.json.POJOMappingFeature", "true")
.contextListenerClass(ContextLoaderListener.class)
.build());
Hope this could help someone ;)
Max