Skip to content

Commit

Permalink
improved exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
hohwille committed Oct 3, 2024
1 parent 8cf9711 commit d490f38
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public ApplicationException(Localizable message) {
* The constructor.
*
* @param message the {@link #getMessage() message} describing the problem briefly.
* @param cause is the {@link #getCause() cause} of this exception.
* @param cause the {@link #getCause() cause} of this exception.
*/
public ApplicationException(String message, Throwable cause) {

Expand All @@ -67,8 +67,8 @@ public ApplicationException(String message, Throwable cause) {
/**
* The constructor.
*
* @param message the {@link #getLocalizedMessage() message} describing the problem briefly.
* @param cause is the {@link #getCause() cause} of this exception.
* @param message the {@link #getNlsMessage() NLS message} describing the problem briefly.
* @param cause the {@link #getCause() cause} of this exception.
*/
public ApplicationException(Localizable message, Throwable cause) {

Expand All @@ -79,7 +79,7 @@ public ApplicationException(Localizable message, Throwable cause) {
* The constructor.
*
* @param message the {@link #getMessage() message} describing the problem briefly.
* @param cause is the {@link #getCause() cause} of this exception. May be <code>null</code>.
* @param cause the {@link #getCause() cause} of this exception. May be <code>null</code>.
* @param uuid the explicit {@link #getUuid() UUID} or <code>null</code> to initialize by default (from given
* {@link Throwable} or as new {@link UUID}).
*/
Expand All @@ -91,8 +91,8 @@ protected ApplicationException(String message, Throwable cause, UUID uuid) {
/**
* The constructor.
*
* @param message the {@link #getLocalizedMessage() message} describing the problem briefly.
* @param cause is the {@link #getCause() cause} of this exception. May be <code>null</code>.
* @param message the {@link #getNlsMessage() NLS message} describing the problem briefly.
* @param cause the {@link #getCause() cause} of this exception. May be <code>null</code>.
* @param uuid the explicit {@link #getUuid() UUID} or <code>null</code> to initialize by default (from given
* {@link Throwable} or as new {@link UUID}).
*/
Expand Down Expand Up @@ -136,18 +136,28 @@ public final UUID getUuid() {
return this.uuid;
}

/**
* ATTENTION: Logging frameworks like logback do not follow Java exception standards and write {@link #toString()} but
* only {@link #getMessage()} of the {@link Exception} together with the stacktrace into the log. Since an
* {@link ApplicationException} contains additional information like {@link #getUuid() UUID} and {@link #getCode()
* code} that needs to be logged we are forced to return the message from this method in the following form:
*
* <pre>[{@link #getCode() «code»}: ]{@link #getNlsMessage() «message»}
* «uuid»
* </pre>
*
* Please note that the {@link #getCode() code} is omitted if the {@link #getCode()} method is not overridden and
* returning a custom code.<br>
* In case you want to get the plain exception message, you therefore need to either call
* {@link #getLocalizedMessage()} or use {@link #getNlsMessage()}.
*
* @return the untranslated message in the form specified above.
*/
@Override
public String getMessage() {

StringBuilder buffer = new StringBuilder();
getLocalizedMessage(Locale.ROOT, buffer);
buffer.append(System.lineSeparator());
buffer.append(this.uuid);
String code = getCode();
if (!getClass().getSimpleName().equals(code)) {
buffer.append(":");
buffer.append(code);
}
toString(Locale.ROOT, buffer, true);
return buffer.toString();
}

Expand All @@ -161,6 +171,12 @@ public Localizable getNlsMessage() {
return this.message;
}

@Override
public String getLocalizedMessage() {

return getLocalizedMessage(Locale.getDefault());
}

@Override
public String getLocalizedMessage(Locale locale) {

Expand Down Expand Up @@ -196,22 +212,14 @@ private static void printStackTrace(ApplicationException throwable, Locale local

try {
synchronized (buffer) {
buffer.append(throwable.getClass().getName());
buffer.append(": ");
throwable.getLocalizedMessage(locale, buffer);
buffer.append(System.lineSeparator());
UUID uuid = throwable.getUuid();
if (uuid != null) {
buffer.append(uuid.toString());
buffer.append(System.lineSeparator());
}
throwable.toString(locale, buffer);
StackTraceElement[] trace = throwable.getStackTrace();
for (int i = 0; i < trace.length; i++) {
buffer.append("\tat ");
buffer.append(trace[i].toString());
buffer.append(System.lineSeparator());
}
for (Throwable suppressed : ((Throwable) throwable).getSuppressed()) {
for (Throwable suppressed : throwable.getSuppressed()) {
buffer.append("Suppressed: ");
buffer.append(System.lineSeparator());
printStackTraceCause(suppressed, locale, buffer);
Expand Down Expand Up @@ -342,14 +350,21 @@ public String toString(Locale locale) {
*/
public Appendable toString(Locale locale, Appendable appendable) {

return toString(locale, appendable, false);
}

private Appendable toString(Locale locale, Appendable appendable, boolean omitClass) {

Appendable buffer = appendable;
if (buffer == null) {
buffer = new StringBuilder(32);
}
try {
Class<?> myClass = getClass();
buffer.append(myClass.getName());
buffer.append(": ");
if (!omitClass) {
buffer.append(myClass.getName());
buffer.append(": ");
}
String code = getCode();
if (!myClass.getSimpleName().equals(code)) {
buffer.append(code);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
* http://www.apache.org/licenses/LICENSE-2.0 */
package io.github.mmm.base.exception;

import java.util.Collection;

import io.github.mmm.base.i18n.Localizable;

/**
Expand All @@ -24,7 +26,7 @@ public class ObjectNotFoundException extends ApplicationException {
/**
* The constructor.
*
* @param object is a description (e.g. the classname) of the object that was required but could not be found.
* @param object the description (e.g. the classname) of the object that was required but could NOT be found.
*/
public ObjectNotFoundException(Object object) {

Expand All @@ -34,8 +36,8 @@ public ObjectNotFoundException(Object object) {
/**
* The constructor.
*
* @param object is a description (e.g. the classname) of the object that was required but could not be found.
* @param key is the key to the required object.
* @param object the description (e.g. the classname) of the object that was required but could NOT be found.
* @param key the key to the required object.
*/
public ObjectNotFoundException(Object object, Object key) {

Expand All @@ -45,16 +47,30 @@ public ObjectNotFoundException(Object object, Object key) {
/**
* The constructor.
*
* @param object is a description (e.g. the classname) of the object that was required but could NOT be found.
* @param key is the key to the required object.
* @param cause is the {@link #getCause() cause} of this exception.
* @param object the description (e.g. the classname) of the object that was required but could NOT be found.
* @param key the key to the required object.
* @param cause the {@link #getCause() cause} of this exception.
*/
public ObjectNotFoundException(Object object, Object key, Throwable cause) {

super(createMessage(object, key), cause);
this(object, key, null, cause);
}

private static String createMessage(Object object, Object key) {
/**
* The constructor.
*
* @param object the description (e.g. the classname) of the object that was required but could NOT be found.
* @param key the key to the required object.
* @param options the available options (e.g. {@link Collection} of comma separated {@link String} of the available
* keys).
* @param cause the {@link #getCause() cause} of this exception.
*/
public ObjectNotFoundException(Object object, Object key, Object options, Throwable cause) {

super(createMessage(object, key, options), cause);
}

private static String createMessage(Object object, Object key, Object options) {

StringBuilder sb = new StringBuilder("Could not find ");
sb.append(object);
Expand All @@ -64,15 +80,18 @@ private static String createMessage(Object object, Object key) {
sb.append(key);
sb.append('\'');
}
sb.append(".");
if (options != null) {
sb.append(" in ");
sb.append(options);
}
return sb.toString();
}

/**
* The constructor.
*
* @param message the {@link #getNlsMessage() NLS message}.
* @param cause is the {@link #getCause() cause} of this exception. May be <code>null</code>.
* @param cause the {@link #getCause() cause} of this exception. May be <code>null</code>.
*/
protected ObjectNotFoundException(Localizable message, Throwable cause) {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.github.mmm.base.exception;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Locale;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

/**
* Test of {@link ApplicationException}.
*/
public class ApplicationExceptionTest extends Assertions {

@Test
public void testWithCode() {

// arrange
String message = "Something went wrong.";
String code = "MagicCode";
// act
ApplicationException exception = new ApplicationException(message) {
@Override
public String getCode() {

return code;
}
};
StringWriter sw = new StringWriter(1024);
PrintWriter pw = new PrintWriter(sw);
exception.printStackTrace(pw);
String stackTrace = sw.toString();
// assert
assertThat(exception.getLocalizedMessage()).isEqualTo(message);
assertThat(exception.getLocalizedMessage(Locale.ROOT)).isEqualTo(message);
assertThat(exception.getNlsMessage().getMessage()).isEqualTo(message);
String msg = code + ": " + message + System.lineSeparator() + exception.getUuid();
assertThat(exception.getMessage()).isEqualTo(msg);
String toString = exception.getClass().getName() + ": " + msg;
assertThat(exception.toString()).isEqualTo(toString);
assertThat(stackTrace).startsWith(toString)
.contains("io.github.mmm.base.exception.ApplicationExceptionTest.testWithCode(");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.github.mmm.base.exception;

import java.util.Collection;
import java.util.List;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

/**
* Test of {@link ObjectNotFoundException}.
*/
public class ObjectNotFoundExceptionTest extends Assertions {

@Test
public void testSimple() {

// arrange
String name = "io.github.mmm.UndefinedObject";
// act
ObjectNotFoundException exception = new ObjectNotFoundException(name);
// assert
assertThat(exception.getLocalizedMessage()).isEqualTo("Could not find " + name);
}

@Test
public void testWithKey() {

// arrange
String name = "Entity";
String key = "MagicKey";
// act
ObjectNotFoundException exception = new ObjectNotFoundException(name, key);
// assert
assertThat(exception.getLocalizedMessage()).isEqualTo("Could not find " + name + " for key '" + key + "'");
}

@Test
public void testWithKeyAndOptions() {

// arrange
String name = "Entity";
String key = "MagicKey";
Collection<String> options = List.of("NormalKey", "Key", "HolyKey");
// act
ObjectNotFoundException exception = new ObjectNotFoundException(name, key, options, null);
// assert
assertThat(exception.getLocalizedMessage())
.isEqualTo("Could not find " + name + " for key '" + key + "' in [NormalKey, Key, HolyKey]");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public void testGetRequired() {
metaInfo.getRequired("key2");
failBecauseExceptionWasNotThrown(ObjectNotFoundException.class);
} catch (ObjectNotFoundException e) {
assertThat(e).hasMessageContaining("Could not find MetaInfo-value for key 'key2'.");
assertThat(e).hasMessageContaining("Could not find MetaInfo-value for key 'key2'");
}
}

Expand Down Expand Up @@ -167,7 +167,7 @@ public void testWithPrefix() {
metaInfo.getRequired("key2");
failBecauseExceptionWasNotThrown(ObjectNotFoundException.class);
} catch (ObjectNotFoundException e) {
assertThat(e.getNlsMessage().getMessage()).isEqualTo("Could not find MetaInfo-value for key 'prefix.key2'.");
assertThat(e.getNlsMessage().getMessage()).isEqualTo("Could not find MetaInfo-value for key 'prefix.key2'");
}

// and act
Expand Down

0 comments on commit d490f38

Please sign in to comment.