I have a simple try catch method that returns an exception as String
public class Program {
try {
someFunction()
} catch (Exception e){
// i need to get the code and message as strings
}
}
these are some examples of the exceptions I can get:
You have got the error. [ErrorCode: 400 XYZ]
You have got the error. [ErrorCode: 404 XYZ]
You got error which has no errorCode
and these are the error codes I want for each of these exceptions:
INVALID_TEMPLATE
REQUEST_REJECTED
NO_ERROR_CODE
I tried a few things and this is what I came up with
public class Program {
try {
someFunction(x);
} catch (Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
String message = sw.toString();
Matcher m = Pattern.compile(".*:\\s+(.*)\\s+\\[ErrorCode: (\\d+)").matcher(message);
if (m.find()) {
String errorMessage = m.group(1); // You have got the error.
String errorCode = m.group(2); // 400
// Here I want to fetch the error Category based on the errorCode
String errorCategory = XYZ.errorCategory(errorCode);
`//Based on`
the errorCode, errorCategory of INVALID_TEMPLATE which is an enum must be returned
SomeOtherFunc(errorCategory, errorMessage);
}
}
public class XYZ {
private static final Map < String, String > errorMap = new HashMap < > ();
public void populateErrorMap() {
errorMap.put("400", INVALID_TEMPLATE(400, 400));
errorMap.put("404", REQUEST_REJECTED(404, 404));
}
}
public static String errorCategory(String errorCode) {
return errorMap.get(errorCode);
}
Is this optimal or is there a better solution?
Also this is still unable to handle the exception in case there is no error code (should return NO_ERROR_CODE).
How can I archive that?
In real life there are two audiences:
the log handler with a configurable log level (ERROR) and logging (in English) with much info;
the end user with an localized translated message, also with parameters.
The first property is that you probably want a message as format String with Object... parameters. Probably should use MessageFormat.
Sensible would be to support typed parameters.
/** Type-checkable Message Definition. */
public record MessageDef(String format, Class<?>... parameterTypes) {
public void checkFormat() {
... check actual parameters with parameterTypes.length
}
public void checkUsage(Object[] args) {
... check parameter types
}
}
One could make an enum for the error categories. However enums are more suitable for closed domains with a fixed set of values. Extending values in future means that you have created a needless common bottleneck for source version control and so on.
An error is more like an open domain. However if you number them with error codes, an enum gives a nice overview.
The only advantage of error codes is the internationalisation. An Hungarian error message can be easily retrieved.
Then, if you rethrow check exceptions as RuntimeException, like IllegalArgumentException or you own custom ones, you might not want parallel classes: run-time exceptions and categories.
All-in-all I would advise an enum:
public enum MessageType {
INVALID_TEMPLATE(400, Level.ERROR,
new MessageDef("You have got the error in {0}.", String.class)),
...
REQUEST_REJECTED(200, Level.INFO,
new MessageDef("Done."));
public final int code;
public final Level level;
public final MessageDef def;
MessageType(int code, Level level, MessageDef def) {
this.code = code;
this.level = level;
this.def = def;
}
}
One small remark: such little discussion points in the beginning of a project sometimes might be better postponed to a fast refactoring after having written sufficient code. Here an enum might not fit, you might have much re-throwing of exceptions. A premature decision is not needed. And might hamper fast productivity.
Especially as you probably need not mark the code places, you most likely call the same show-error dialog.
I am not sure if I understood your issue properly
If you want to transform your exception code to a custom category this looks shorter:
public class Program {
Map<Integer, String> errorMap = new HashMap<Integer, String>();
errorMap.put(400, "INVALID_TEMPLATE");
errorMap.put(404, "REQUEST_REJECTED");
try {
someFunction (x) ;
} catch (Exception e ) {
SomeOtherFunction(errorMap.get(e.getStatusCode()),errorMessage);
}
}
Otherwise, you could add multiple catches for each exception type if you want to call different functions.
try {
someFunction (x) ;
} catch (IllegalArgumentException e ) {
// do this if exception 1
callThisFunction1()
} catch (IndexOutOfBoundsException e ) {
// do this if exception 2
callThisFunction2()
} catch(ExceptionType3 | Exceptiontype4 ex) {
// do this if exception 3 or 4
callThisFunction3()
}
Also please try to write down your questions better, like give it a proper order.
1)your input
2)your desired output
3)your code
4)the issue
Thank you...
Related
I'm wondering if there is a clean and complete way to assert on the message attached to a thrown exception when that message was generated using String.format(). For example, a class like:
public class Car {
public static final String DRIVE_ERROR = "Can't drive while car %s is parked!";
private String name;
private boolean parked;
public Car(String name) {
this.name = name;
this.parked = true;
}
public void drive() {
if (parked) {
throw new IllegalStateException(String.format(DRIVE_ERROR, name));
}
}
}
(Sorry for the weird example, just trying to keep it as simple as possible)
Now if I were testing the car, I'd have a class like this:
public class CarTest {
#Test
public void drive_test() {
Car car = new Car("Greased Lightning");
assertThatThrownBy(() -> car.drive())
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("???");
}
}
The question is, what is the best way to assert on the message? In this example, I could separate out the declaration of the name of the car, then use String format myself to grab the static string from Car and format in the name, but that seems like a lot of extra code, and can't be easily used in a lot of instances (eg. when the item that goes in the formatted string is determined at runtime). What I'd really like to be able to do is pass the error message string to hasMessageContaining and have it ignore the "%s" placeholder and accept anything in that spot. Is there a way to do regex matching of Strings with assertJ? Or some other way of doing this cleanly?
EDIT: I'm also open to alternatives on throwing exceptions that have messages that are easier to test. One solution is just using String concatenation, like throw new Exception(STATIC_ERROR_MESSAGE + name) and then testing that the message contains the first part, but that really limits your message formatting ability and doesn't look very clean.
Exception message assertions are limited compared to regular String assertion.
What you could do is use matches or containsPattern assertions, ex:
#Test
public void test() {
// GIVEN some preconditions
// WHEN
Throwable thrown = catchThrowableOfType(() -> { throw new IllegalStateException("boom!"); },
IllegalStateException.class);
// THEN
assertThat(thrown.getMessage()).matches(".oo.")
.containsPattern("oo.");
// or even better thanks to Rolland Illig suggestion
assertThat(thrown).hasMessageMatching(".oo.");
}
Note that by using catchThrowableOfType you don't have to check that the caught exception is of the expected type anymore.
I am trying to load methods Customer.cypher and Customer.cypherCBC method from my class Configuration. Customer class is rendering from different environments so few environmets are having cypherCBC() and cypher() method and few are having only cypher() method.
Now i want to check if cypherCBC if not there in Customer class then load cypher() method. My function is so far;
try {
Class<?> customerClass = Class.forName("com.myapp.impl.service.Customer");
Object obj = customerClass.newInstance();
//here getting "NoSuchMethodException" exception
Method methodCBC = customerClass.getDeclaredMethod("cypherCBC", String.class); //line - 7
if(methodCBC.getName().equals("cypherCBC")){
methodCBC.invoke(obj, new String(dbshPass));
System.out.println("CYPHER_CBC: "
+ methodCBC.invoke(obj, new String(dbshPass)));
}else{
Method method = customerClass.getDeclaredMethod("cypher", String.class);
method.invoke(obj, new String(dbshPass));
System.out.println("CYPHER: " + method.invoke(obj, new String(dbshPass)));
}
}catch (Exception e){
e.printStackTrace();
}
Getting an error at line 7.
NoSuchMethodException:
com.myapp.impl.service.Customer.cypherCBC(java.lang.String)
that means for particular environment class Customer doesn't having cypherCBC() method, but ideally it should come in else part and execute cypher() method.
Class<?> client = null;
Object obj = null;
try{
client = Class.forName("com.myapp.impl.service.Client");
obj = client.newInstance();
}catch (InstantiationException ex) {
System.err.println("Not able to create Instance of Class");
} catch (IllegalAccessException ex) {
System.err.println("Not able to access Class");
} catch (ClassNotFoundException ex) {
System.err.println("Not able to find Class");
}
try {
Method methodCBC = client.getDeclaredMethod("cypherCBC", String.class);
System.out.println("CYPHER_CBC: " + methodCBC.invoke(obj, new String(dbshPass)));
}catch (NoSuchMethodException ex) {
System.err.println("Not able to find Method on class");
ex.printStackTrace();
} catch (Exception e){
e.printStackTrace();
}
That is exactly what is to be expected: getDeclaredMethod() throws that exception when no method exists that meets your specification. And you are wondering that it throws an exception if the required method is missing? Hint: better read the javadoc next time. Don't assume that something does something, but verify your assumptions!
Besides: read your code again. What is it doing? You are asking "give me the method named 'foo'". And then, your next step is to ask that method "is your name 'foo'". So even without reading javadoc, it should become clear that your logic is flawed.
As solution, you can implement a non-throwing lookup yourself, like
private Method lookupCypher(Class<?> client, String methodName) {
for (Method declaredMethod : client.getDeclardMethods()) {
if (declaredMethod.getName().equals(methodName)) {
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
if (parameterTypes.length == 1 && parameterTypes[0].equals(String.class)) {
// so declaredMethod has the given name, and takes one string as argument!
return declaredMethod;
}
}
// our search didn't reveal any matching method!
return null;
}
Using that helper method, you can rewrite your code to:
Method toInvoke = lookupCypher(client, "cypherCBC");
if (toInvoke == null) {
toInvoke = lookupCypher(client, "cypher");
}
toInvoke(obj, new String ...
Or, with the idea from hunter in mind; a much more "OO like" version:
interface CustomerCypherWrapper {
void cypher(String phrase);
}
class NewCustomerWrapper() implements CustomerCypherWrapper {
#Override
void cypher(String phrase) {
new Customer.cypherCBC(phrase);
}
}
class oldCustomerWrapper() implements CustomerCypherWrapper {
#Override
void cypher(String phrase) {
new Customer.cypher(phrase);
}
}
And your client code boils down to:
CustomerCypherWrapper wrapper =
(lookupCypher(..., "cypherCBC") == null)
? new NewCustomerWrapper()
: new OldCustomerWrapper();
wrapper.cypher();
[ I hope you notice that my version A) is easier to read and B) doesn't contain any duplicated code any more. ]
And yes, an alternative implementation of the lookup method could just go like
private Method lookupCyper(Client<?>, String methodName) {
try {
return client.getDeclaredMethod(methodName, String.class);
} catch ....
and return null;
}
... return your public cypherCBC method
But that is an "uncommon practice" in Java. In Java, we ask for permission; instead of forgiveness. Unlike other languages
if you compile the application with a Customer class which has both method,you can use reflection once to check whether the cypherCBC method available or not at runtime, then you can keep that status, you can call the method without using reflection
if(newVersion)
{
customer.cypherCBC(arg);
}
else
{
customer.cypher(arg);
}
But to write a better application,you should use two version baselines.
even though this is a small code fragment you should setup a another module to hide this Customer class and its interactions,that module should have two versions. but your main module has only single version.Now when you you deliver the application , product should be packaged with right version baseline based on compatibility for the target environment.
Although reflection works (as explained in the other answers). if you have control over the Customer class, you can try a non-reflection approach.
interface CBCCypherable {
public String cypherCBC(String pass);
}
You can now have two versions of Customer class, one that implements CBCCypherable and one that doesn't. And when you call it, it looks like this:
Customer c = new Customer();
if (c instanceof CBCCypherable) {
((CBCCypherable)c).cypherCBC(pass);
} else {
c.cypher(pass);
}
What you get with this solution is much simpler code, and as a bonus the compiler will check that you use the correct method name and parameter types. Unlike with reflection, where that's all your job, and you have to run the code to find out if something's wrong.
P.s.: I don't know if this is just sample code or you are really encrypting/hashing passwords here, but it's generally considered a bad idea to roll your own security code.
I have been searching online and researching in some books, but the examples given are limited and I still have some doubts in user-defined exception.
Using the following codes as an example:
//Conventional way of writing user-defined exception
class IdException extends Exception
{
public IdException(String s)
{
super(s);
}
}
class Product
{
String id = new String();
public Product(String _id) throws IdException
{
id = _id;
//Check format of id
if (id.length() < 5)
throw(new IdException(_id));
}
}
It seems that the conventional way of writing a user-defined exception is almost always the same. In the constructor of the user-defined exception, we always call super(msg). This triggers a question in me: If most exceptions are implemented this way, what difference are there between all these exceptions?
For example, I can have multiple user-defined exceptions, but all seems to do the same thing without any differences. (There is no implementation in these exceptions, what makes them work?)
Example:
class IdException extends Exception
{
public IdException(String s)
{
super(s);
}
}
class NameException extends Exception
{
public NameException(String s)
{
super(s);
}
}
class ItemException extends Exception
{
public ItemException(String s)
{
super(s);
}
}
QUE: So shouldn't we (for example) implement the checking of id inside the exception class? If not all exception classes just seem to do the same thing (or not doing anything).
Example of implementing the check in an Exception:
class IdException extends Exception {
public IdException(String s)
{
super(s);
//Can we either place the if-statements here to check format of id ?
}
//Or here ?
}
Ideally you should not implement your business logic inside Exception. Exception tells information about exceptional behaviour, And in Custom Exception you can customise that information.
Find the best practice to write Custom Exception.
We have so many Exceptions already defined in java. All do the same thing : to notify user about the problem in code.
Now suppose we have only one Exception, then How we can what error occurs when the exception is thrown. After all, name matters a lot.
To take your example Exceptions, I would create a more elaborate message by formatting the data provided:
public IdException(String id, String detail) {
super(String.format("The id \"%s\" is invalid: %s", id, detail));
}
throw new IdException(_id, "Id too short.");
This way there is no real logic in the IdException class other than providing the given value (id) and a detail message together in the e.getMessage() String so debugging and logging is easy to read and the code itself is also straightforward:
There is something wrong with the Id _id, namely it is too short. Thus we throw it back at the caller.
Also, when you throw different types of Exceptions in a code, it allows caller code to handle each Exception type differently:
try {
getItem(id, name);
} catch (IdException ex) {
fail(ex.getMessage()); // "The Id is bogus, I don't know what you want from me."
} catch (NameException ex) {
warn(ex.getMessage()); // "The name doesn't match the Id, but here's the Item for that Id anyways"
} catch (ItemException ex) {
fail("Duh! I reported to the dev, something happened");
emailToAdmin(ex.getMessage()); // "The Item has some inconsistent data in the DB"
}
class MyException extends Exception{
int x;
MyException(int y) {
x=y;
}
public String toString(){
return ("Exception Number = "+x) ;
}
}
public class JavaException{
public static void main(String args[]){
try{
throw new MyException(45);
}
catch(MyException e){
System.out.println(e) ;
}
}
}
output: Exception Number = 45
I am coding a web-app in Java-EE and find myself with a very unexpected result when I am trying to display errors on user-input.
The app is built on the JSP/Servlet/Form/Bean model. Basically, the JSP stores data in the request, and transfers it to the servlet. Then the servlet transfers the request raw to the form, which then reads the data, performs the necessary checks and returns the bean to the servlet.
Most of the fields must have specific values, some others must simply be non-null.
I have written error detection code to secure inputs however I find myself with a very strange result:
when the field is non-null but the value is incorrect (say, an hour located outside the 00:00-23:59 range), it does return the proper error, along with the error message, stored in a HashMap, and I can access it in my JSP.
However, when the field is null, it returns the message, probably stores it in the HashMap as well (I know this because the ${!empty errors.dataErrors} test returns true and the error field is displayed in my JSP) but there's no way to access the values of the errors
I have searched through my code but still can't find where the error comes from. Here are snippets of it if someone knows where the problem comes from
doPost method from the servlet:
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
NewBookingForm form = new NewBookingForm();
Booking booking = form.registerBooking(request);
String VUE;
request.setAttribute("booking", booking);
request.setAttribute("errors", form);
this.getServletContext().getRequestDispatcher(VIEW).forward(request, response);
}
the map is a field in the NewBookingForm class, declared and initialized outside the registerBooking method like this private Map<String,String> dataErrors = new HashMap<String,String>(); and it has a private setter (for access within the class) and a public getter (for access in the Servlet and in the JSP)
inside the form class, I use this function to get the field values:
private static String getFieldValue(HttpServletRequest request, String fieldName)
{
String value = request.getParameter(fieldName);
if (value == null || value.trim().length() == 0){return null;}
else{return value;}
}
After getting the values with a series of calls like String fieldDepartureStation = getFieldValue(request, FIELD_DEPARTURE_STATION); at the beginning of my method, I then check them using try/catch blocks like this
try
{validation.departureStation(fieldDepartureStation);}
catch(Exception e)
{setDataErrors(FIELD_DEPARTURE_STATION, e.getMessage());}
The validations method within the validation class are a bit different if the data must have specific value-ranges or must simply be non-null.
In the former case, they are something like this:
public void departureTime(String time) throws Exception
{
if (!validationRETime(time)) { throw new Exception("Please input a time with the hh:mm pattern"); }
}
....
private boolean validationRETime(String strTime)
{
String regExp = "^([01][0-9]|2[0-3])[:][0-5][0-9]$"; // hh:mm
if (strTime.matches(regExp))
{
return true;
}
else
{
return false;
}
}
whereas in the latter case they are simply
public void departureStation(String station) throws Exception
{
if (station.equals(null)) { throw new Exception("Please input a departure station"); }
}
Finally, in my JSP, I use the following code to display errors:
<c:if test="${!empty errors.dataErrors}">
<p>Errors</p>
<c:forEach items="${errors.dataErrors}" var="message">
<p><c:out value="${message.value}" /></p>
</c:forEach>
</c:if>
And it does display the Error paragraph when I purposedly enter incorrect values, but the <c:forEach> is only looping and displaying the error messages when the wrong field is non-null but with an incorrect value. Thus with a field that only needs to be non-null, I never get the message (but I do get the error)
These are all the things I could think of that could possibly go wrong, but I have yet to discover where they did and if someone could help me, I'd be very glad.
The problem is in your departureStation method: -
public void departureStation(String station) throws Exception
{
if (station.equals(null)) {
throw new Exception("Please input a departure station");
}
}
Your test for null value is itself triggering a NPE. So, as soon as station.equals(null) is executed for station = null, a NPE exception is thrown, which is then propagated to the caller. So, your if block will not even be executed. And hence you are not throwing the Exception as you might be thinking.
Now, also note that, the NPE that is thrown does not contain any message. So, e.getMessage() will return null on it.
Now, let's move back to the caller: -
try
{validation.departureStation(fieldDepartureStation);}
catch(Exception e)
{setDataErrors(FIELD_DEPARTURE_STATION, e.getMessage());}
Here you are doing the biggest Crime in the world of Exception Handling, by using a catch block for Exception. Since Exception is the super class of all the exceptions, it will handle all the exceptions in the same way. So, it consumes the NPE, and passes it to setDataErrors().
So, you will of course get the errors, but, the value e.getMessage() will be null. And that is why you are not seeing any message. You can even test it by logging the value of e.getMessage() in the catch block above.
Solution ??
Just change your null check with this one: -
if (station == null) {
throw new Exception("Please input a departure station");
}
And everything will be ok. I think, you will have to do this change in all of your methods. Always perform the null check using == operator.
public class Test {
public static void main(String[] args) {
Test c = new Test ();
try {
c.departureTime("30:30");
} catch (Exception e) {
System.out.println(e.getMessage());
}
try {
c.departureTime(null);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
public void departureTime(String time) throws Exception {
if (!validationRETime(time)) {
throw new Exception("Please input a time with the hh:mm pattern");
}
}
private boolean validationRETime(String strTime) {
String regExp = "^([01][0-9]|2[0-3])[:][0-5][0-9]$"; // hh:mm
if (strTime.matches(regExp)) {
return true;
} else {
return false;
}
}
}
You will figure out your problem by running above code. Simply putting, you need to make sure exp.getMessage() always has value. To fix the issue, you might want to tweak your departureTime() method to provide more fine-grained exception handling.
I have enum say ErrorCodes that
public enum ErrorCodes {
INVALID_LOGIN(100),
INVALID_PASSWORD(101),
SESSION_EXPIRED(102) ...;
private int errorCode;
private ErrorCodes(int error){
this.errorCode = error;
} //setter and getter and other codes
}
now I check my exception error codes with this error codes. I don't want to write if this do this, if this do this. How I can solve this problem (writing 10+ if blocks)
Is there any design patter to that situation ?
Thanks
Either you do it with a if-statement or a switch, or you just implement the logic in question into the ErrorCode somehow.
In an OO fashion it all depends on how you want the application or system react to the error code. Lets say you just want it to output somekind of dialog:
public doSomethingWithError() {
ErrorCodes e = getError();
// the source of error, or originator, returns the enum
switch(e) {
case ErrorCodes.INVALID_LOGIN:
prompt('Invalid Login');
case ErrorCodes.INVALID_PASSWORD:
prompt('Invalid password');
// and so on
}
}
We could instead create an ErrorHandler class that does this instead:
// We'll implement this using OO instead
public doSomethingWithError() {
ErrorHandler e = getError();
// the originator now returns an ErrorHandler object instead
e.handleMessage();
}
// We will need the following abstract class:
public abstract class ErrorHandler {
// Lets say we have a prompter class that prompts the message
private Prompter prompter = new Prompter();
public final void handleMessage() {
String message = this.getMessage();
prompter.prompt(message);
}
// This needs to be implemented in subclasses because
// handleMessage() method is using it.
public abstract String getMessage();
}
// And you'll have the following implementations, e.g.
// for invalid logins:
public final class InvalidLoginHandler() {
public final String getMessage() {
return "Invalid login";
}
}
// E.g. for invalid password:
public final class InvalidPasswordHandler() {
public final String getMessage() {
return "Invalid password";
}
}
The former solution is easy to implement, but becomes difficult to maintain as the code grows larger. The latter solution is more complex, (aka. Template Method pattern following the Open-Closed Principle) but enables you to add more methods into the ErrorHandler when you need it (such as restoring resources or whatever). You can also implement this with the Strategy pattern.
You won't get away completely with the conditional statements, but in the latter the conditional is pushed to the part of the code where the error is originated. That way you won't have double maintenance on conditional statements both at the originator and the error handling code.
EDIT:
See this answer by Michael Borgwardt and this answer by oksayt for how to implement methods on Java Enums if you want to do that instead.
Java enums are very powerful and allow per-instance method implementations:
public enum ErrorCode {
INVALID_LOGIN {
public void handleError() {
// do something
}
},
INVALID_PASSWORD {
public void handleError() {
// do something else
}
},
SESSION_EXPIRED {
public void handleError() {
// do something else again
}
};
public abstract void handleError();
}
Then you can simply call errorCode.handleError();. However, it is questionable whether an ErrorCode enum is really the right place for that logic.
As pointed out by Spoike, using polymorphism to pick the right error handling method is an option. This approach basically defers the 10+ if blocks to the JVM's virtual method lookup, by defining a class hierarchy.
But before going for a full-blown class hierarchy, also consider using enum methods. This option works well if what you plan to do in each case is fairly similar.
For example, if you want to return a different error message for each ErrorCode, you can simply do this:
// Note singular name for enum
public enum ErrorCode {
INVALID_LOGIN(100, "Your login is invalid"),
INVALID_PASSWORD(101, "Your password is invalid"),
SESSION_EXPIRED(102, "Your session has expired");
private final int code;
private final String
private ErrorCode(int code, String message){
this.code = code;
this.message = message;
}
public String getMessage() {
return message;
}
}
Then your error handling code becomes just:
ErrorCode errorCode = getErrorCode();
prompt(errorCode.getMessage());
One drawback of this approach is that if you want to add additional cases, you'll need to modify the enum itself, whereas with a class hierarchy you can add new cases without modifying existing code.
I believe the best you can do is implementing the strategy pattern. This way you won't have to change existing classes when adding new enums but will still be able to extend them. (Open-Closed-Principle).
Search for Strategy Pattern and Open Closed Principle.
You can create a map of error codes(Integer) against enum types
Edit
In this solution, once the map is prepared, you can look up an error code in the map and thus will not require if..else look ups.
E.g.
Map<Integer, ErrorCodes> errorMap = new HashMap<Integer, ErrorCodes>();
for (ErrorCodes error : ErrorCodes.values()) {
errorMap.put(error.getCode(), error);
}
Now when you want to check an error code coming from your aplpication, all you need to do is,
ErrorCodes error = errorMap.get(erro_code_from_application);
Thus removing the need for all the if..else.
You just need to set up the map in a way that adding error codes doesn't require changes in other code. Preparation of the map is one time activity and can be linked to a database, property file etc during the initialization of your application
In my opinion there is nothing wrong with ErrorCodes as enums and a switch statement to dispatch error handling. Enums and switch fit together really well.
However, maybe you find the following insteresting (kind of over-design), see an Example
or "Double dispatching" on Wikipedia.
Assumed requirements:
Error-handling should be encapsulated in an own class
Error-handling should be replacable
Type safety: Whenever an error is added, you are forced to add error handling at each error-handler implementation. It is not possible to "forget" an error in one (of maybe many) switch statments.
The code:
//Inteface for type-safe error handler
interface ErrorHandler {
void handleInvalidLoginError(InvalidLoginError error);
void handleInvalidPasswordError(InvalidLoginError error);
//One method must be added for each kind error. No chance to "forget" one.
}
//The error hierachy
public class AbstractError(Exception) {
private int code;
abstract public void handle(ErrorHandler);
}
public class InvalidLoginError(AbstractError) {
private String additionalStuff;
public void handle(ErrorHandler handler) {
handler.handleInvalidLoginError(this);
}
public String getAdditionalStuff();
}
public class InvalidPasswordError(AbstractError) {
private int code;
public void handle(ErrorHandler handler) {
handler.handleInvalidPasswordError(this);
}
}
//Test class
public class Test {
public void test() {
//Create an error handler instance.
ErrorHandler handler = new LoggingErrorHandler();
try {
doSomething();//throws AbstractError
}
catch (AbstractError e) {
e.handle(handler);
}
}
}