I have a very specific question, and i really searched the answer all over the place...
Here is a situation: i have a Scatter-Gather component with a custom aggregation strategy.
http://clip2net.com/s/j66jK8 - Image of a subflow
Semantic of this process is rather simple. Request comes with Basic Authentication Header, the upper road calls just empty java processor, which returns original payload, the lower road authenticates user over LDAP, and returns Boolean result of this authentication process. Custom aggregation class checks result and if authentication was OK, then returns original payload, which results from the road #1. If not OK, then throws exception. Nothing wrong here, it works.
There is a bit tricky thing. If a user passed wrong authentication data then exception occurs in ldap:bind module. According to documentation exception is propagated to the Scatter-Gather so i'm trying to catch it using this:
#Override
public MuleEvent aggregate(AggregationContext context) throws MuleException {
for (MuleEvent event: context.collectEventsWithExceptions()) {
event.getMessage().getExceptionPayload().getException().printStackTrace();
throw new RuntimeException(event.getMessage().getExceptionPayload().getException());
}
MuleEvent result = DefaultMuleEvent.copy(context.getEvents().get(0));
if (!(Boolean) context.getEvents().get(1).getMessage().getPayload()) {
throw new SecurityException();
}
return result;
}
BUT!
As a result i see exception which stacktrace does not have javax.naming.AuthenticationException which was rased by ldap:bind component, and was printed to log automaticaly (see below).
So, my question is: how can i reach and rethrow this javax.naming.AuthenticationException exception out of Custom Aggregation Class?
I'd appreciate all you ideas and help. Thank you in advance.
WARN 2014-10-15 20:51:18,552 [[minkult].ScatterGatherWorkManager.02] org.mule.module.ldap.api.jndi.LDAPJNDIConnection: Bind failed.
ERROR 2014-10-15 20:51:18,559 [[minkult].ScatterGatherWorkManager.02] org.mule.retry.notifiers.ConnectNotifier: Failed to connect/reconnect: Work Descriptor. Root Exception was: javax.naming.AuthenticationException: [LDAP: error code 49 - INVALID_CREDENTIALS: Bind failed: Attempt to lookup non-existant entry: cn=sim,ou=people,dc=example,dc=com]; resolved object com.sun.jndi.ldap.LdapCtx#5de37d66. Type: class javax.naming.AuthenticationException
COUNT: 1
org.mule.api.transport.DispatchException: route number 1 failed to be executed. Failed to route event via endpoint: InterceptingChainLifecycleWrapper 'wrapper for processor chain 'null''
[
ScriptComponent{CheckAuth.component.553657235},
org.mule.module.ldap.processors.BindMessageProcessor#647af13d,
org.mule.module.ldap.processors.SearchMessageProcessor#2aac6fa7,
InvokerMessageProcessor [name=ldapUtils, object=com.at.mkrf.aggregate.LDAPUtils#5714c7da, methodName=findGroupByName, argExpressions=[#[payload], #[systemName]], argTypes=[Ljava.lang.Class;#5af349a6]
]. Message payload is of type: NullPayload
On a CompositeRoutingException, you can call:
exception.getExceptions().values()
to get an Array of Throwables thrown from within the scatter-gather. Then just re-throw the appropriate exception.
Related
Background
We use ApolloHandler to handle the exceptions in our Nest.js + GraphQL application.
Problem
Although ApolloHandler manages to create a structured GraphQL error response, every exception (plus it stack trace) also generates a console log and a logger entry [ExceptionHandler], polluting the application log with thousands of already managed input errors.
Question
How to set Nest.js to supress those ApolloHandler exceptions? Of course non ApolloHandler exceptions should remain logged.
Create your own custom logger to filter out those messages like:
export class AppLogger extends Logger {
error(message: string, trace: string, context?: string) {
if (message !== 'Validations failed!') {
super.error(message, trace, context)
}
}
}
And use it as
app.useLogger(new AppLogger())
How to get the languageCode in the method getData()? Or just outside the getConfig() method? I read that languageCode is passed in the request parameter of the getData() method. I have other properties of the config. But languageCode not. This is the output of the requestparameter:
{dateRange={endDate=2018-12-17, startDate=2018-11-20}, fields=[{name=Field_1}, {name=Field2}]}
This is my getConfig method:
function getConfig(params) {
var cc = DataStudioApp.createCommunityConnector();
var config = cc.getConfig();
var lang = params['languageCode'];
config
.newInfo()
.setId('id_1')
.setText(i18n('SomeText'))
config.setDateRangeRequired(true);
return config.build();
}
And one more question. When I throw an exception in a message, along with the text of the exception, a string is also displayed on which I threw an exception. Is there any way to hide this information? And just display the text of the thrown exception? Without the string on which the exception was thrown?
And one more question. When I throw an exception in a message, along with the text of the exception, a string is also displayed on which I threw an exception. Is there any way to hide this information? And just display the text of the thrown exception? Without the string on which the exception was thrown?
The debug information is only shown when isAdminUser() returns True. To omit this information, set this to return false.
I have a service method which does some operation inside a transaction.
public User method1() {
// some code...
Vehicle.withTransaction { status ->
// some collection loop
// some other delete
vehicle.delete(failOnError:true)
}
if (checkSomething outside transaction) {
return throw some user defined exception
}
return user
}
If there is a runtime exception we dont have to catch that exception and the transaction will be rolled back automatically. But how to determine that transaction rolled back due to some exception and I want to throw some user friendly error message. delete() call also wont return anything.
If I add try/catch block inside the transaction by catching the Exception (super class) it is not getting into that exception block. But i was expecting it to go into that block and throw user friendly exception.
EDIT 1: Is it a good idea to add try/catch arround withTransaction
Any idea how to solver this?? Thanks in advance.
If I understand you question correctly, you want to know how to catch an exception, determine what the exception is, and return a message to the user. There are a few ways to do this. I will show you how I do it.
Before I get to the code there are a few things I might suggest. First, you don't need to explicitly declare the transaction in a service (I'm using v2.2.5). Services are transactional by default (not a big deal).
Second, the transaction will automatically roll back if any exception occurs while executing the service method.
Third, I would recommend removing failOnError:true from save() (I don't think it works on delete()... I may be wrong?). I find it is easier to run validate() or save() in the service then return the model instance to the controller where the objects errors can be used in a flash message.
The following is a sample of how I like to handle exceptions and saves using a service method and try/catch in the controller:
class FooService {
def saveFoo(Foo fooInstance) {
return fooInstance.save()
}
def anotherSaveFoo(Foo fooInstance) {
if(fooInstance.validate()){
fooInstance.save()
}else{
do something else or
throw new CustomException()
}
return fooInstance
}
}
class FooController {
def save = {
def newFoo = new Foo(params)
try{
returnedFoo = fooService.saveFoo(newFoo)
}catch(CustomException | Exception e){
flash.warning = [message(code: 'foo.validation.error.message',
args: [org.apache.commons.lang.exception.ExceptionUtils.getRootCauseMessage(e)],
default: "The foo changes did not pass validation.<br/>{0}")]
redirect('to where ever you need to go')
return
}
if(returnedFoo.hasErrors()){
def fooErrors = returnedFoo.errors.getAllErrors()
flash.warning = [message(code: 'foo.validation.error.message',
args: [fooErrors],
default: "The foo changes did not pass validation.<br/>${fooErrors}")]
redirect('to where ever you need to go')
return
}else {
flash.success = [message(code: 'foo.saved.successfully.message',
default: "The foo was saved successfully")]
redirect('to where ever you need to go')
}
}
}
Hope this helps, or gets some other input from more experienced Grails developers.
Here are a few other ways I've found to get exception info to pass along to your user:
request.exception.cause
request.exception.cause.message
response.status
A few links to other relevant questions that may help:
Exception handling in Grails controllers
Exception handling in Grails controllers with ExceptionMapper in Grails 2.2.4 best practice
https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/exception/ExceptionUtils.html
Using Groovy / Grails and log4j is there any way to ensure every exception thrown in the code is logged at error level.
Rather than having to find every catch block and explictly log it?
If not groovy / grails - a java suggestion will suffice.
Thanks
I don't believe there's any way to do this for handled exceptions, but you can do it for unhandled exceptions by adding the following to UrlMappings.groovy
"500"(controller: 'error')
Then create an ErrorController.groovy under grails-app/controllers
class ErrorController {
def index() {
Throwable exception = request?.exception?.cause
log.error 'something bad happened', exception
}
}
I have the following code:
class ServiceA {
def save(Object object) {
if (somethingBadComesBack) {
throw new CustomRuntimeException(data)
}
}
}
class ServiceB {
def serviceA
def save(Object object) {
try {
serviceA.save(object)
// do more stuff if good to go
} catch(CustomRuntimeException e) {
// populate some objects with errors based on exception
}
}
}
class ServiceC {
def serviceB
def process(Object object) {
serviceB.save(object)
if (object.hasErrors() {
// do some stuff
}else{
// do some stuff
}
def info = someMethod(object)
return info
}
}
class SomeController {
def serviceC
def process() {
def object = .....
serviceC.save(object) // UnexpectedRollbackException is thrown here
}
}
When ServiceA.save() is called and an exception occurs, ServiceC.save() is throwing an UnexpectedRollbackException when it tries to return.
I did the following:
try {
serviceC.process(object)
}catch(UnexpectedRollbackException e) {
println e.getMostSpecificCause()
}
and I am getting:
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
I'm not sure where to start looking for how to fix this.
You're using a runtime exception to roll back the transaction, but that's cheating - it's taking advantage of a side effect. Runtime exceptions roll back transactions automatically since you don't need to catch them so it's assumed that if one is thrown, it wasn't expected and the default behavior is to roll back. You can configure methods to not roll back for specific expected runtime exceptions, but this is somewhat rare. Checked exceptions don't roll back exceptions because in Java they must be caught or declared in the throws, so you have to have either explicitly thrown it or ducked it; either way you had a chance to try again.
The correct way to intentionally roll back a transaction is to call setRollbackOnly() on the current TransactionStatus but this isn't directly accessible in a service method (it is in a withTransaction block since it's the argument to the closure). But it's easy to get to: import org.springframework.transaction.interceptor.TransactionAspectSupport and call TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(). This will required that you rework your code since there won't be an exception to catch, so you'll need to check that it was rolled back with TransactionAspectSupport.currentTransactionStatus().isRollbackOnly().
I'm not sure if it's a Grails issue or standard behavior, but when I was debugging this there were 3 commit calls with 3 different TransactionStatus instances. Only the first had the rollback flag set, but the second was aware of the first and was ok. The third one was considered a new transaction and was the one that triggered the same exception that you were seeing. So to work around this I added this to the 2nd and 3rd service methods:
def status = TransactionAspectSupport.currentTransactionStatus()
if (!status.isRollbackOnly()) status.setRollbackOnly()
to chain the rollback flag. That worked and I didn't get the UnexpectedRollbackException.
It might be easier to combine this with a checked exception. It's still overly expensive since it will fill in the stacktrace unnecessarily, but if you call setRollbackOnly() and throw a checked exception, you will be able to use the same general workflow you have now.
It seems like the default transactionality of services is biting you, and the unchecked exception thrown in Service A is dooming the transaction to rollback only, even once caught.
The above docs say the txn propagation level is PROPAGATION_REQUIRED, which should mean the same transaction is shared from your service C on up to A, if my memory serves me. Can you have Service A's save method throw a checked exception instead of RuntimeException, to avoid the auto-rollback from the latter? Or disable transactions on your services, if that's an option for you?