I have simplified my use case with an Order/items example.
An order has some items.
Every item is controlled => An AvailabilityException can be thrown from "controlItem".
After the exception is thrown, item is marked as Ok and next item is handled.
onException(AvailabilityException.class)
.onExceptionOccurred(ItemProcessor)
.handled(true)
.bean(service, "markAsOk");
from(startQueue)
.split(simple("${body.items}"))
.to(direct:controlItem")
.end()
.to(successQueue);
from("direct:controlItem")
.bean(service, "controlItem");
Now I have another case :
When an unexpected exception (NullPointerException, ...) is thrown I would like to stop the process. We don't handle next items and order is redirected to an error queue.
How can I do that ?
You can use doTry and doCatch
from(startQueue)
.doTry()
.split(simple("${body.items}"))
.to(direct:controlItem")
.end()
.to(successQueue);
.doCatch(AvailabilityException.class)
....
....
.handled(true)
.doCatch(RunTimeException.class)
....
....
.handled(false)
.stop() // stop the execution
.doFinally() //optional
Instead of stop() you can write a processor there and use exchange.getContext().stop();
You can as well add another onException for RunTimeExceptions and stop the route.
Another possible way is to use the property ROUTE_STOP . You can set this property to true in a processor on exception.
exchange.setProperty(Exchange.ROUTE_STOP, Boolean.TRUE);
Related
I get this exception anytime my form item contains time
case FormApp.ItemType.TIME:
return item.asTimeItem()
.createResponse(item.asTimeItem().createResponse(1,1));
break;
Issue:
You are unnecessarily calling TimeItem.createResponse(hour, minute) twice, and using the returned value from one of these invocations (that is, an ItemResponse) as a parameter when calling it again.
You should be providing two integers instead of an ItemResponse. Hence the error:
return item.asTimeItem().createResponse(item.asTimeItem().createResponse(1,1));
Solution:
Call this method just once, and provide the appropriate parameters:
case FormApp.ItemType.TIME:
return item.asTimeItem().createResponse(1,1);
break;
If item in question is a TimeItem then there is no method asTimeItem() in the TimeItem Class
Perhaps try this:
case FormApp.ItemType.TIME:
return item.createResponse(1,1);
break;
I'm playing with resumable exceptions. In this example, I try to numify something that doesn't numify. I catch that and attempt to give the $value variable an appropirate value then resume execution:
try {
my $m = 'Hello';
my $value;
$value = +$m;
put "Outside value is 「{$value.^name}」";
CATCH {
when X::Str::Numeric {
put "「$m」 isn't a number!";
put "Inside value is 「{$value.^name}」";
$value = 0;
put "Inside value is now 「$value.」";
.resume;
}
default {
put "Unhandled type 「{.^name}」";
}
}
put "End of the block";
}
put "Got to the end.";
The CATCH block can see the lexical scope it is in, a resuming picks up where it left off. I expected that I'd be able to change $value and have the rest of the block use that value, but outside of the CATCH the value becomes a Failure:
「Hello」 isn't a number!
Inside value is 「Any」
Inside value is now 「0.」
Outside value is 「Failure」
End of the block
Got to the end.
What's up?
Inside of a try block, use fatal takes effect, to cause lazy exceptions returned from method or sub calls to throw immediately. Outside the lexical scope of a try block, note that:
my $value = +$m;
Would result in a Failure being assigned to $value. The try turns it into something more like:
my $value = force-failure(+$m);
Which you could imagine being defined as something like:
sub force-failure(Mu \f) { f.sink if f ~~ Failure; f }
(I'm hand-waving because the compiler spits out the code to do this inline and with a few optimizations).
In the case under consideration, the .sink triggers the exception to the thrown. The CATCH block runs. The .resume indicates that we do not wish to unwind the call stack as would normally happen with a CATCH block, so execution continues inside of force-failure, which then returns f - the Failure. This all happens prior to the assignment in the mainline code to $value; the Failure is therefore assigned, overwriting the value given by the CATCH block.
Unfortunately, you can't escape this with //= because that does the test before running the RHS (which is what we usually want it to do). However, it is possible to do:
my $numified = +$m;
my $value //= $numified;
Of course, this is all a bit of a contrived example, since the normal idiom would be to not have a try block at all, and to write it as:
my $value = +$m // 0;
Thus taking advantage of the Failure. In general, resumable exceptions need a good amount of care, because in many cases code will not be written expecting a resumption to take place. It turns out that the code generated for fatalizing a Failure is one such piece.
I have the following array
ArrayList<Car> list = new ArrayList<>();
I want to iterate it and throw an exception if it contains a certain value
i.e.
if at least one
list.stream.filter(x -> x.color.equals("Black"));
then I want to stop iteration and throw an exception.
Is there a way?
You could use anyMatch for this:
boolean matched = list.stream().anyMatch(x -> x.color.equals("Black"));
if(matched) throw new SomeException();
Since it does not evaluate the rest of the pipeline if the condition is satisfied for one element when iterating through it, and that it returns false if the stream is empty, I think this is what you are looking for.
Of course, you could do it in a single statement but it may not improve readability depending on the situation:
if(list.stream().anyMatch(x -> x.color.equals("Black"))) {
throw new SomeException();
}
Easiest is:
list.forEach( x -> {
if(x.color.equals("Black")) throw new RuntimeException();
});
I have a problem to catch util.JDBCExceptionReporter during save().
Code:
membership.withTransaction { status ->
try{
def membership = membership.findByUserId(userId)
if(!membership){
membership = new membership(userId: userId, email: userEmail, baseLevel: membership.findByName(membershipName.MEMBER)).save(flush: true)
}
}catch(org.springframework.dao.DataIntegrityViolationException e){
status.setRollbackOnly()
return false
}catch(all){
println "Insert Membership Exception.\n User Id: $userId\n " + e
}
When I create two thread to run this code, it throw a error:
2014-05-06 12:53:07,034 [Actor Thread 5] ERROR util.JDBCExceptionReporter - Duplicate entry 'test#gmail.com' for key 'email'
I don't want to show this error every time when their has two threads doing the same insert, because the first thread will go through and insert successfully, so I don't really care about second one.
My question is how to catch util.JDBCExceptionReporter?
Thanks.
Just guessing:
By default Grails doesn't throw exceptions when saving. To throw integrity exceptions you have to use save(failOnError: true).
So in this case, it's just an internal trace (util.JDBCExceptionReporter is not an exception).
In your case, instead of capturing exceptions I'd use validate before saving so you can get the integrity errors before trying to save.
As lsidroGH said, util.JDBCExceptionReporter is not an exception, it's a log message. It logs both SQLExceptions and SQLWarnings. There is no problem with your code, as one thread will have a save() call that returns true and the other thread's save() will get false.
If you don't want this message to show up in your logs, you will need to increase your log level for org.hibernate.util.JDBCExceptionReporter from ERROR to FATAL but this will potentially exclude valid exceptions you would want logged. Your best bet is to ignore it, as your code works.
I'm doing a destroy via ActiveRecord using a field. However, a record in the database with that field value does not exist. So, given this bit of debugging using pry:
[1] pry(main)> fieldvalue
=> "17785"
[2] pry(main)> person = Person.where(:fieldname => fieldvalue.to_i)
=> []
[3] pry(main)> person
=> []
[4] pry(main)> person.destroy_all
=> []
...why does it not raise an exception at step 4?
The original bit of code, similarly, silently fails to raise an exception:
begin
Person.where(:fieldname => fieldvalue.to_i).destroy_all
rescue ActiveRecord::RecordNotFound => exception
#logger.info("ActiveRecord::RecordNotFound exception raised. Details: #{exception}")
rescue Exception => exception
#logger.info("Unknown exception raised. Details: #{exception}")
else
#logger.info("Destroying person with field value: #{fieldvalue}")
end
i.e. it just logs the "Destroying..." line each time it's run.
destroy_all does not raise an exception when executed on an empty set ([]) because the method, according to the documentation:
Destroys the records matching conditions by instantiating each record and calling its destroy method.
Because your where query is returning an empty set, there are no records to instantiate – destroy is never actually being executed on anything, hence there are no exceptions thrown.
Contrast this to a hypothetical situation wherein a set is returned that – for some unlikely reason – contained a nil object. The set might look something like this
people = Person.where("created_at < ?", Time.now)
#=> [#<Person id:1>, nil, #<Person id:2>] # for demonstration only; would not actually happen
people.destroy_all
#=> NoMethodError: undefined method `destroy' for nil:NilClass
A NoMethodError would be thrown on the second iteration of destroy_all, since destroy would be executed on nil, which does not have a destroy method.
One other thought: you may be conflating the where ActiveRecord method with the find method. where returns an ActiveRecord::Relation object, which effectively operates as an array of objects which match the passed conditions. find, in contrast, returns the first object itself_ which match the conditions passed. In the real world example from above, if you're seeking to return a single object from your query, it may make more sense to use find rather than where:
people = Person.where(:fieldname => fieldvalue.to_i) # returns a collection of all objects matching the condition
people.class
#=> ActiveRecord::Relation
person = Person.find(:fieldname => fieldvalue.to_i) # returns the FIRST object matching the condition
person.class
#=> Person