tags:

views:

2531

answers:

7

I am writing a web service in Java, and I am trying to figure out the best way to define error codes and their associated error strings. I need to have a numerical error code and an error string grouped together. Both the error code and error string will be sent to the client accessing the web service. For example, when a SQLException occurs, I might want to do the following:

// Example: errorCode = 1, 
//          errorString = "There was a problem accessing the database."
throw new SomeWebServiceException(errorCode, errorString);

The client program might be shown the message:

"Error #1 has occured: There was a problem accessing the database."

My first thought was to used an Enum of the error codes and override the toString methods to return the error strings. Here is what I came up with:

public enum Errors {
  DATABASE {
    @Override
    public String toString() {
      return "A database error has occured.";
    }
  },

  DUPLICATE_USER {
    @Override
    public String toString() {
      return "This user already exists.";
    }
  },

  // more errors follow
}

My question is: Is there a better way to do this? I would prefer an solution in code, rather than reading from an external file. I am using Javadoc for this project, and being able to document the error codes in-line and have them automatically update in the documentation would be helpful.

+17  A: 

Well there's certainly a better implementation of the enum solution (which is generally quite nice):

public enum Error {
  DATABASE(0, "A database error has occured."),
  DUPLICATE_USER(1, "This user already exists.");

  private final int code;
  private final String description;

  private Error(int code, String description) {
    this.code = code;
    this.description = description;
  }

  public String getDescription() {
     return description;
  }

  public int getCode() {
     return code;
  }

  @Override
  public String toString() {
    return code + ": " + description;
  }
}

You may want to override toString() to just return the description instead - not sure. Anyway, the main point is that you don't need to override separately for each error code. Also note that I've explicitly specified the code instead of using the ordinal value - this makes it easier to change the order and add/remove errors later.

Don't forget that this isn't internationalised at all - but unless your web service client sends you a locale description, you can't easily internationalise it yourself anyway. At least they'll have the error code to use for i18n at the client side...

Jon Skeet
Damn, too slow...
Cowan
Awesome, just what I wanted. Thanks!
William Brendel
To internationalize, replace the description field with a string code that can be looked up in a resource bundle?
Marcus Downing
@Marcus: I like that idea. I'm concentrating on getting this thing out the door, but when we look at internationalization, I think I will do what you suggested. Thanks!
William Brendel
+9  A: 

Overloading toString() seems a bit icky -- that seems a bit of a stretch of toString()'s normal use.

What about:

public enum Errors {
  DATABASE(1, "A database error has occured."),
  DUPLICATE_USER(5007, "This user already exists."),
  //... more here ...

  private final int id;
  private final String message;

  Errors(int id, String message) {
     this.id = id;
     this.message = message;
  }

  public int getId() { return id; }
  public int getMessage() { return message; }
}

seems a lot cleaner to me... and less verbose.

Cowan
Overloading toString() on any objects (let alone enums) is quite normal.
cletus
+1 Not quite as flexible as Jon Skeet's solution, but it still solves the problem nicely. Thanks!
William Brendel
I meant that toString() is most commonly and usefully used to give enough information to identify the object -- it often includes the class name, or some way to meaningfully tell the type of object. A toString() which returns just 'A database error has occurred' would be surprising in many contexts.
Cowan
I agree with Cowan, using toString() in this way seems a bit 'hackish'. Just a quick bang for the buck and not a normal usage. For the enum, toString() should return the name of the enum constant. This would look interesting in a debugger when you want the value of a variable.
Robin
+3  A: 

As far as I am concerned, I prefer to externalize the error messages in a properties files. This will be really helpfull in case of internationalization of your application (one properties file per language). It is also easier to modify an error message, and it will not need any re-compilation of the Java sources.

On my projects, generally I have an interface that contains errors codes (String or integer, it doesn't care much), which contains the key in the properties files for this error:

public interface ErrorCodes {
    String DATABASE_ERROR = "DATABASE_ERROR";
    String DUPLICATE_USER = "DUPLICATE_USER";
    ...
}

in the properties file:

DATABASE_ERROR=An error occurred in the database.
DUPLICATE_USER=The user already exists.
...

Another problem with your solution is the maintenability: You have only 2 errors, and already 12 lines of code. So imagine your Enumeration file when you will have hundreds of errors to manage!

romaintaz
I would up this more than 1 if I could. Hardcoding the strings is ugly for maintenance.
Robin
A: 

I (and the rest of our team in my company) prefer to raise exceptions instead of returning error codes. Error codes have to be checked everywhere, passed around, and tend to make the code unreadable when the amount of code becomes bigger.

The error class would then define the message.

PS: and actually also care for internationalization !
PPS: you could also redefine the raise-method and add logging, filtering etc. if required (at leastin environments, where the Exception classes and friends are extendable/changeable)

blabla999
The error code is in the exception, not instead of.
Robin
sorry, Robin, but then (at least from the above example), these ought to be two exceptions - "database error" and "duplicate user" are so completely different that two separate error-subclasses should be created, which are individually catchable (one being a system, the other being an admin error)
blabla999
and then, again, error-code enums are not needed.
blabla999
and what are the error codes used for, if not to differentiate between one or the other exception ? So at least above the handler, he is exactly that: dealing with error-codes which are passed around and if-switched upon.
blabla999
I think the name of the exception would be far more illustrative and self-describing than an error code. Better to put more thought into discovering good exception names, IMO.
duffymo
+1  A: 

I'd recommend that you take a look at java.util.ResourceBundle. You should care about I18N, but it's worth it even if you don't. Externalizing the messages is a very good idea. I've found that it was useful to be able to give a spreadsheet to business folks that allowed them to put in the exact language they wanted to see. We wrote an Ant task to generate the .properties files at compile time. It makes I18N trivial.

If you're also using Spring, so much the better. Their MessageSource class is useful for these sorts of things.

duffymo
+3  A: 

At my last job I went a little deeper in the enum version:

public enum Messages {
    @Error
    @Text("You can''t put a {0} in a {1}")
    XYZ00001_CONTAINMENT_NOT_ALLOWED,
    ...
}

@Error, @Info, @Warning are retained in the class file and are available at runtime. (We had a couple of other annotations to help describe message delivery as well)

@Text is a compile-time annotation.

I wrote an annotation processor for this that did the following:

  • Verify that there are no duplicate message numbers (the part before the first underscore)
  • Syntax-check the message text
  • Generate a messages.properties file that contains the text, keyed by the enum value.

I wrote a few utility routines that helped log errors, wrap them as exceptions (if desired) and so forth.

I'm trying to get them to let me open-source it... -- Scott

Scott Stanchfield
A: 

this came in handy, i was needing for something exactly like this, for the guy who says that raise exceptions, in my case i'm developing an applet that control many hardware devices, and in case of error i return a error code to the front end, because the applet is controlled via javascript the error code is very useful.

I must develop a util to map enums to exceptions and bundles like other suggest, sounds cool :) and very generic to use in future apps