There's a piece of code that throws a exception:
java.lang.RuntimeException: cn.dev33.satoken.exception.NotLoginException: Invalid Token:ldxutBDDKBEDa9LjWNTKLFbW7g7B86qU.
And then it goes into handleRuntimeException rather than returnNotLoginException method.
#Component
#Slf4j
#Primary
public class MyLockKeyBuilder extends DefaultLockKeyBuilder {
#Override
public String buildKey(MethodInvocation invocation, String[] definitionKeys) {
String key = super.buildKey(invocation, definitionKeys);
Object loginId = StpUtil.getLoginId(); // throw a exception
key = loginId.toString();
return key;
}
}
#ControllerAdvice(basePackages = "com.test")
#Slf4j
public class GraceExceptionHandlerApp {
#ResponseStatus(HttpStatus.UNAUTHORIZED)
#ExceptionHandler(value = NotLoginException.class)
#ResponseBody
public JSONObject returnNotLoginException(NotLoginException e) {
e.printStackTrace();
String message = e.getMessage();
ResponseStatusEnum failed = ResponseStatusEnum.UNAUTHORIZED;
failed.setMsg(message);
return ZheliResult.exception(failed);
}
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(RuntimeException.class)
#ResponseBody
public JSONObject handleRuntimeException(RuntimeException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
return ZheliResult.errorCustom(ResponseStatusEnum.FAILED);
}
...
}
I want it goes into the returnNotLoginException method, could anyone tell me how to do it?
UPDATE
I've made a mistake, really, for I didn't offer enough info.
Missed Info:
My application was a distributed system and services to invoke another via rpc communication. MyLockKeyBuilder was on the provider service, and GraceExceptionHandlerApp was on the comsumer service.
When the provider service throw a exception and before it being passed to the comsumer sevice, it would be filter by a Filter called ExceptionFilter, which wrap the exception that the comsumer side doesn't recognize to RuntimeException, to avoid serialization issue.
Finally I solved this problem by rewritting the ExceptionFilter class to allow original NotLoginException to be passed to the consumer side.
NotLoginException is the inner exception of your RuntimeException. If you want your controller advice to handle it, catch the RuntimeException buildKey and throw its inner exception.
Based on your question,
java.lang.RuntimeException: cn.dev33.satoken.exception.NotLoginException: Invalid Token:
Your exception type is java.lang.RuntimeException & cause of exception is NotLoginException.
Controller advice will invoke respective method when type of exception matches & not cause of exception.
So if you really want to invoke returnNotLoginException, then you need to throw NotLoginException in your logic instead of throwing RuntimeException.
Something like:
..
throw new NotLoginException("exception"); //in your StpUtil.getLoginId();
..
Related
I have a method Service method - SampleServiceImpl().
I have declared the Service method as following:
#Transactional
#Override
public sampleDTO SampleServiceImpl(SampleDTO sampleDTO) throws SampleException, ParseException {
// Method call to methodA.
createDataA(sampleDTO);
// Method call to methodB.
createDataB(sampleDTO);
return sampleDTO ;
}
Here the DataA is created and it does not throw any exception.
But in DataB, we are trying to create DataB based on DataA. Due to some logic, we cannot create DataB. So, we throw a Sample Exception like:
count = checkIfDataBExisting(sampleDTO);
if(count == 1){
throw new SampleException(ErrorConstants.DATA_B_EXISTING);
}
But the problem is, the transaction that got committed during createDataA(sampleDTO) method call, does not get rolled Back.
Why does this don't actually work? I'm bit confused with this behavior.
EDIT:1
createData1(sampleDTO) method -
private ADTO createDataA(SampleDTO sampleDTO) throws SampleException{
ADTO aDTO = null;
try {
//CREATE NEW WORK DRIVER
aDTO = createNewDataA(sampleDTO);
//Other arbitary database transactions occurs after creation of AData.
} catch (SampleException exc) {
SampleException newException = new SampleException (exc.getExceptionObject().getExceptionCode(), exc);
throw newException ;
}
return aDTO;
}
EDIT2:
SampleException Declaration -
public class SampleException extends Exception{
//Definitions and Declarations.
}
By default declarative transactions rollback only for Runtime exceptions.
SampleException has to be RuntimeException for the Transaction to be rolled back.
Your method signature:
public sampleDTO SampleServiceImpl(SampleDTO sampleDTO) throws SampleException, ParseException
makes me think that SampleException is a checked one.
See the documentation for #Transactional:
If no rules are relevant to the exception, it will be treated like DefaultTransactionAttribute (rolling back on RuntimeException and Error but not on checked exceptions).
You can either make SampleException extends RuntimeException or set the rollbackFor attribute of #Transactional.
I have a problem with my ExceptionMapper:
#Provider
#Component
public class GenericExceptionMapper implements ExceptionMapper<Throwable> {
public GenericExceptionMapper() {
logger.debug("hej hopp");
}
#Override
public Response toResponse(Throwable exception) {
ErrorMessage errorMessage = new ErrorMessage("Technical error",
"An unknown technical error occured.");
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(errorMessage).build();
}
}
It doesn't catch any exceptions from my resources. I have placed a debug point in the constructor and see that it is created. I have debuged the request and it doesn't seem to look for any exception handlers.
This is part of my configuration:
#ComponentScan("com.companywhereiwork.application")
And another part:
jerseyServlet.setInitParameter("jersey.config.server.provider.packages",
"com.companywhereiwork.application");
When I throw an exception from inside one of my resources:
if (Math.random() < 1f) {
throw new RuntimeException("Blä");
}
It is returned to the client as a tomcat errorpage. It doesn't enter my errorhandler.
The solution was to remove the #Component annotation. For some reason, the ExceptionMapperFactory didn't pick up my class when it had double annotations like this. I didn't think it would be a problem, but it was. It now works (and I know a little more about the ExceptionMapperFactory).
In my application I have different layers like the rest layer, service layer and DB layer, according to business scenarios I am trowing different business exceptions from the service layer.
But now, I have to set different HTTP codes like 400, 403, 409, 412.. to REST responses.
How can I set different HTTP status codes based on different scenarios?
Which is the most feasible way like: aspect, exception mapper, or ....?
Since I can set HTTP status only once in rest layer (
referred this ), I am not able to map to different HTTP codes because my exception is from service layer.
My exception class looks like this:
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = 1L;
public BusinessException(ErrorEnumeration error) {
}
public BusinessException(Exception e, ErrorEnumeration error) {
}
}
and the exception will be thrown from the service like this:
throw new BusinessException(ErrorEnumeration.VALIDATION_FAILED);
Please help by suggesting a solution
You can use exceptions defined in jax-rs or you can use your own exceptions. Fist catch your business exceptions and convert them to jax-rs versions. For example, for 404 you can throw javax.ws.rs.NotFoundException.
You can also write your own exceptions by extending them from javax.ws.rs.ClientErrorException
Here is an example for 409-Conflict status exception
import javax.ws.rs.ClientErrorException;
import javax.ws.rs.core.Response;
public class ConflictException extends ClientErrorException{
public ConflictException(Response.Status status) {
super(Response.Status.CONFLICT); // 409
}
}
Update
Most simple and feasible way is catching your business exceptions and re-throw them with jax-rs exceptions.
try{
businessService.executeBusinessRule();
}catch (BusinessException e){
// It is better if your BusinessException has some child class to handle
if(e.getError() == ErrorEnumeration.VALIDATION_FAILED){
throw new BadRequestException();
}else{
throw new ConflictException();
}
}
If you are using spring you can always catch these exceptions using aop.
#Aspect
public class BusinessExceptionInterceptor{
#AfterThrowing(pointcut = "execution(* com.your.service.packge..* (..))", throwing = "e")
public void errorInterceptor(BusinessException e) {
// re-throw again...
}
Update 2
Also it is better to define a new exception instead of reusing same exception with different state. You can define a new ValidationException which extends from BusinessException like this.
public class ValidationException extends BusinessException{
public ValidationException() {
super(ErrorEnumeration.VALIDATION_FAILED);
}
}
By using this way you can still handle all the BusinessException but it is easier to identify or map them to Jax-rs exceptions.
Our application uses several back-end services and we maintain wrappers which contain the methods to make the actual service calls. If any exception occurs in any of those methods while invoking a service, we throw a custom exception encapsulating the original exception as shown below.
interface IServiceA {
public void submit(String user, String attributes);
}
public class ServiceAWrapper implements IserviceA {
private ActualService getActualService() {
.....
}
public void submit(String user, String attributes) {
try {
Request request = new Request();
request.setUser(user);
request.setAttributes(attributes);
getActualService().call(request);
} catch(ServiceException1 e) {
throw new MyException(e, reason1);
} catch(ServiceException2 e) {
throw new MyException(e, reason2);
}
}
}
I would like to know if there's any framework that would allow me to
capture (and probably log) all the
parameters passed to my wrapper
methods at run-time; if the methods
are called.
capture the actual exception
object(MyException instance in above
example), if any thrown; so that I
could append the passed parameters
to the object at run-time.
I am currently exploring AspectJ to see if it can address my requirements, but I am not sure if it can be used to capture the parameters passed to methods at runtime and also to capture exception objects, if any occur.
Thanks.
With AspectJ, you can use around advice to execute advice instead of the code at the join point. You can then execute the actual join-point from within the advice by calling proceed. This would allow you to capture the input parameters, log them, and proceed to call the actual method.
Within the same advice you could capture any logs throw from the method, and inspect or log them before passing it back up to higher levels.
Matt B's answer is right. Specifically, you can do something like this:
aspect MonitorServiceCalls {
private final Logger LOG = LoggerFactory.getLog("ServiceCallLog");
Object around() throws MyException: call(public * *(..) throws MyException)
&& target(IServiceA+) {
MethodSignature msig = (MethodSignature)thisJoinPoint;
String fullMethName = msig.getMethod().toString();
try {
Object result = proceed();
LOG.info("Successful call to {} with arguments {}",
fullMethName,
thisJoinPoint.getArgs());
return result;
} catch(MyException e) {
LOG.warn("MyException thrown from {}: {}", msig.getMethod(), e);
throw e;
}
}
}
AspectJ is the right option. You will be able to get hold of the parameters by way of a JoinPoint object that will be passed to your advise methods. You can also get hold of the exception either by implementing an after throwing advise or an around advise.
I use Spring WebMVC to provide a REST API. I use methods like
#RequestMapping("/path({id}") void getById(#PathVariable("id") int id) {} methods.
When the client incorrectly put a string instead of an integer id into the query, I get a NumberFormatException like:
java.lang.NumberFormatException: For input string: "dojo"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
at java.lang.Long.parseLong(Long.java:410)
at java.lang.Long.valueOf(Long.java:525)
at org.springframework.util.NumberUtils.parseNumber(NumberUtils.java:158)
at org.springframework.core.convert.support.StringToNumberConverterFactory$StringToNumber.convert(StringToNumberConverterFactory.java:59)
at org.springframework.core.convert.support.StringToNumberConverterFactory$StringToNumber.convert(StringToNumberConverterFactory.java:1)
at org.springframework.core.convert.support.GenericConversionService$ConverterFactoryAdapter.convert(GenericConversionService.java:420)
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:37)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:135)
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:199)
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:104)
at org.springframework.beans.SimpleTypeConverter.convertIfNecessary(SimpleTypeConverter.java:47)
at org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:526)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolvePathVariable(HandlerMethodInvoker.java:602)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.java:289)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:163)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:414)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:402)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:771)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:716)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:647)
My Question is now, how can I elegantly catch it? I know that Spring provides #ExeptionHandler annotations but I don't want to catch the NFE in general. I want to be able to catch all parsing exception in order to present a nice error message to the client.
Any ideas?
Cheers,
Jan
Is that the actual exception? (it doesn't match your code example) Normally one would expect that to be wrapped in org.springframework.beans.TypeMismatchException which is probably specific enough that you could write an #ExceptionHandler method for it.
If that's not specific enough, you will need to forgo the Spring-Magic and just change the parameter type to String + parse it yourself. Then you can handle it any way you like.
I have found solution for your problem here http://www.coderanch.com/t/625951/Spring/REST-request-mapping-parameter-type
Just try
#RequestMapping("/path({id:[\\d]+}") void getById(#PathVariable("id") int id) {} methods.
And then not valid usage will cause 404. I'm not sure if version 3.0 supports this.
I am not 100% sure about whether this works for #PathVaribale or not, but generally for model binding you could use a BindingResult object next to your path variable and model and parsing error will be added to the BindingResult/Errors object.
Perhaps I do this because I am an old tyme programmer, but I use String as the type for all #PathVariable and #RequestParameter parameters then I do the parsing inside the handler method. This allows me to easily catch all NumberFormatException exceptions.
Although this is not the "Spring" way of doing this, I recommend it because it is easy for me and easy for my future offshore maintenance programmers to understand.
putting your comments together, I tried the following:
public class ValidatingAnnotationMethodHandlerAdapter extends AnnotationMethodHandlerAdapter {
#Override
protected ServletRequestDataBinder createBinder(HttpServletRequest request, Object target, String objectName) throws Exception {
return new ServletRequestDataBinder(target, objectName) {
#Override
public <T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException {
try {
return super.convertIfNecessary(value, requiredType);
} catch (RuntimeException e) {
throw new ControllerException("Could not parse parameter: " + e.getMessage());
}
}
#Override
public <T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam) throws TypeMismatchException {
try {
return super.convertIfNecessary(value, requiredType, methodParam);
} catch (RuntimeException e) {
throw new ControllerException("Could not parse parameter: " + e.getMessage());
}
}
};
}
ControllerException is a custom exception which is catched by an #ExceptionController annotated method (I use this exception in all validator classes).
Hope you like it,
Jan