Can I suspend or resume a route based on a condition? - apache-camel

I want to be able to control programmatically a Route based on a condition but I cannot find a way to do it.
I don't want to stop the flow of a Route as in ProcessorDefinition.stop().
I tried to create a new RoutePolicy without any luck.
public class ProjectStateRoutePolicy extends RoutePolicySupport {
// this would be a check to an outside service
private boolean shouldStartRoute = false;
#Override
public void onStart(Route route) {
suspendIfNotInValidState(route);
}
#Override
public void onResume(Route route) {
suspendIfNotInValidState(route);
}
private void suspendIfNotInValidState(Route route) {
if ( !shouldStartRoute) {
try {
suspendRoute(route);
} catch (Exception e) {
throw ObjectHelper.wrapRuntimeCamelException(e);
}
}
}
}
According to my understanding onResume() and the like are CamelContext specific and not Route specific. Is there a fine-grained mechanism in place to have knowledge of a Route Lifecycle?
I want to be a able to control the lifecycle of the Route even if somene tries to start the Route manually e.g.from JMX.
PS: I don't think that Events such as RouteStartedEvent satisfy this constraint, since messages may have been consumed in between receiving and acting upon the event.

Here is some code that can do it, given you have the camel context and the route name.
public class StopRoute {
Logger log = LoggerFactory.getLogger(getClass().getName());
public void stop(CamelContext camelContext, String routeId) throws Exception {
log.info("Stopping camel route: " + routeId);
camelContext.stopRoute(routeId, 2, TimeUnit.SECONDS);
}
}
Similarly for StartRoute.
And then you can use it in your route like this:
.bean(StopRoute.class, "stop(*, " + INCOMING_SNAPSHOTS.routeId() + ")")
Hope this helps!
P.S. controlbus: essentially would do the same thing, as I understand ;)

Related

How can I do something before processing a request in Nancy 2.x?

I want to do some things in every request, no matter the Module or Route. How can I accomplish this in Nancy 2.x?
If found How to Intercept all Nancy requests and How do I capture all requests irrespective of verb or path, but they are only applicable for Nancy 1.x and the Documentation is out-of-date.
As you say documentation is not updated and most of the resources you can find online are for version 1.x.
How to solve it depends a little bit on what you want to do. If you are not messing with the response you can override ApplicationStartUp in the bootstrapper like this:
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
pipelines.BeforeRequest.AddItemToEndOfPipeline((ctx) =>
{
Console.Out.WriteLine("Hit");
return null;
});
base.ApplicationStartup(container, pipelines);
}
If you, on the other hand, need to meddle with the response and the headers you can do it in the constructor of your overridden NancyModule with your Get setup like this:
public InstrumentProgrammingNancyModule()
{
//// Enable CORS.
After.AddItemToEndOfPipeline((ctx) =>
{
ctx.Response.WithHeader("Access-Control-Allow-Origin", "*")
.WithHeader("Access-Control-Allow-Methods", "GET");
});
Get("/" , _ =>
{
return somethingOrOther;
});
....
}
Both of these solutions work with Nancy 2.0.
You can try this :
public class NewBootstrapper : DefaultNancyBootstrapper
{
protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
{
//Your code here
base.RequestStartup(container, pipelines, context);
}
}

Camel return message from direct route for reuse

I am very confused about returning a "body" from sub-routes called by to() in Camel. From what I found about direct routes is that they encourage route reuse and are used for logical splitting over-complicated routes. But I seem to fail do a simplest "split":
from("jms:createRequestQueue")
.to("direct:createRequest")
// here the processing of the message fails, see below
.bean(processor)
.to("...");
from("direct:createRequest")
.onException(Exception.class).bean(requestErrorHandler).stop()
.unmarshal().json(JsonLibrary.Jackson, MyModelRequest.class);
The class of the processor instance looks like this:
public class RequestProcessor {
#Handler
public void update(#Body MyModelRequest request) {
// do stuff
}
}
The thing is that the result of the route for unmarshalling the request (the 2nd route) is not propagated back to the calling route. An exception is thrown saying that it cannot convert String (the JSON coming into the queue) to the MyModelRequest class. So it seems that the JSON body in the first route is not replaced by the result of the unmarshalling route. This does seem like a nice route reuse I would hope for.
I stumbled upon InOut messages, but the docs are very unclear and my experiments failed the same way.
What do I need to do to really extract parts of a route to another route for reuse?
So the problem was in the onException clause (was left out of original question, I thought it wasnt the problem). I confused end() call with stop() call and so the route was stopped too soon returning the unparsed JSON in String. Correct is:
from("direct:createRequest")
.onException(Exception.class).bean(requestErrorHandler).end()
.unmarshal().json(JsonLibrary.Jackson, MyModelRequest.class);
Without a runnable example that reporoduces theerror it's hard to tell what goes wrong but it should (and does! tested on 2.17.2 as well as 2.15.3) work:
#Component
public class DemoRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
from("timer:sender?delay=10s&period=3s")
.setBody(constant("{\"title\":\"Lord of the Rings\",\"author\":\"J.R.R. Tolkien\"}"))
.to("direct:unmarshal")
.bean(new RequestProcessor())
.log("${body}!");
from("direct:unmarshal")
.unmarshal().json(JsonLibrary.Jackson, Book.class);
}
public static class RequestProcessor {
#Handler
public void update(#Body Book book) {
System.out.println("Got " + book);
}
}
public static class Book {
private final String title;
private final String author;
private Book() {
this(null, null);
}
public Book(String title, String author) {
this.title = title;
this.author = author;
}
public String getTitle() {
return title;
}
public String getAuthor() {
return author;
}
#Override
public String toString() {
return "title='" + title + "', author='" + author + '\'';
}
}
}
Here's the output:
2016-08-30 13:48:34.337 INFO 16240 --- [ main] com.example.DemoApplication : Started DemoApplication in 5.859 seconds (JVM running for 6.587)
Got title='Lord of the Rings', author='J.R.R. Tolkien'
2016-08-30 13:48:44.298 INFO 16240 --- [ timer://sender] route1 : title='Lord of the Rings', author='J.R.R. Tolkien'!
Got title='Lord of the Rings', author='J.R.R. Tolkien'
2016-08-30 13:48:47.232 INFO 16240 --- [ timer://sender] route1 : title='Lord of the Rings', author='J.R.R. Tolkien'!

Getting the current page when receiving a toast notification (WP8.1 Silverlight, receiving WNS toast notification)

I have an event that fires when the app is live and I receive an notification CurrentChannel_PushNotificationReceived. In this function I want to find out which page is currently displayed to know if the notification should update content on the page. The question is therefore twofold, how to know which page is currently displayed and interact with the toast notification.
Update
The issue is that I cannot interact with the elements because of clash with the OS threading (Dispatcher).
Therefore using the below code it allows me to access the content of the message. But I am still not able to get the info of the current_page
_channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
_channel.PushNotificationReceived += OnPushNotificationReceived;
private void OnPushNotificationReceived(PushNotificationChannel sender, PushNotificationReceivedEventArgs args)
{
switch (args.NotificationType)
{
case PushNotificationType.Badge:
this.OnBadgeNotificationReceived(args.BadgeNotification.Content.GetXml());
break;
case PushNotificationType.Tile:
this.OnTileNotificationReceived(args.TileNotification.Content.GetXml());
break;
case PushNotificationType.Toast:
this.OnToastNotificationReceived(args.ToastNotification.Content.GetXml());
break;
case PushNotificationType.Raw:
this.OnRawNotificationReceived(args.RawNotification.Content);
break;
}
args.Cancel = true;
}
private void OnBadgeNotificationReceived(string notificationContent)
{
// Code when a badge notification is received when app is running
}
private void OnTileNotificationReceived(string notificationContent)
{
// Code when a tile notification is received when app is running
}
private void OnToastNotificationReceived(string notificationContent)
{
// Code when a toast notification is received when app is running
// Show a toast notification programatically
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(notificationContent);
var toastNotification = new ToastNotification(xmlDocument);
//toastNotification.SuppressPopup = true;
ToastNotificationManager.CreateToastNotifier().Show(toastNotification);
}
private void OnRawNotificationReceived(string notificationContent)
{
// Code when a raw notification is received when app is running
}
Question
How do I access the current page information in the different onXXXXNotificationReceived. The current snippets work but not within these functions:
var currentPage = ((PhoneApplicationFrame)Application.Current.RootVisual).Content;
var tempBool = currentPage.GetType() is BC_Menu.StartUp.SecondScreen;
or
RootFrame.CurrentSource;
My guess is it is because of the UI-thread. So how can I use the dispatcher to get the information? I have tried some solutions with the dispatcher, but I cannot await the information, and therefore it is not applicable.
System.Windows.Threading.DispatcherOperation op = App.RootFrame.Dispatcher.BeginInvoke(new Func<Uri>(() =>
{
return RootFrame.CurrentSource;
})
);
await op; //Not awaitable.
There's no reason to await the dispatcher to the UI thread. Simply dispatch to the UI thread and then perform the rest of your logic, like displaying the toast or navigating to a page, from within the UI thread...
Register the event...
var channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
channel.PushNotificationReceived += Channel_PushNotificationReceived;
On the event handler, cancel displaying the notification and then dispatch to UI thread...
private void Channel_PushNotificationReceived(PushNotificationChannel sender, PushNotificationReceivedEventArgs args)
{
// Make sure you cancel displaying the toast on this thread (not on UI thread)
// since cancellation needs to be set before this thread/method returns
args.Cancel = true;
// Then dispatch to the UI thread
App.RootFrame.Dispatcher.BeginInvoke(delegate
{
var currPage = ((PhoneApplicationFrame)Application.Current.RootVisual).Content;
switch (args.NotificationType)
{
case PushNotificationType.Toast:
// TODO
break;
}
});
}
Do all of your code inside the dispatcher's delegate. All your code will be executing on the UI thread... you'll be able to navigate pages, obtain current page, etc.
Ok. Try this. Create a static property on App.xaml.cs.
public static object CurrentPageInfo { get; set; }
And assign the page type or page name to the property on 'OnNavigatedTo' method on every page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var currentPage = ((PhoneApplicationFrame)Application.Current.RootVisual).Content;
App.CurrentPageInfo = currentPage.GetType() is BC_Menu.StartUp.SecondScreen;
}
So that you can identify the page source type on receiving notifications by accessing the App.CurrentPageInfo property. Hope it helps!

camel getRoutes returns empty list, how to implement a route controller

i have an java based camel app (using guice). I want to separate start/ stop of all routes into a class "RouteControl" (no route should be aware of this central control).
At the moment all configured routes are autostart=false and RouteControl injects CamelContext and does this:
/**
* Starts all routes found in context.
*/
public void startAll() {
log.info("starting all routes.");
for (Route route : context.getRoutes()) {
String id = route.getId();
try {
log.info("starting route " + id);
context.startRoute(id);
} catch (Exception e) {
throw new IllegalStateException("Unable to start route " + id + " cause, ", e);
}
}
}
But this isn't working: if i call this after main.run() (it is not called, because run not returns) but if i call this before main.run context.getRoutes() is returning an empty list. But log says Total 2 routes, of which 0 is started.
So something is wrong, or is there a better way to implement such a central route control?
Update (Claus' answer is not working):
Now my code look like that:
/**
* Starts all routes found in context.
*/
public void startAll() {
log.info("starting all routes.");
for (RouteDefinition route : ((ModelCamelContext) context).getRouteDefinitions()) {
String id = route.getId();
try {
log.info("starting route " + id);
context.startRoute(id);
} catch (Exception e) {
throw new IllegalStateException("Unable to start route " + id + " cause, ", e);
}
}
}
The loop is correct now - i see "starting route" fore each route but at the end the log states: DefaultCamelContext INFO Total 2 routes, of which 0 is started.
getRoutes is the current routes in CamelContext, which means the current running routes. Use getRouteDefinitions to get all the defined routes (both running and not running) and you can then use that to start the routes. There is a getRouteStatus to know the state of the route whether its running or not.

Custom Exception Message in apex Salesforce

I want to create a custom exception. In this exception I want to make a constructor which take one string arguments and append in getMessage .How to accomplish this.
public class TimelineException extends Exception{
private String message;
public override String getMessage(){
return 'Not able to post to Timeline.getting response from Api : '+ message;
}
}
Main problem I am facing is when I use this:
public TimelineException(Sting x){
}
then its give me error
Save error: System exception constructor already defined: (String) GMirror.cls /Google Salesforce Integration/src/classes.
You do not need to implement something. Just create a your exception class which would extends salesforce Exception class.
public class CommonException extends Exception {}
then just use it as other exception classes
try {
throw new CommonException('Test Common Exception');
} catch(CommonException ex) {
System.debug(ex.getMessage());
}
console output:
09:18:55:059 USER_DEBUG [4]|DEBUG|Test Common Exception
You can use something like this in your controller:
try
{
'content of what you tring to do'
}
catch(Exception e)
{
system.debug(e.getStackTraceString());
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'special friendly message to user'));
}
then in the visualForce page you write apex:messages/ where you want the message to be displayed

Resources