I have implemented a Camel Service , but when i try to shutdown my route , it's impossible .... I have to kill the process. What i have missed ?
First I create a class which implements camel.Service :
#Service("myService")
public class MyService implements org.apache.camel.Service {
...
public WebSocket ws = null;
private Boolean isRunning=true;
public void mainCall() {
try {
.....
ws = connect();
while(isRunning) {
.....
}
} catch (IOException e) {
e.printStackTrace();
} catch (WebSocketException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void start() throws Exception {
isRunning = true;
mainCall();
}
#Override
public void stop() throws Exception {
isRunning = false;
ws.disconnect();
}
I add my service on my Camel context like below :
#Autowired
private MyService myService;
#Autowired
private CamelContext context;
#PostConstruct
public void setupCamelContext() throws Exception {
....
context.addService(myService);
}
At the end i start my route :
from("timer://runOnce?repeatCount=1&delay=5000")
.serviceCall("myService");
use HAWTIO for CAMEL if you want to stop/start routes manually.
Here's the link: http://hawtio.github.io/hawtio/plugins/camel/
I resolve my problem by splitting my Service in two :
One who implement org.apache.camel.Service
Second who implement start fucntion but with an #Async anotation
The main problem in my case was my infinite loop block stuck the start function, Asunc method resolve the problem
Related
apologies for the basic question; I'm new to the Java world and the spring framework. I've built a little example application that makes a bunch of async requests to an external service and returns a list of the responses ('metrics'), but I need to make my application wait until all the responses have come back. Right now I have a (don't hate me) Thread.sleep while I let the results come back, but obviously this is very nasty. Can anyone suggest a better way of architecting this?
Calling class:
#Service
public class MetricService {
#Autowired
private MetricProcessor processor;
private LinkedBlockingQueue<Metric> queue;
#Scheduled(fixedDelay = 60000)
public void queryExternalService() {
List<Metrics> metrics = new ArrayList<>();
metrics = processor.getMetrics();
//this is horrible and I'm a horrible human being
try {
Thread.sleep(10000); //wait for the requests to come back
}
catch (Exception e) {
e.printStackTrace();
}
queue.addAll(metrics);
}
}
Class:
#Component
public class MetricProcessor {
#Autowired
private AsyncClient externalClient;
public List<Metrics> getMetrics() {
List<Metrics> returnObj = new Arraylist<>();
for(Blah blah : bleh) {
Request request = new Request("abc");
externalClient.getMetricAsync(request, new AsyncHandler<request, result>() {
#Override
public void onError(Exception e) {
System.out.println("Error");
}
#Override
public void onSuccess(Request request, Result result) {
returnObj.add(new Metric(result.getKey(), result.getValue()));
}
});
}
return returnObj;
}
}
Any help would be greatly appreciated!
Try a Future.
In MetricService:
public void queryExternalService() {
Future<List<Metrics>> metricsFuture = processor.getMetrics();
try {
queue.addAll(metricsFuture.get(60, TimeUnit.SECONDS));
} catch (InterruptedException | ExecutionException | TimeoutException e) {
e.printStackTrace();
}
}
So notice instead of the desired List, your processor provides a reference to a Future which may fulfil that request later:
public Future<List<Metrics>> getMetrics() {
MetricsFuture metricsFuture = new MetricsFuture();
// Need to ask for the metrics to be built
metricsFuture.buildMetrics();
return metricsFuture;
}
private static class MetricsFuture extends AbstractFuture<List<Metrics>> {
// Assuming the requests are asynchronous, this should be a thread-safe list
List<Metrics> returnObj = new CopyOnWriteArrayList<>();
void buildMetrics() {
for(Blah blah : bleh) {
final Request request = new Request("abc");
externalClient.getMetricAsync(request, new AsyncHandler<request, result>() {
#Override
public void onError(Exception e) {
onError(request, e);
}
#Override
public void onSuccess(Request request, Result result) {
addMetrics(new Metrics(result.getKey(), result.getValue()));
}
});
}
}
void onError(Request request, Exception e) {
// Is any error a total failure? This allows us to terminate waiting
setException(e); // alternative we could remove request or keep a list of errors
System.out.println("Error");
}
void addMetrics(Metrics metric) {
returnObj.add(metric);
// Once we have received the expected number of results we can pass that prepare that
// as a result of this future.
if(returnObj.size() == bleh.size()) {
set(returnObj);
}
}
}
I am attempting to run multiple services parallely using ExecutorService. But i failed to execute parallely.
I have written java.util.concurrent.TimeUnit.MINUTES.sleep(1) to wait one minute in Service1 class.
But Service2 is processing only after Service1 processed.
Below is my code snippet, Kindly correct me/code if my understand about ExecutorService is wrong
public void startService() {
try {
ExecutorService service = Executors.newFixedThreadPool(3);
service.submit(new Service1());
service.submit(new Service2());
service.submit(new Service3());
service.shutdown();
service.awaitTermination(1, TimeUnit.MINUTES);
System.exit(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public class Service1 implements Callable<Object> {
{
try {
java.util.concurrent.TimeUnit.MINUTES.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public Object call() throws Exception {
return null;
}
}
public class Service2 implements Callable<Object> {
#Override
public Object call() throws Exception {
System.out.println(" Service 2 "); // It prints after 1 minute only.
return null;
}
}
public class Service3 implements Callable<Object> {
#Override
public Object call() throws Exception {
System.out.println(" Service 3 ");
return null;
}
}
The code:
{
try {
java.util.concurrent.TimeUnit.MINUTES.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
}
is a constructor, and it is called by the main thread when it's doing new Service1().
So yeah, it must complete before it has a chance to submit the services.
UPDATE:
In your original post, the sleep was in the call method, and it worked. Now, your Service1 is equivalent to:
public class Service1 implements Callable<Object> {
public Service1() {
try {
java.util.concurrent.TimeUnit.MINUTES.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public Object call() throws Exception {
return null;
}
}
And only the call method is run by the executor. the Service1 instance cannot even be submitted before the constructor completes.
I would like to use Apache Camel for sending files from a local directory to a FTP location. We only get 1 or 2 times a day files in that local directory. So it isn’t necessary to have that connection open the whole day. What I would like to achieve is that we will open the connection when there are files in the directory (this is something we could do by checking the dir), but how could we check if sending the files with Apache Camel is done and close the connection?
Is there some feedback from the RouteBuilder or what is the best way to achieve this?
Thanks
You can use the disconnect=true option to close the connection after sending is complete.
See the documentation at: http://camel.apache.org/ftp2
I solved the issue by using the FTPToClientRouteBuilder which is called in the following method:
#Override
public void sentFilesViaFTPToClient() {
CamelContext context = new DefaultCamelContext();
try {
context.addRoutes(new FTPToClientRouteBuilder());
context.start();
while (true) {
if (CollectionUtils.isEmpty(context.getRoutes())) {
LOG.info("Finished sending files via FTP.");
context.stop();
break;
}
}
} catch (Exception e) {
throw new RuntimeException("Couldn't FTP files", e);
} finally {
try {
context.stop();
} catch (Exception e) {
//ignore exception
}
}
}
FTPToClientRouteBuilder
public class FTPToClientRouteBuilder extends RouteBuilder {
private static final Logger LOG = LoggerFactory.getLogger(FTPToClientRouteBuilder.class);
private String ftpLocation;
private String uploadLocation = "file:" + location + "?delete=true&moveFailed=../error&sendEmptyMessageWhenIdle=true";
#Override
public void configure() throws Exception {
// set graceful shutdown timeout
getContext().getShutdownStrategy().setTimeout(10);
from(uploadLocation)
.choice()
.when(body().isNotNull())
.log("Uploading file ${file:name}")
.to(ftpLocation)
.log("Uploaded file ${file:name} complete.")
.otherwise()
.process(new ShutdownProcessor());
}
private class ShutdownProcessor implements Processor {
#Override
public void process(Exchange exchange) throws Exception {
new Thread() {
#Override
public void run() {
try {
exchange.getContext().stop();
} catch (Exception e) {
LOG.error("Couldn't stop the route", e);
}
}
}.start();
}
}
}
I have a MessageDriven bean which consume messages and send it to somewhere else by REST API. Like this:
#MessageDriven(name = "n", activationConfig = {...})
public class SubmitMessageBean implements MessageListener {
#Resource
private MessageDrivenContext context;
#Override
public void onMessage(final Message message) {
try {
// Submit data to somewhere with REST API
} catch (IOException ex) {
this.context.getRollbackOnly();
} catch (JMSException ex) {
e.printStackTrace();
}
}
}
When error happens I want to retry sending in a minute. It there anyway to do that?
OK, I figured it out. You can also easily add delay to give the remote system more time to resolve the error.
#MessageDriven(name = "n", activationConfig = {...})
public class SubmitMessageBean implements MessageListener {
#Inject
JMSContext context;
#Resource(mappedName = "...")
Queue queue;
#Override
public void onMessage(Message message) {
Integer retry = 0;
try {
// track how many time we tried before
retry = message.getIntProperty("retry");
// Submit data to somewhere with REST API
} catch (IOException ex) {
// Put it back in queue again
// You can limit number of retry by keeping retry variable
if (retry < 5) {
JMSProducer producer = this.context.createProducer();
producer.setProperty("retry", r);
// Add some delay to start it again after a minute
producer.setDeliveryDelay(60000);
// Send it again with send()
}
} catch (JMSException ex) {
e.printStackTrace();
}
}
}
Of course you need to fill {...} with your own right values.
I have this code snippet for executing a script shell from a java Tomcat web development using wicket.
public class CallingScript extends RouteBuilder {
String result;
#Override
public void configure() throws Exception {
from("direct:exec")
.to("exec:ls?args=/home/foo/")
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
ExecResult execresult = exchange.getIn().getBody(ExecResult.class);
result = execesult.toString();
}
});
}
public String getResult() {
return result;
}
}
and I use it in the Onclick method in Wicket 7.0.0, no error message has been generated but always a null string is returned by the exec route.
CamelContext camelContext = new DefaultCamelContext();
CallingScript call = new CallingScript();
try {
camelContext.addRoutes(call);
} catch (Exception e) {
e.printStackTrace();
}
try {
camelContext.start();
} catch (Exception e) {
e.printStackTrace();
}
try {
camelContext.stop();
} catch (Exception e) {
e.printStackTrace();
}
String res = call.getResult();
getSession().info("directory contents " + res + " !");
Just starting camel context is not sufficient to trigger a route. If you want to manually trigger a route, you can use camelContext.startRoute() method.
By the way, IMHO, It is a best practice to start one camel context for per application.