I have method which every now and then generates a string. I would like to register method as uri and produce a exchange method which will be used as input for a route.
The method is call by a different class
SampleClass sc = new SampleClass();
sc.sampleMethod("Hello");
Eg:
public class SampleClass{
#Produce(uri = "direct:consumerMethod")
ProducerTemplate producer;
public sampleMethod(Object obj){
producer.sendBody(object);
}
}
The route is defined as below:
#Override
public void configure() {
from("direct:consumerMethod").process(new GenerateD());
}
But the route doesnt call GenerateD class when i produce using the sampleMethod. Is this not feasible or am i doing something wrong?
Finally this is what worked for my use case.
Starting camelcontext as below:
CamelContext camelContext = new DefaultCamelContext();
camelContext.addRoutes(new SampleRoute());
camelContext.start();
My routebuilder class :
class SampleRoute extends RouteBuilder {
#Override
public void configure() {
try
{
from("direct:consumerMethod").process(new DDT());
}catch(Exception e)
{
e.printStackTrace();
}
}
}
I then create a interface which has a sendMessage method.
public interface DDTConsumer {
public String sendMessage(Object object);
}
Now i implement this method to create an endpoint of this interface and send a message to the endpoint.
DDTConsumer ddt;
try {
ddt = new ProxyBuilder(camelContext).endpoint("direct:consumerMethod").build(DDTConsumer.class);
ddt.sendMessage(msg.getValue());
} catch (Exception e) {
e.printStackTrace();
}
This solved my problem and the route is working fine now. Hope it helps others as well.
In your class where you have the sampleMethod(Object) add the following field:
#Produce(uri = "direct:consumerMethod")
ProducerTemplate template;
In your sampleMethod(Object) you can use the previously added template like this:
public sampleMethod(Object obj){
template.sendBody(object);
}
And it should send a Message to the direct:consumerMethod route.
Use something like this, if you want to call somemethod
#Override
public void configure() {
from("direct:consumerMethod").log(simple("${bean:generateD?method=generateDMethod}"));
}
The above expression will call the generateDMethod of generateD object (bean) and log the methods output to console (the default log writer).
To make above expression work, you have to store generateD bean in the Registry, which will be further associated with your application's CamelContext. You can do the same as follows
#Autowired
private GenerateD generateD;
#Override
protected CamelContext createCamelContext() throws Exception {
SimpleRegistry registry = new SimpleRegistry();
registry.put("generateD", generateD); //the generateD bean,which can be used anywhere in the camelcontext
SpringCamelContext camelContext = new SpringCamelContext();
camelContext.setRegistry(registry); //add the registry
camelContext.setApplicationContext(getApplicationContext());
camelContext.start();
return camelContext;
}
This adds the bean to camelContext. Please check my answer at this link to have complete example.
Related
Context
I've a scenario - I need to expose a rest endpoint and provide a post method , which will be used by a fronend to receive form values (name, email, address).
With these details I need ti call a third party api (that could take upto 10 seconds to respond).
Additionally , I need to store the processed application and the response in DB somewhere to keep a track for monitoring purposes.
Problem
I plan to use Spring #Async functionality for storing details in Queue (the a DB) so that I dont keep the user waiting for response while I do this storing. The #Async seems to be creating new Thread , I can see from the logs but Controller doesn’t send the response back to client (Which is contrary to actual #Async knowledge I have which is not alot) ; So, until the async completes user has to wait for the response.
Is there anything wrong here or missing ?
TIA for your help.
here are some snippets of my classes-
Main
#EnableAsync(proxyTargetClass = true)
#EnableScheduling
#SpringBootApplication
public class CardsApplication {
public static void main(String[] args) {
SpringApplication.run(CardsApplication.class, args);
}
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
Controller
#Validated
#RequiredArgsConstructor
public class CardEligibilityController {
private final CardEligibilityService cardEligibilityService;
#PostMapping("/check-eligibility")
#CrossOrigin(origins = "*")
public EligibilityResponse checkEligibility(#RequestBody #Valid Applicant applicant){
return cardEligibilityService.eligibilityService(applicant);
}
}
Service 1
public interface CardEligibilityService {
EligibilityResponse eligibilityService(Applicant applicant);
}
#Slf4j
#Service
#RequiredArgsConstructor
public class CardEligibilityServiceImpl implements CardEligibilityService {
private final ThirdPartyEligibilityAdapter thirdPartyEligibilityAdapter;
private final QueueService queueService;
private final QueueMessageResponseService queueMessageResponseService;
#Override
public EligibilityResponse eligibilityService(Applicant applicant){
EligibilityResponse eligibilityResponse = checkEligibility(applicant);
queueService.pushMessage(queueMessageResponseService.createQueueResponse(applicant,eligibilityResponse));
return eligibilityResponse;
}
private EligibilityResponse checkEligibility(Applicant applicant) {
return thirdPartyEligibilityAdapter.getEligibility(applicant);
}
}
Service 2
public interface QueueService {
void pushMessage(QueueMessage queueMessage);
void retry();
}
#Service
#RequiredArgsConstructor
#Slf4j
public class QueueServiceImpl implements QueueService{
private final List<QueueMessage> deadQueue = new LinkedList<>();
//TODO check why async gets response stuck
#Override
#Async
public void pushMessage(QueueMessage queueMessage){
try {
//Push message to a queue - Queue settings Rabbit/Kafka - then this could be
//used by listeners to persist the data into DB
log.info("message queued {} ", queueMessage);
} catch (Exception e) {
log.error("Error {} , queueMessage {} ", e, queueMessage);
deadQueue.add(queueMessage);
}
}
**This method is a fault tolerance mechanism in case push to queue had any issues, The Local Method call to pushMessage isn’t the problem I also tried this by deleting retry method method**
#Override
#Scheduled(fixedDelay = 300000)
public void retry() {
log.info("Retrying Message push if there are any failure in enqueueing ");
final List<QueueMessage> temp = new LinkedList<>(deadQueue);
deadQueue.clear();
Collections.reverse(temp);
temp.forEach(this::pushMessage);
}
}
Service 3
public interface QueueMessageResponseService {
QueueMessage createQueueResponse(Applicant applicant, EligibilityResponse eligibilityResponse);
}
#Service
public class QueueMessageResponseServiceServiceImpl implements QueueMessageResponseService {
#Override
public QueueMessage createQueueResponse(Applicant applicant, EligibilityResponse eligibilityResponse) {
return new QueueMessage(applicant,eligibilityResponse);
}
}
EDIT 2
THE MOST STRANGE BEHAVIOUR
If I add Thread.sleep(20); in my async method, This works as expected , the user gets a response back without waiting for async to complete. But Still unable to understand the cause.
#Async
public void pushMessage(QueueMessage queueMessage) {
try {
//Push message to a queue - Queue settings Rabbit/Kafka - then this could be
//used by listeners to persist the data into DB
Thread.sleep(20);
log.info("message queued {} ", queueMessage);
} catch (Exception e) {
log.error("Error {} , queueMessage {} ", e, queueMessage);
deadQueue.add(queueMessage);
}
}
The call to pushMessage in retry is a LOCAL call. So the proxy is not involved an the method is executed synchronously.
You have to move the async method to it's own class.
I want to make periodical REST request with a Dropwizard Backend. More concretely I want to make an GET request to an external REST API every minute and process the result.
I used the quartz here and now I try to use the jersey client to make a REST request. I use guice as my dependency injection.
My application class has the following methods
#Override
public void initialize(final Bootstrap<DockerwizardConfiguration> bootstrap) {
Job everyJob = new EveryTestJob();
bootstrap.addBundle(new JobsBundle(everyJob));
}
#Override
public void run(final DockerwizardConfiguration configuration,
final Environment environment) {
Injector injector = Guice.createInjector(new AbstractModule() {
#Override
protected void configure() {
bind(HelloWorldParameter.class)
.annotatedWith(Names.named("helloWorldParameter"))
.toInstance(configuration.getHelloWorldParameter());
}
});
JerseyClientConfiguration conf = configuration.getJerseyClientConfiguration();
conf.setChunkedEncodingEnabled(false);
final Client client = new JerseyClientBuilder(environment).using(conf).build(getName());
environment.jersey().register(new ExternalServiceResource(client)); // How should that be implented with guice
environment.jersey().register(injector.getInstance(HelloWorldResource.class));
}
And my EveryTestJob class is implemented as follows
#Every("1s")
public class EveryTestJob extends Job {
#Override
public void doJob(JobExecutionContext context) throws JobExecutionException {
// logic run every time and time again
}
}
I am unsure how I this can be organized.
I've been trying to figure this out for a while, and this is what I have found out:
The JobBundle is added before any Resources so the JobExecutionContext will not include the client (https://www.dropwizard.io/0.9.2/docs/manual/internals.html)
Tried using the injector but didn't work either (https://github.com/HubSpot/dropwizard-guice)
Finally I stumbled on Jersey 2.0: Create repeating job which showed how to add the client into the context!
Here's my solution:
In the resource class,
#Path("/myPath")
public class myResource {
#Inject
public myResource() {
try {
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.getContext().put"myResource", this); // Inserts myResource into the context
} catch (SchedulerException e) {
// Handle exception
}
}
// Other stuff for api
}
Then in the job class (I'm using Dropwizard-jobs 2.0.1 where doJobs doesn't take in any arguments so I used execute instead),
#Every("10s")
public class myJob extends Job {
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
myResource res = (myResource) context.getScheduler().getContext().get("myResource");
// Do stuff with your resource
} catch (SchedulerException e) {
// Handle exception
}
}
}
Not sure if you have access to the ExternalServiceResource, but I hope this helps!
I upgraded to camel 2.16 and one of my route Unit Tests started failing.
Here is my route definition:
public class Route extends RouteBuilder{
#Override
public void configure() throws Exception {
from(start).enrich("second");
from("direct:second")
.log(LoggingLevel.DEBUG, "foo", "Route [direct:second] started.");
}
}
Here is my test:
#RunWith(MockitoJUnitRunner.class)
public class RouteTest extends CamelTestSupport {
private Route builder;
#Produce(uri = "direct:start")
protected ProducerTemplate template;
#Before
public void config() {
BasicConfigurator.configure();
}
#Override
protected RouteBuilder createRouteBuilder() {
builder = new Route();
return builder;
}
#Override
protected CamelContext createCamelContext() throws Exception {
SimpleRegistry registry = new SimpleRegistry();
return new DefaultCamelContext(registry);
}
#Test
public void testPrimeRouteForSubscriptionId() {
Exchange exchange = ExchangeBuilder.anExchange(new DefaultCamelContext()).build();
exchange.getIn().setBody(new String("test"));
template.send(exchange);
}
}
The error I'm getting when I run the test is:
org.apache.camel.component.direct.DirectConsumerNotAvailableException: No consumers available on endpoint: Endpoint[direct://second]. Exchange[][Message: test]
Worthy of note is the following line in the camel 2.16 notes:
http://camel.apache.org/camel-2160-release.html
The resourceUri and resourceRef attributes on and has been removed as they now support a dynamic uris computed from an Expression.
Thanks in advance for any help.
Swap the order so the the direct route is started before the enrich.
http://camel.apache.org/configuring-route-startup-ordering-and-autostartup.html
Or use seda instead of direct in your unit test: http://camel.apache.org/seda
Or use ?block=true in the direct uri to tell Camel to block and wait for a consumer to be started and ready before it sends a message to it: http://camel.apache.org/direct
This is a somewhat old issue, but since i pulled out most of my hair out last night, trying to figure out why it was ok to use to("direct:myEndpoint") but not enrich("direct:myEndpoint"), I'll post the answer anyway - maybe it'll save somebody else from getting bald spots ;-)
It turns out to be a test-issue. In case of Direct endpoints, enrich checks whether there is a running route in the context before passing the Exchange to it, but it does so by looking at the CamelContext held by the Exchange it is currently handling. Since you passed your ProducerTemplate an Exchange what was created with a new DefaultCamelContext(), it has no "direct:second" route available.
Luckily there is a couple of simple solutions. Either create the Exchange using the CamelContext from CamelTestSupport, or use the ProducerTemplate sendBody(...) method instead:
#Test
public void testWithSendBody() {
template.sendBody(new String("test"));
}
#Test
public void testPrimeRouteForSubscriptionId() {
Exchange exchange = ExchangeBuilder.anExchange(context()).build();
exchange.getIn().setBody(new String("test"));
template.send(exchange);
}
The blueprint test keeps throwing exception, No Consumers available.
My scenario was that I have an osgi svc which exposes a method which can be called from any another osgi svc.
So the exposed svc method makes a call to a direct:
#EndpointInject(uri = "direct-vm:toRestCall")
ProducerTemplate toRestCall;
svcMethod(Exchange xch){
exchange.setOut(
toRestCall.send("seda:toDirectCall", xch -> {
try{
xch.getIn().setBody("abc");
}catch (Exception ex){
ex.getMessage();
}
}
}).getIn());
And when I tested the direct that it calls, Blueprint advice with JUnit used to keep throwing the following exception:
org.apache.camel.component.direct.DirectConsumerNotAvailableException:
No consumers available on endpoint: Endpoint. Exchange[Message: {..........
I'm new to Guice and Shiro, and i'm trying to use it with my DB (h2).
I've read this : click
but as they said it's just working for the users and roles sections, which is useless for me.
My shiro.ini is working, i managed to create user, login and logout without the Guice part.
My MyShiroModule
public class MyShiroModule extends ShiroModule{
protected void configureShiro() {
try {
bindRealm().toConstructor(IniRealm.class.getConstructor(Ini.class));
} catch (NoSuchMethodException e) {
addError(e);
}
}
#Provides
Ini loadShiroIni() {
return Ini.fromResourcePath("classpath:shiro.ini");
}
}
and my Module :
public class Module extends AbstractModule {
#Singleton
protected void configure() {
Injector injector = Guice.createInjector(new MyShiroModule());
SecurityManager securityManager = injector.getInstance(SecurityManager.class);
SecurityUtils.setSecurityManager(securityManager);
}
}
they're as they said in the tutorial.
What do i have to add to use the [main] part of my shiro.ini?
I never got the JDBC realm to work with Guice since, as you noted, it only reads the users and groups section for whatever reason. I ended up not using Shiro.ini at all just creating the JdbcRealm myself like this:
public class ShiroAuthModule extends ShiroModule {
#Override
public void configure() {
super.configure();
// Bind your data source however you need to - I use JNDI
// but it would be easy to switch to a properties file.
bind(Context.class).to(InitialContext.class);
bind(DataSource.class).toProvider(JndiIntegration.fromJndi(DataSource.class, "java:/comp/env/jdbc/security"));
}
#Provides
#Singleton
JdbcRealm loadJdbcRealm(Ini ini, DataSource ds,
#Named("shiro.authenticationQuery") String authenticationQuery,
#Named("shiro.userRolesQuery") String roleQuery,
#Named("shiro.permissionsQuery") String permissionQuery) {
JdbcRealm realm = new JdbcRealm();
realm.setAuthenticationQuery(authenticationQuery);
realm.setUserRolesQuery(roleQuery);
realm.setPermissionsQuery(permissionQuery);
realm.setPermissionsLookupEnabled(true);
realm.setDataSource(ds);
return realm;
}
#Override
protected void configureShiro() {
// shiro.properties should be on your classpath and
// contain the named properties in loadJdbcRealm
Properties properties = Module.loadProperties(this, "shiro.properties");
Names.bindProperties(binder(), properties);
try {
bindRealm().to(JdbcRealm.class);
} catch (SecurityException e) {
addError(e);
}
}
}
i need to invoke the method base on the cron pattern. this is my java code .where i included one method and this method i need to call.i try in google but not getting any idea how to call.
public class Schedule {
int i;
public String show()
{
return "hi"+i++;
}
public static void main(String args[])throws Exception
{
CamelContext context = new DefaultCamelContext();
context.addRoutes(new RouteBuilder() {
public void configure() {
from("quartz2://myGroup/myfirstrigger?cron=0/2+*+*+*+*+?").to(new Schedule().show());
}
});
context.start();
}
}
i am not sure also this is right or not
You should look at the documentation on bean binding (Here). I think your route would be better like the following:
from("quartz2://myGroup/myfirstrigger?cron=0/2+*+*+*+*+?")
.bean(Schedule.class, "show");