handling a specific exception type - aspectj

I've defined two AfterThrowing advices to handle exceptions with the same pointcut.
#AfterThrowing(pointcut="...", throwing="ex")
public void method1(Exception ex) {}
#AfterThrowing(pointcut="...", throwing="ex")
public void method2(GatewayException ex) {}
Is there a way for me to prevent the generic method1 being executed if the exception is a GatewayException?
Any ideas greatly appreciated
C

It would be the easiest to check the instance of the exception inside the advice body and return early if it's of the more specific exception type:
#AfterThrowing(pointcut="...", throwing="ex")
public void method1(Exception ex) {
if (ex instanceof GatewayException) {
return;
}
// handle the more generic Exception case
}
#AfterThrowing(pointcut="...", throwing="ex")
public void method2(GatewayException ex) {
// handle the more specific GatewayException
}
I know you expected a solution based on some AspectJ language construct, but the thing is, there's no such construct.

Related

Capture exception thrown by Hystrix fallback?

I'm designing a service facade and I have a method signature that looks like this:
public Policy getPolicy(long policyId) throws PolicyNotFoundException
If nothing bad happens then a Policy object (simple POJO) is returned. If the requested policy is not found then a checked exception PolicyNotFoundException is thrown (just as a reference - we follow this article when it comes to best practices on exception handling within an application).
The layer above the service facade layer (in this case a Spring MVC RestController) knows how to handle such a PolicyNotFoundException and return an appropriate payload.
I'm trying to incorporate this into a HystrixCommand by doing something like this:
#HystrixCommand(groupKey = "PolicyService", fallbackMethod = "getPolicySafe", ignoreExceptions = { PolicyNotFoundException.class })
public Policy getPolicy(long policyId) throws PolicyNotFoundException {
LOGGER.info("Getting policy {}", policyId);
// Simulate some error condition for testing purposes
throw new RuntimeException("Something happened!");
}
private Policy getPolicySafe(long policyId, Throwable t) throws PolicyNotFoundException {
LOGGER.warn("Falling back to circuit-breaker for getting policy {}", policyId, t);
throw new PolicyNotFoundException(policyId);
}
Basically I want my circuit breaker to simply behave as if the policy wasn't found by the original lookup. The problem I'm having with this though is the exception I throw from the fallback method is getting lost in translation somewhere. The exception I end up seeing in the layer above is the RuntimeException thrown by the command method and not the exception thrown by the fallback method. Is there a way around this? I don't want to change the contract of my original method either nor do I want the layer above this to know anything other than to have to catch PolicyNotFoundException in the case a policy isn't found. Whatever is needed here should be captured within this service facade layer.
Any and all help would be greatly appreciated. Thanks!
So based on the link #spencergibb gave - I may have found a solution after upgrading to Hystrix 1.5.7. This code works as expected
PolicyRestController.java
#RestController
#RequestMapping("/policies")
public class PoliciesApi {
private static final Logger LOGGER = LoggerFactory.getLogger(PoliciesApi.class);
#Autowired
private PolicyService policyService;
#RequestMapping(value = "/{policyId}", method = RequestMethod.GET, produces = { MediaTypes.POLICY_JSON_VALUE, MediaTypes.POLICY_XML_VALUE })
public Policy getPolicy(#PathVariable long policyId) {
try {
// This just shown for simplicity. There is more to this method (input validation/etc)
return this.policyService.getPolicy(policyId);
}
catch (PolicyNotFoundException ex) {
// NotFoundException is a RuntimeException annotated with #ResponseStatus(HttpStatus.NOT_FOUND)
// So the service returns a 404 to the client
LOGGER.info("Policy {} wasn't found", ex.getPolicyId(), ex);
throw new NotFoundException(String.format("Policy %s was not found", ex.getPolicyId()));
}
}
}
PolicyService.java
public interface PolicyService {
#Cacheable("allPolicies")
public List<Policy> getPolicies();
#Cacheable("policies")
public Policy getPolicy(long policyId) throws PolicyNotFoundException;
}
PolicyServiceImpl.java:
#Service
public class PolicyServiceImpl implements PolicyService {
#HystrixCommand(groupKey = "PolicyService", fallbackMethod = "getPolicySafe", ignoreExceptions = { PolicyNotFoundException.class })
public Policy getPolicy(long policyId) throws PolicyNotFoundException {
LOGGER.info("Getting policy {}", policyId);
// Simulate some error condition for testing purposes
throw new RuntimeException("Something happened!");
}
#HystrixCommand(groupKey = "PolicyService", ignoreExceptions = { PolicyNotFoundException.class }, raiseHystrixExceptions = { HystrixException.RUNTIME_EXCEPTION })
private Policy getPolicySafe(long policyId) throws PolicyNotFoundException {
// Here is we hit our fallback we want to log a warning & simply act as if the policy wasn't found by throwing the same contingency exception as the API does
LOGGER.warn("Falling back to circuit-breaker for getting policy {}", policyId);
throw new PolicyNotFoundException(policyId);
}
}
While your solution might work for you I've noticed some weirdness in your code (I can't check my assumptions so I would like to ask you to check this).
Try to avoid using checked exceptions in your code because it's
awkward to maintain.
Based on your code you will never catch
"PolicyNotFoundException" since you're using raiseHystrixExceptions = { HystrixException.RUNTIME_EXCEPTION } which means that you won't to get your custom exception so that HystrixRuntimeException will be propagated. Try to rewrite your code as follows so it should
simplify the code and maybe fix some of your problems:
#Service
public class PolicyServiceImpl implements PolicyService {
#HystrixCommand(groupKey = "PolicyService", fallbackMethod = "getPolicySafe")
public Policy getPolicy(long policyId) throws PolicyNotFoundException {
LOGGER.info("Getting policy {}", policyId);
throw new PolicyNotFoundException(); // throw real PolicyNotFoundException if policy is absent for the given id
}
#HystrixCommand(groupKey = "PolicyService")
private Policy getPolicySafe(long policyId) throws PolicyNotFoundException {
// Here is we hit our fallback we want to log a warning & simply act as if the policy wasn't found by throwing the same contingency exception as the API does
LOGGER.warn("Falling back to circuit-breaker for getting policy {}", policyId);
throw new PolicyNotFoundException(policyId);
}
}
This is the default behavior of hystrix. "If command has a fallback then only first exception that trigers fallback logic will be propagated to caller"
See the error propagation section here.
I do this:
#Component
public class HystrixClient {
#HystrixCommand(ignoreExceptions = {ClientArgumentException.class})
public POJO getPojo(String id)
throws ClientNoDataFoundException, ClientArgumentException, ClientGeneralException {
//call my service and return POJO
}
}
#Component
public TrueClientUsedForAnotherSerivce {
#Autowired
HystrixClient hystrixClient;
public POJO getPojo(String id)
throws ClientNoDataFoundException, ClientArgumentException, ClientGeneralException, ClientOpenCircuitException {
try {
POJO result = hystrixClient.getCellular(id);
return result;
}
catch(HystrixRuntimeException e) {
LOG.debug("The circuit is open");
throw new ClientOpenCircuitException("Open circuit");
}
}
It only works if #HystrixCommand method is in another class.

How can i catch exceptions in a method that throws them(JAVA)?

I have a class with a method that throws some exceptions and catches them inside itself, but when i call it in my Main class they seem to not being catched.
An example about my problem:
public class Test {
public static void method (int number) throws InvalidNumberException {
try {
if (number == 5) {
throw new InvalidNumberException("Invalid number");
}
} catch (InvalidNumberException inv) {
System.out.println(inv);
}
}
}
public class InvalidNumberException extends Exception {
public InvalidNumberException (String s) {
super(s);
}
}
public class Main {
public static void main(String args[]) {
Test.method(5);
}
}
When i try to compilate the last one i get this error:
Main.java:3: error: unreported exception InvalidNumberException; must be caught or declared to be thrown
Test.method(5);
Is there a way to fix it without catching the exception in the Main class?
Because you're catching the InvalidNumberException inside of method, there's no need for a throws clause, however, the existence of it mandates that calls to it must handle the exception. Thus, the compiler is expecting you to handle the exception in main.
To solve this, simply remove the throws clause modifying method, since you're already handling the exception inside.

AspectJ applying Around advice on methods that return void

given a block of Advice like below:
#Around("execution(* com.myproject..*(..))")
public Object log(ProceedingJoinPoint pjp) throws Throwable{
....
Object result = pjp.proceed();
......
return result;
}
I just want to know if I have a method that returns void, will this Advice get applied, and will that result in some kind of error?
Example:
package com.myproject.mypackage;
public Class MyClass {
public void run() {
// Will this method run properly as a result of 'pjp.proceed()' above?
}
}
Tried experimenting by running a few stub methods myself, I found that the Advice will get applied, and there will be no error other than those generated by the joinpoint itself.

class member returns null after osgi bind method

My problem is that in the main class I have some osgi references that work just fine when the class is call. But after that all the references became null. When I close the main windows and call shutdown method, the hubService reference returns null. What do I do wrong here?
private void shutdown() {
if(hubService == null) {
throw new NullPointerException();
}
hubService.shutdownHub(); // why is hubService null?
}
// bind hub service
public synchronized void setHubService(IHubService service) {
hubService = service;
try {
hubService.startHub(PORT, authenticationHandler);
} catch (Exception e) {
JOptionPane.showMessageDialog(mainFrame, e.toString(), "Server", JOptionPane.ERROR_MESSAGE);
System.exit(0);
}
}
// remove hub service
public synchronized void unsetHubService(IHubService service) {
hubService.shutdownHub();
hubService = null;
}
If a field can be read and written by multiple threads, you must protect access to read as well as write. Your first method, shutdown, does not protect the read of hubService so that the value of hubService can change between the first read and the second read. You don't show the declaration of the hubService field. You could make it volatile or only read when synchronized (on the same object used to synchronized when writing the field). Then your shutdown implementation could look like:
private volatile IHubService hubService;
private void shutdown() {
IHubService service = hubService; // make a copy of the field in a local variable
if (service != null) // use local var from now on since the field could have changed
service.shutdownHub();
}
I assume your shutdown method is the DS deactivate method? If so, why do you shutdown in the unset method as well in the shutdown method?
Overall the design does not seem very sound. The IHubService is used as a factory and should return some object that is then closed in the deactivate method. You made the IHubService effectively a singleton. Since it must come from another bundle, it should handle its life cycle itself.
Since you also do not use annotations, it is not clear if your set/unset methods are static/dynamic and/or single/multiple. The following code should not have your problems (exammple code with bnd annotations):
#Component public class MyImpl {
IHubService hub;
#Activate
void activate() {
hubService.startHub(PORT, authenticationHandler);
}
#DeActivate
void deactivate() {
hubService.shutdown();
}
#Reference
void setHub(IHubService hub) { this.hub = hub; }
}

Entity Framework - DbUpdateException to a custom Exception

I am using Entity Framework and when a DbUpdateException is thrown from dbContext.SaveChanges() how do I create a custom exception and throw that instead?
Would I create a condition on each SQL constraint that can be thrown:
if (e.InnerException.InnerException.Message.Contains("UNIQUE KEY"))
{
throw new CustomException("message");
}
EDIT: That approach makes good sense to me. If you know your application/DB is going to have a specific error, and it will help you or your users to have a specific custom exception type that quickly identifies what would otherwise be a somewhat complex or specific scenario, then absolutely yes. It's good practice to use both the exception type and the exception message to make the error as clear as possible. My code below is an even simpler example than what you seem to drilling down into. Rather than letting other code end up with a null reference exception or some other consequence, I beat everything to the punch with throw new DatabaseDataNotFoundException("Cannot find ServerAppConfiguration value for {0}", key);.
Just make your own exception class that inherits from Exception, here's a custom exception I use for exactly that purpose:
public class DatabaseDataNotFoundException : Exception
{
public DatabaseDataNotFoundException() : base() { }
public DatabaseDataNotFoundException(string message) : base(message) { }
public DatabaseDataNotFoundException(string message, params object[] args)
: base(String.Format(CultureInfo.CurrentCulture, message, args)) { }
public DatabaseDataNotFoundException(string message, Exception inner)
: base(message, inner) { }
public DatabaseDataNotFoundException(string message, Exception inner, params object[] args)
: base(String.Format(CultureInfo.CurrentCulture, message, args), inner) { }
protected DatabaseDataNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
Then your code becomes:
if (e.InnerException.InnerException.Message.Contains("UNIQUE KEY"))
{
throw new DatabaseDataNotFoundException("message");
}