Reputation: 172
I'm developing a public API for people to use in their applications. I'm currently trying to figure out the best way to handle exceptions. For example, the following code snippet throws four different kinds of exceptions:
Signature s = Signature.getInstance("SHA1withRSA"); //NoSuchAlgorithmException
s.initSign(keyChain.getPrivateKey()); //InvalidKeyException
s.update(plainText.getBytes("UTF-8")); //UnsupportedEncodingException
byte[] signature = s.sign(); //SignatureException
String signedData = base64Encoder.encode(signature);
My question is, how do I handle these in the best possible way?
One way I came up with was catching exceptions and throwing a custom exception:
public void signRequest(Request request) throws APISignatureException {
try {
...
Signature s = Signature.getInstance("SHA1withRSA"); //NoSuchAlgorithmException
s.initSign(keyChain.getPrivateKey()); //InvalidKeyException
s.update(plainText.getBytes("UTF-8")); //UnsupportedEncodingException
byte[] signature = s.sign(); //SignatureException
String signedData = base64Encoder.encode(signature);
...
}
catch (Exception e) {
throw new APISignatureException("Failed to create signature", e);
}
}
Is this a good way of handling exceptions for an open API?
Upvotes: 4
Views: 797
Reputation: 6816
You need to understand this please look into it.
Basic understanding is
try {
//Something that can throw an exception.
} catch (Exception e) {
// To do whatever when the exception is caught.
}
There is also an finally block which will always be execute even if there is an error. it is used like this
try {
//Something that can throw an exception.
} catch (Exception e) {
// To do whatever when the exception is caught & the returned.
} finally {
// This will always execute if there is an exception or no exception.
}
In a particular case of scanner you can have the following exceptions (link).
InputMismatchException - if the next token does not match the Integer regular expression, or is out of range
NoSuchElementException - if input is exhausted
IllegalStateException - if this scanner is closed
So you would need to catch exceptions like
try {
rows=scan.nextInt();
} catch (InputMismatchException e) {
// When the InputMismatchException is caught.
System.out.println("The next token does not match the Integer regular expression, or is out of range");
} catch (NoSuchElementException e) {
// When the NoSuchElementException is caught.
System.out.println("Input is exhausted");
} catch (IllegalStateException e) {
// When the IllegalStateException is caught.
System.out.println("Scanner is close");
}
Upvotes: 1
Reputation: 1434
When I write an API, I catch lower level exceptions and throw a related API exception. In that way you are not hiding anything and you allow API users to catch one related exception.
If the API is large, you may want several API exceptions that are all subclasses of a common API exception. For example:
public class APIException { ...
}
public class APISignatureException extends APIException { ...
}
public class APISomeException extends APIException { ...
}
Upvotes: 1
Reputation: 23329
If you using Java 7 then you can catch multiple exceptions in the same catch clause,
catch (APISignatureException|SignatureException e) {
throw e;
}
Otherwise you need to catch them separately
catch (APISignatureException e) {
throw e;
}
catch (SignatureException e) {
throw e;
}
You shouldn't use the super type which is Exception
so you don't end up catching exceptions that you don't want to handle
Upvotes: 1
Reputation: 1518
Right now all of your exceptions are caught by catch (Exception e) { and they then all throw a APISignatureException.
What you really want to do is catch your specific exception, like this:
public void signRequest(Request request) throws APISignatureException {
try {
...
Signature s = Signature.getInstance("SHA1withRSA"); //NoSuchAlgorithmException
s.initSign(keyChain.getPrivateKey()); //InvalidKeyException
s.update(plainText.getBytes("UTF-8")); //UnsupportedEncodingException
byte[] signature = s.sign(); //SignatureException
String signedData = base64Encoder.encode(signature);
...
}
catch (APISignatureException e) {
//Handle exception
}
catch (InvalidKeyException e) {
//Handle exception
}
Also, if you catch specific exceptions there's no need to throw another exception.
You can also combine catching of multiple exceptions like this:
catch (APISignatureException|InvalidKeyException e) {
// Handle exception
}
You can create custom exceptions by extending the Exception class:
class APISignatureException extends Exception {
// empty constructor
public APISignatureException () {}
//constructor that takes a string message
public APISignatureException (String message)
{
super(message);
}
}
For more details take a look at Exception
Upvotes: 1
Reputation: 4737
Catching general Exceptions is usually a bad idea.
You can do this instead (Java 7):
public void signRequest(Request request) throws APISignatureException {
try {
Signature s = Signature.getInstance("SHA1withRSA"); //NoSuchAlgorithmException
s.initSign(keyChain.getPrivateKey()); //InvalidKeyException
s.update(plainText.getBytes("UTF-8")); //UnsupportedEncodingException
byte[] signature = s.sign(); //SignatureException
String signedData = base64Encoder.encode(signature);
}
catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException | SignatureException e) {
throw new APISignatureException("Failed to create signature", e);
}
}
But ask yourself what the client is going to do with this as you're forcing him to still catch your Exception.
If the client shouldn't be concerned because in general this won't go wrong, you can throw an unchecked Exception:
public void signRequest(Request request) {
try {
Signature s = Signature.getInstance("SHA1withRSA"); //NoSuchAlgorithmException
s.initSign(keyChain.getPrivateKey()); //InvalidKeyException
s.update(plainText.getBytes("UTF-8")); //UnsupportedEncodingException
byte[] signature = s.sign(); //SignatureException
String signedData = base64Encoder.encode(signature);
}
catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException | SignatureException e) {
throw new RuntimeException("Failed to create signature", e); // This doesn't need to be explicitly caught
}
}
What I mean is, if the signing goes wrong, there's probably no need for the user to continue his application as if nothing happened by catching your own Exception. He need to change some configuration and run his application again. The RuntimeException
will just propagate until a more general catcher is catching it.
Upvotes: 2
Reputation: 20520
It's nearly a reasonable way! I'd say that if you replaced your catch
with a list of specific exceptions, rather than a blanket Exception
, it would be completely defensible.
It would also be quite reasonable to let all four types propagate upwards, though. It depends on what you want. If the user wants to have immediate access to the reason for failure, you might leave the types unaltered and uncaught. If you want this layer of abstraction, where the user just gets a "something went wrong with the signature" type, but can still drill down into the details, then the structure you've got is ideal.
The point is that you're not hiding anything: the original exception is still buried in the new one, and available to the caller.
Upvotes: 3
Reputation: 949
This is my personal opinion
I would say the more exceptions you have towards the user, the more detailed errors he can develop towards. And since this is an open API meaning everybody can use it, I would go for granularity and provide with errors that can help the user in figuring out what is wrong.
If you provide one specific error on each exception the user may also provide feedback to you as a developer. He is trying your api and gets an error, he will provide you git that one exception you are providing him and then you have to figure out what is wrong.
Upvotes: 1
Reputation: 20446
Handling exception depends on what you want to do. Most of the time you can do nothing in current method and only able to communicate error to caller method, then you wrap and rethrow (or even declare exception in you own signature). But some time you can handle it. For example if you fail to find and open property file you can use default values.
Upvotes: 1