class CSVReader {
private List<String> output;
private InputStream input;
public CSVReader(InputStream input) {
this.input = input;
}
public void read() throws Exception{
//do something with the inputstream
// create output list.
}
public List<String> getOutput() {
return Collections.unmodifiableList(output);
}
I am trying to create a simple class which will be part of a library. I would like to create code that satisfies the following conditions:
handles all potential errors or wraps them into library errors and
throws them.
creates meaningful and complete object states (no incomplete object structures).
easy to utilize by developers using the library
Now, when I evaluated the code above, against the goals, I realized that I failed badly. A developer using this code would have to write something like this -
CSVReader reader = new CVSReader(new FileInputStream("test.csv");
reader.read();
read.getOutput();
I see the following issues straight away -
- developer has to call read first before getOutput. There is no way for him to know this intuitively and this is probably bad design.
So, I decided to fix the code and write something like this
public List<String> getOutput() throws IOException{
if(output==null)
read();
return Collections.unmodifiableList(output);
}
OR this
public List<String> getOutput() {
if(output==null)
throw new IncompleteStateException("invoke read before getoutput()");
return Collections.unmodifiableList(output);
}
OR this
public CSVReader(InputStream input) {
read(); //throw runtime exception
}
OR this
public List<String> read() throws IOException {
//read and create output list.
// return list
}
What is a good way to achieve my goals? Should the object state be always well defined? - there is never a state where "output" is not defined, so I should create the output as part of constructor? Or should the class ensure that a created instance is always valid, by calling "read" whenever it finds that "output" is not defined and just throw a runtime exception? What is a good approach/ best practice here?
I would make read() private and have getOutput() call it as an implementation detail. If the point of exposing read() is to lazy-load the file, you can do that with exposing getOutput only
public List<String> getOutput() {
if (output == null) {
try {
output = read();
} catch (IOException) {
//here you either wrap into your own exception and then declare it in the signature of getOutput, or just not catch it and make getOutput `throws IOException`
}
}
return Collections.unmodifiableList(output);
}
The advantage of this is that the interface of your class is very trivial: you give me an input (via constructor) I give you an output (via getOutput), no magic order of calls while preserving lazy-loading which is nice if the file is big.
Another advantage of removing read from the public API is that you can go from lazy-loading to eager-loading and viceversa without affecting your clients. If you expose read you have to account for it being called in all possible states of your object (before it's loaded, while it's already running, after it already loaded). In short, always expose the least possible
So to address your specific questions:
Yes, the object state should always be well-defined. Your point of not knowing that an external call on read by the client class is needed is indeed a design smell
Yes, you could call read in the constructor and eagerly load everything upfront. Deciding to lazy-load or not is an implementation detail dependent on your context, it should not matter to a client of your class
Throwing an exception if read has not been called puts again the burden to calling things in the right, implicit order on the client, which is unnecessary due to your comment that output is never really undefined so the implementation itself can make the risk-free decision of when to call read
I would suggest you make your class as small as possible, dropping the getOutput() method all together.
The idea is to have a class that reads a CSV file and returns a list, representing the result. To achieve this, you can expose a single read() method, that will return a List<String>.
Something like:
public class CSVReader {
private final InputStream input;
public CSVReader(String filename) {
this.input = new FileInputStream(filename);
}
public List<String> read() {
// perform the actual reading here
}
}
You have a well defined class, a small interface to maintain and the instances of CSVReader are immutable.
Have getOutput check if it is null (or out of date) and load it in automatically if it is. This allows for a user of your class to not have to care about internal state of the class's file management.
However, you may also want to expose a read function so that the user can chose to load in the file when it is convenient. If you make the class for a concurrent environment, I would recommend doing so.
The first approach takes away some flexibility from the API: before the change the user could call read() in a context where an exception is expected, and then call getOutput() exception-free as many times as he pleases. Your change forces the user to catch a checked exception in contexts where it wasn't necessary before.
The second approach is how it should have been done in the first place: since calling read() is a prerequisite of calling getOutput(), it is a responsibility of your class to "catch" your users when they "forget" to make a call to read().
The third approach hides IOException, which may be a legitimate exception to catch. There is no way to let the user know if the exception is going to be thrown or not, which is a bad practice when designing runtime exceptions.
The root cause of your problem is that the class has two orthogonal responsibilities:
Reading a CSV, and
Storing the result of a read for later use.
If you separate these two responsibilities from each other, you would end up with a cleaner design, in which the users would have no confusion over what they must call, and in what order:
interface CSVData {
List<String> getOutput();
}
class CSVReader {
public static CSVData read(InputStream input) throws IOException {
...
}
}
You could combine the two into a single class with a factory method:
class CSVData {
private CSVData() { // No user instantiation
}
// Getting data is exception-free
public List<String> getOutput() {
...
}
// Creating instances requires a factory call
public static CSVData read(InputStream input) throws IOException {
...
}
}
We are currently developing a framework for internal use. We are now at the point that we want to use standardized exception IDs and messages. The developer just has to provide an ID and a default message and the framework looks up the associated message(if possible) or uses the default message(if the message could not be retrieved).
The first idea was to write an exception factory which enforces the developer to provide an error ID and then lookup the message in the factory method. The problem with this approach is, that an additional frame is added in the stacktrace(the factory method where the exception is created). Thats not very nice. Almost every solution which tries to hide the creation has this issue.
The second idea was to couple the message lookup and our exception class and write an abstract exception class which takes the error ID in the constructor and handles the message lookup. But thats also not very nice cause it lacks loose coupling.
Third idea was let the developer write the lookup of the message every time.. also bad..
Do you have any ideas?
P.S. I think thats the first time I need macros in Java..
the different approaches in code:
1.
throw createException(new ErrorId("123"), TechnicalException.class, "defaultmsg"); //uses reflection, also not very nice but works
or
throw createTechnicalException(new ErrorId("123", "defaultmsg"); //write a method for every type of exception
2.
public TechnicalException(ErrorId errorId, String defaultmsg) {
super(defaultmsg);
//get msg here and set on a new field cause detailMessage is not accessible. Also overwrite toString();
}
throw new TechnicalException(new ErrorId("123"), "defaultmsg");
3.
ErrorId errorId = new ErrorId("123");
String msg = someProvider.getMessage(errorId);
throw new TechnicalException(errorId, msg);
It's possible to edit the stack trace, removing unneeded elements. If all exceptions' constructors need the same data, use single createException, else use createCustomException methods.
This removes unnecessary stack trace elements.
public static <T extends Throwable> T adjustStackTrace(T ex, int removedElements) {
//use an explicit constraint for removedElements
if (removedElements < 1) {
throw new IllegalArgumentException("removedElements may not be less than 1");
}
final StackTraceElement[] stack = ex.getStackTrace();
//or an implicit constraint
removedElements = Math.max(removedElements, stack.length);
final StackTraceElement[] newStack = new StackTraceElement[stack.length - removedElements];
System.arraycopy(stack, removedElements, newStack, 0, stack.length - removedElements);
ex.setStackTrace(newStack);
return ex;
}
a method for a custom excepton:
public static TechnicalException createTechnicalException(Object... someExceptionSpecificData) {
StringBuilder sb = new StringBuilder();
sb.append("...").append("..."); //create a message
//process the someExceptionSpecificData
Object[] someNewData = someExceptionSpecificData;
TechnicalException ex = new TechnicalException(sb.toString(), someNewData);
ex = adjustStackTrace(ex, 1 /*remove StackTraceElement, related to this method call*/);
return ex;
}
Or you can use a single method and pass desired exception class:
public Exception createException(Class<?> exClass, Object... someExceptionSpecificData) {...}
usage example:
public void useExceptionFactory() {
throw ExceptionFactory.createTechnicalException();
}
public void adjustEvenMore() {
//adjust even more to hide the implementation of your library
throw ExceptionFactory.adjustStackTrace(ExceptionFactory.createTechnicalException(),1);
}
This example is based on net.sf.oval.internal.util.Assert from OVal framework, see http://oval.sourceforge.net/
I am trying to make it clear of the difference between Throws in method signature and Throw Statements in Java.
Throws in method signature is as following:
public void aMethod() throws IOException{
FileReader f = new FileReader("notExist.txt");
}
Throw Statements is as following:
public void bMethod() {
throw new IOException();
}
From my understanding, a throws in method signature is a notification that the method may throw such an exception. throw statement is what actually throw a created object under according circumstances.
In that sense, throws in method signature should always appear if there exist a throw statement in the method.
However, the following code doesn't seem doing so. The code is from the library. My question is why it is happening? Am I understanding the concepts wrong?
This piece of code is a copy from java.util.linkedList. #author Josh Bloch
/**
* Returns the first element in this list.
*
* #return the first element in this list
* #throws NoSuchElementException if this list is empty
*/
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
Update on the answer:
update 1 : is above code the same as the following?
// as far as I know, it is the same as without throws
public E getFirst() throws NoSuchElementException {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
update 2 : For checked exception. Do I need to have "throws" in the signature? Yes.
// has to throw checked exception otherwise compile error
public String abc() throws IOException{
throw new IOException();
}
You are pretty much right on. Except for one thing I'll mention in a bit.
throws is as much a part of the method API as the name and the parameters. Clients know if they call that method, they need to handle that exception--by simply throwing it also or by catching it and handling it (which may in fact entail the throwing of another exception wrapping the original). throws is addressed at compile time.
throw is the actual act of letting the runtime know something bad happened--that the exceptional condition we were worried about has in fact taken place. So it needs to be dealt with at runtime.
But you weren't quite right when you said, "Throws in method signature should always appear if there exist a throw statement in the method." That is often true but not always. I could also call another method that throws an exception within my method, and if I don't catch it, my method needs to throw it. In that case, there is no explicit throw of the same exception by me.
The final point is that you only need to declare an exception in throws when the exception is a checked exception--meaning it is from the other side of the Exception class hierarchy from RuntimeException. Common checked exceptions are IOException and SQLException. Checked exceptions must be listed in the throws part of the method signature if you don't handle them yourself. Anything subclassing RuntimeException--like NoSuchElementException in your example and also the hated NullPointerException--is an unchecked exception and doesn't have to be caught or thrown or anything.
Typically, you use checked exceptions for recoverable problems (where the client knows what can happen and can gracefully handle the problem and move on) and unchecked exceptions for catastrophic problems (like can't connect to the database).
If you can get past all the AOP stuff, this is a great discussion of how you use checked and unchecked exceptions effectively.
Vidya provided great answer to your questions.
The most important words are "The final point is that you only need to declare an exception in throws when the exception is a checked exception"
Just to show you an example code what does this mean. Imagine that we would like to use FileOutputStream in order to pass some data. The function would look like this:
public void saveSomeData() throws IOException {
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("input.txt");
out = new FileOutputStream("output.txt");
int c;
while ((c = out.read() != -1) {
in.write(c);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// Close in
if (in != null) {
in.close(); // <-- If something bad happens here it will cause runtime error!
}
// Close out
...
}
}
Now imagine, if you wouldn't provide throws IOException and something bad happens inside finally{} statement - it would cause an error.
RuntimeExceptions dont have to be handled in try-catch block so they dont have to be declared as thrown and NoSuchElementException is RuntimeException because it extends it.
throw attribute in method signature, like you correctly guessed, is a hint to the compiler that the method raises an exception that must be caught by the caller. This kind of exception, namely called checked exception is something that the caller MUST always catch or dispatch to its caller again. This is something at compiler level, the signature specifies which exception the method is able to throw: this enforces a try-catch or re-dispatch in the caller and a throw statement somewhere inside the method, is a constraint that the developer places to specify something about the method behavior.
On the other hand other exceptions, namely unchecked or runtime exceptions, (NoSucheElementException is one example) are exceptions which you are not forced to specify becuase they arise from different situations.
The conceptual difference is that checked exception are usually used to warn about exceptional situation which should be handled somehow (think about IOException) by the developer, while unchecked are real errors (like NullPointerException or like in your example NoSuchElementException)
I am trying my hand at writing test cases. From what I have read, my tests should fail from the start and I should strive to make tests pass. However, I find myself writing tests checking boundaries and the exceptions they should cause:
#Test(expected=NegativeArraySizeException.class)
public void testWorldMapIntInt() {
WorldMap w = new WorldMap(-1, -1);
}
#Test(expected=IndexOutOfBoundsException.class)
public void testGetnIntnInt() {
WorldMap w = new WorldMap(10,10);
Object o = w.get(-1, -1);
}
However, this test passes by default because Java will throw the exception anyway. Is there a better way to handle these kinds of expected exceptions, possibly a way that fails by default-- forcing me to strive to handle these cases?
I agree that the style you present is not so good. The problem is that it doesn't check where in the method the exception is thrown, so it's possible to get false negatives.
We usually write tests for exceptions like this:
public void testWorldMapIntInt() {
try {
WorldMap w = new WorldMap(-1, -1);
Assert.fail("should have thrown IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException e) {}
}
Expected behaviour for WorldMap is to throw an exception if (-1, -1) passed into it
Initially it doesn't do that, so your test will fail as it does not see expected exception.
You implement the code for WorldMap correctly, including throwing exception when (-1, -1) passed in.
You rerun your test, it passes.
Sound like good TDD to me!
That seems like a fair test to write. WorldMap is no standard Java class. Presumably it's your own class. Therefore the test wouldn't be passing if you hadn't already written some code. This test will force you to throw (or propagate) an appropriate exception from your class. That sounds like a good test to me, which you should write before implementing the behavior.
I personally look for mistakes like that in the WorldMap constructor and throw an IllegalArgumentException, that way you can provide a better error message, such as what the value passed in was and what the expected range is.
As for having that test fail by default, I cannot think of a reasonable way of doing that if you are going to have it actually do something (if you are writing the tests first then it should fail because the constructor won't have any code).
Agree with accepted answer, try-fail-catch idiom, although ugly and cluttering the test, is much better than #Test(expcted=...) as it might report false positives.
A while back I implemented very simple JUnit rule to deal with exception testing in both safe and readable manner:
public class DefaultFooServiceTest {
#UnderTest
private FooService fooService = new DefaultFooService();
#Rule
public ExceptionAssert exception = new ExceptionAssert();
#Test
public void shouldThrowNpeWhenNullName() throws Exception {
//given
String name = null;
//when
fooService.echo(name);
//then
exception.expect(NullPointerException.class);
}
#Test
public void shouldThrowIllegalArgumentWhenNameJohn() throws Exception {
//given
String name = "John";
//when
fooService.echo(name);
//then
exception.expect(IllegalArgumentException.class)
.expectMessage("Name: 'John' is not allowed");
}
}
See blog post and source.
I noticed some confusion initially with my question. I'm not asking about how to configure a logger nor how to use a logger properly, but rather how to capture all of the information that would have been logged at a lower logging level than what the current logging level is in the exception message.
I have been noticing two patterns in Java for logging information that may be useful to a developer when an exception occurs.
The following pattern seems very common. Basically, you just have your logger log information in-line as needed, so that when an exception occurs you have the log trace.
try {
String myValue = someObject.getValue();
logger.debug("Value: {}", myValue);
doSomething(myValue);
}
catch (BadThingsHappenException bthe) {
// consider this a RuntimeException wrapper class
throw new UnhandledException(bthe);
}
The drawback with the above approach is that if your users require relatively quiet logs and need a high level of reliability to the point where they just can't "try it again in debug mode", the exception message contains insufficient data by itself to be useful to the developer.
The next pattern is one that I have seen that tries to mitigate this problem but seems ugly:
String myValue = null;
try {
myValue = someObject.getValue();
doSomething(myValue);
}
catch (BadThingsHappenException bthe) {
String pattern = "An error occurred when setting value. [value={}]";
// note that the format method below doesn't barf on nulls
String detail = MessageFormatter.format(pattern, myValue);
// consider this a RuntimeException wrapper class
throw new UnhandledException(detail, bthe);
}
The above pattern seems to somewhat solve the problem, however, I'm not sure I like to declare so many variables outside the scope of the try block. Especially, when I have to deal with very complicated states.
The only other approach I have seen is using a Map to store key-value pairs that are then dumped into the exception message. I'm not sure I like that approach either since it seems to create code bloat.
Is there some Java voodoo out there that I am missing? How do you handle your exception state information?
We tend to create our most important application specific runtime exception classes with some special constructors, some constants and a ResourceBundle.
Example snippet:
public class MyException extends RuntimeException
{
private static final long serialVersionUID = 5224152764776895846L;
private static final ResourceBundle MESSAGES;
static
{
MESSAGES = ResourceBundle.getBundle("....MyExceptionMessages");
}
public static final String NO_CODE = "unknown";
public static final String PROBLEMCODEONE = "problemCodeOne";
public static final String PROBLEMCODETWO = "problemCodeTwo";
// ... some more self-descriptive problem code constants
private String errorCode = NO_CODE;
private Object[] parameters = null;
// Define some constructors
public MyException(String errorCode)
{
super();
this.errorCode = errorCode;
}
public MyException(String errorCode, Object[] parameters)
{
this.errorCode = errorCode;
this.parameters = parameters;
}
public MyException(String errorCode, Throwable cause)
{
super(cause);
this.errorCode = errorCode;
}
public MyException(String errorCode, Object[] parameters, Throwable cause)
{
super(cause);
this.errorCode = errorCode;
this.parameters = parameters;
}
#Override
public String getLocalizedMessage()
{
if (NO_CODE.equals(errorCode))
{
return super.getLocalizedMessage();
}
String msg = MESSAGES.getString(errorCode);
if(parameters == null)
{
return msg;
}
return MessageFormat.format(msg, parameters);
}
}
In the properties file we specify the exception descriptions, e.g.:
problemCodeOne=Simple exception message
problemCodeTwo=Parameterized exception message for {0} value
Using this approach
We can use quite readable and understandable throw clauses (throw new MyException(MyException.PROBLEMCODETWO, new Object[] {parameter}, bthe))
The exception messages are "centralized", can easily maintained and "internationalized"
EDIT: change getMessage to getLocalizedMessage as Elijah suggested.
EDIT2: Forgot to mention: this approach does not support Locale changing "on-the-fly" but it is intentional (it can be implemented if you need it).
Another good logging API is SLF4J. It can be configured to also intercept log APIs for Log4J, Java Util Logging, and Jakarta Commons Logging. And it can also be configured to use various logging implementations, including Log4J, Logback, Java Util Logging, and one or two others. This gives it enormous flexibility. It was developed by the author of Log4J to be its successor.
Of relevance to this question, the SLF4J API has a mechanism to concatenate string valued expressions into a log message. The following calls are equivalent, but the second is about 30x faster to process if you're not outputting debug level messages, since the concatenation is avoided:
logger.debug("The new entry is " + entry + ".");
logger.debug("The new entry is {}.", entry);
There's a two argument version too:
logger.debug("The new entry is {}. It replaces {}.", entry, oldEntry);
And for more than two you can pass in an array of Object like this:
logger.debug("Value {} was inserted between {} and {}.",
new Object[] {newVal, below, above});
This is a nice terse format that eliminates clutter.
Example source is from the SLF4J FAQ.
Edit: Here's a possible refactoring of your example:
try {
doSomething(someObject.getValue());
}
catch (BadThingsHappenException bthe) {
throw new UnhandledException(
MessageFormatter.format("An error occurred when setting value. [value={}]",
someObject.getValue()),
bthe);
}
Or if this pattern occurs more than a few places you could write a set of static methods that capture the commonality, something like:
try {
doSomething(someObject.getValue());
}
catch (BadThingsHappenException bthe) {
throwFormattedException(logger, bthe,
"An error occurred when setting value. [value={}]",
someObject.getValue()));
}
and of course the method would also put the formatted message out on the logger for you.
Perhaps I'm missing something, but if the users really require a relatively quiet log file, why don't you just configure your debug logs to go to a separate spot?
If that's insufficient, then capture a fixed amount of the debug logs in RAM. E.g., the last 500 entries. Then, when something ugly happens, dump the debug logs along with the problem report. You don't mention your logging framework, but this would be pretty easy to do in Log4J.
Even better, assuming you have the user's permission, just send an automatic error report rather than logging. I recently helped some folks run down a hard-to-find bug and made the error reporting automatic. We got 50x the number of bug reports, making the problem pretty easy to find.
Take a look at the MemoryHandler class from java.util.logging. It acts as a buffer between your log.$level() invocations and the actual output, and will pass it's buffer content into the output only if some condition is met.
For example you could configure it to dump content only if it sees ERROR level message. Then you can safely output DEBUG level messages and no one will see them unless actual error occurs and then all messages are written to log file.
I would guess there are similar implementations for other logging frameworks.
EDIT: One possible issue with this approach is a performance lost on generating all the debug messages (see #djna comment). Because of this it could be a good idea to make the level of logging going into the buffer configurable - in production it should be INFO or higher, and only if you are actively hunting a problem down it could be turned down to DEBUG.
Besides your example which declares local fields outside the try block in order to be accessible inside the catch block, one very simple way of handling this is to dump the state of the class out in the Exception using the class's overridden toString method. Granted, this is only useful in Classes that maintain state.
try {
setMyValue(someObject.getValue());
doSomething(getMyValue());
}
catch (BadThingsHappenException bthe) {
// consider this a RuntimeException wrapper class
throw new UnhandledException(toString(), bthe);
}
Your toString() would need to be overridden:
public String toString() {
return super.toString() + "[myValue: " + getMyValue() +"]";
}
edit:
another idea:
You could maintain state in a ThreadLocal debug context. Suppose you create a class called MyDebugUtils which holds a ThreadLocal that contains a Map per Thread. You allow for static access to this ThreadLocal and maintenance methods (ie, to clear the context when your debugging is finished).
The interface could be:
public static void setValue(Object key, Object value)
public static void clearContext()
public static String getContextString()
and in our example:
try {
MyDebugUtils.setValue("someObeject.value", someObject.getValue());
doSomething(someObject.getValue());
} catch (BadThingsHappenException bthe) {
// consider this a RuntimeException wrapper class
throw new UnhandledException(MyDebugUtils.getContextString(), bthe);
} finally {
MyDebugUtils.clearContext();
}
There might be some issues that you would want to iron out, such as handling cases where your doSomething method also contains a try/catch/finally set that clears the debug context. This could be handled by allowing for finer granularity in the context Map than just the Thread in the process:
public static void setValue(Object contextID, Object key, Object value)
public static void clearContext(Object contextID)
public static String getContextString(Object contextID)
One option that no one seems to have mentioned yet is to use a logger that logs to an in memory buffer, and only pushes the information into the actual log target under certain circumstances (e.g., an error level message is logged).
If you're using the JDK 1.4 logging facilities, MemoryHandler does exactly this. I'm not sure if the logging system you're using does this, but I imagine you should be able to implement your own appender/handler/whatever that does something similar.
Also, I just want to point out that in your original example, if your concern is variable scope, you could always define a block to reduce the scope of your variable:
{
String myValue = null;
try {
myValue = someObject.getValue();
doSomething(myValue);
}
catch (BadThingsHappenException bthe) {
String pattern = "An error occurred when setting value. [value={}]";
// note that the format method below doesn't barf on nulls
String detail = MessageFormatter.format(pattern, myValue);
// consider this a RuntimeException wrapper class
throw new UnhandledException(detail, bthe);
}
}
Why not keep a local copy/list of all messages that would have gone to the debug log if it was enabled, and pass that to the custom exception when you throw it? Something like:
static void logDebug(String message, List<String> msgs) {
msgs.add(message);
log.debug(message);
}
//...
try {
List<String> debugMsgs = new ArrayList<String>();
String myValue = someObject.getValue();
logDebug("Value: " + myValue, debugMsgs);
doSomething(myValue);
int x = doSomething2();
logDebug("doSomething2() returned " + x, debugMsgs);
}
catch (BadThingsHappenException bthe) {
// at the point when the exception is caught,
// debugMsgs contains some or all of the messages
// which should have gone to the debug log
throw new UnhandledException(bthe, debugMsgs);
}
Your exception class can make use of this List parameter when forming getMessage():
public class UnhandledException extends Exception {
private List<String> debugMessages;
public UnhandledException(String message, List<String> debugMessages) {
super(message);
this.debugMessages = debugMessages;
}
#Override
public String getMessage() {
//return concatentation of super.getMessage() and debugMessages
}
}
The usage of this would be tedious - as you'd have to declare the local variable in every single try/catch where you wanted this type of information - but it might be worth it if you have just a few critical sections of code in which you'd like to maintain this state information on an exception.
You answered your own question. If you want to pass the state to the exception, you need to store your state somewhere.
You have mentioned adding extra variables to do this, but didn't like all the extra variables.
Someone else mentioned a MemoryHandler as a buffer (holds state) between the logger and the application.
These are all the same idea. Create an object that will hold the state you want you show in your exception. Update that object as your code executes. If an error occurs pass that object into the exception.
Exceptions already do this with StackTraceElements. Each thread keeps a list of the stack trace (method, file, line) which represents its 'state'. When the exception happens, it passes the stack trace to the exception.
What you seem to be wanting, is a copy of all the local variables also.
This would mean making a object to hold all your locals and using that object, instead of the locals directly. Then passing the object to the exception.
I have created a key combination in eclipse for a catch block creation.
logmsg as key and the result will be:
catch(SomeException se){
String msg = ""; //$NON-NLS-1$
Object[] args = new Object[]{};
throw new SomeException(Message.format(msg, args), se);
}
You can put as many informations as you want in the Message like:
msg = "Dump:\n varA({0}), varB({1}), varC({2}), varD({3})";
args = new Object[]{varA, varB, varC, varD};
Or some user information:
msg = "Please correct the SMTP client because ({0}) seems to be wrong";
args = new Object[]{ smptClient };
You should think about using log4j as a logger, so you can print your states where ever you want. With the options DEBUG, INFO, ERROR you can define how many loggings you want to see in your log file.
When you deliver your application you will set the log level to ERROR, but when you want to debug your application you can use DEBUG as default.
When you are using a logger, you only have to print a hand full of information in your exception, because the state of some variables you would print into the log file before you are calling the critical try...catch block.
String msg = "Dump:\n varA({0}), varB({1}), varC({2}), varD({3})";
Object[] args = new Object[]{varA, varB, varC, varD};
logger.debug(Message.format(msg, args));
try{
// do action
}catch(ActionException ae){
msg = "Please correct the SMTP client because ({0}) seems to be wrong";
args = new Object[]{ smptClient };
logger.error(Message.format(msg, args), se);
throw new SomeException(Message.format(msg, args), se);
}
If you want to somehow process the details of the error message, you could:
Use an XML text as the message, so you get a structured way:
throw new UnhandledException(String.format(
"<e><m>Unexpected things</m><value>%s</value></e>", value), bthe);
Use your own (and one for every case) exception types to capture variable information into named properties:
throw new UnhandledValueException("Unexpected value things", value, bthe);
Or else you could include it in the raw message, as suggested by others.
As for the type of debug information you need, why don't you just always log the value and don't bother so much with a local try/catch. Just use the Log4J config file to point your debug messages to a different log, or use chainsaw so you can remotely follow the log messages. If all that fails maybe you need a new log message type to add to debug()/info()/warn()/error()/fatal() so you have more control over which messages get sent where. This would be the case when defining appenders in the log4j config file is impractical due to the high number of places where this type of debug logging needs to be inserted.
While we're on the subject, you've touched on one of my pet peeves. Constructing a new exception in the catch block is a code smell.
Catch(MyDBException eDB)
{
throw new UnhandledException("Something bad happened!", eDB);
}
Put the message in the log and then rethrow the exception. Constructing Exceptions is expensive and can easily hide useful debugging information.
First off, inexperienced coders and those who like to cut-n-paste (or begin-mark-bug, end-mark-bug, copy-bug, copy-bug, copy-bug) it can transform easily to this:
Catch(MyDBException eDB)
{
throw new UnhandledException("Something bad happened!");
}
Now you've lost the original stacktrace. Even in the first case, unless the wrapping Exception handles the wrapped exception properly, you can still lose details of the original exception, the stacktrace being the most likely.
Rethrowing exceptions might be necessary but I've found that it should be handled more generally and as a strategy to communicate between layers, like between your business code and the persistance layer, like so:
Catch(SqlException eDB)
{
throw new UnhandledAppException("Something bad happened!", eDB);
}
and in this case, the catch block for the UnhandledAppException is much further up the call stack where we can give the user an indication that they either need to retry their action, report a bug, or whatever.
This let our main() code do something like this
catch(UnhandledAppException uae)
{
\\notify user
\\log exception
}
catch(Throwable tExcp)
{
\\for all other unknown failures
\\log exception
}
finally
{
\\die gracefully
}
Doing it this way meant that local code could catch the immediate and recoverable exceptions where debug logs could be done and the exception not have to be rethrown. This would be like for DivideByZero or maybe a ParseException of some sort.
As for "throws" clauses, having a layer-based exception strategy meant being able to limit the number of exception types that have to be listed for each method.