diff --git a/src/main/java/org/apache/commons/lang3/exception/ContextedException.java b/src/main/java/org/apache/commons/lang3/exception/ContextedException.java index e7227b821..5f99488f4 100644 --- a/src/main/java/org/apache/commons/lang3/exception/ContextedException.java +++ b/src/main/java/org/apache/commons/lang3/exception/ContextedException.java @@ -16,19 +16,23 @@ */ package org.apache.commons.lang3.exception; +import java.util.List; import java.util.Set; +import org.apache.commons.lang3.tuple.Pair; + /** *

- * An exception that provides an easy and safe way to add contextual information. + * An exception that provides an easy and safe way to add contextual + * information. *

* An exception trace itself is often insufficient to provide rapid diagnosis of the issue. * Frequently what is needed is a select few pieces of local contextual data. * Providing this data is tricky however, due to concerns over formatting and nulls. *

* The contexted exception approach allows the exception to be created together with a - * map of context values. This additional information is automatically included in the - * message and printed stack trace. + * list of context label-value pairs. This additional information is automatically included in + * the message and printed stack trace. *

* An unchecked version of this exception is provided by ContextedRuntimeException. *

@@ -40,9 +44,9 @@ import java.util.Set; * ... * } catch (Exception e) { * throw new ContextedException("Error posting account transaction", e) - * .addValue("accountNumber", accountNumber) - * .addValue("amountPosted", amountPosted) - * .addValue("previousBalance", previousBalance) + * .addContextValue("accountNumber", accountNumber) + * .addContextValue("amountPosted", amountPosted) + * .addContextValue("previousBalance", previousBalance) * } * } * @@ -67,7 +71,7 @@ import java.util.Set; public class ContextedException extends Exception implements ExceptionContext { /** The serialization version. */ - private static final long serialVersionUID = 8940917952810290164L; + private static final long serialVersionUID = 20110706L; /** The context where the data is stored. */ private final ExceptionContext exceptionContext; @@ -134,59 +138,105 @@ public class ContextedException extends Exception implements ExceptionContext { } //----------------------------------------------------------------------- + /** * Adds information helpful to a developer in diagnosing and correcting * the problem. For the information to be meaningful, the value passed - * should have a reasonable toString() implementation. If the added label - * is already available, the label is appended with an index. + * should have a reasonable toString() implementation. Different values + * can be added with the same label multiple times. *

* Note: This exception is only serializable if the object added is serializable. *

* - * @param label a textual label associated with information, null not recommended - * @param value information needed to understand exception, may be null + * @param label a textual label associated with information, {@code null} not recommended + * @param value information needed to understand exception, may be {@code null} * @return this, for method chaining */ - public ContextedException addValue(String label, Object value) { - exceptionContext.addValue(label, value); + public ContextedException addContextValue(String label, Object value) { + exceptionContext.addContextValue(label, value); return this; } /** - * Replaces information helpful to a developer in diagnosing and correcting + * Adds information helpful to a developer in diagnosing and correcting * the problem. For the information to be meaningful, the value passed - * should have a reasonable toString() implementation. If the replaced - * label does not yet exist, it is simply added. + * should have a reasonable toString() implementation. Different values + * can be added with the same label multiple times. *

- * Note: This exception is only serializable if the object added is serializable. + * Note: This exception is only serializable if the object added as value is serializable. *

* - * @param label a textual label associated with information, null not recommended - * @param value information needed to understand exception, may be null + * @param pair a pair of textual label and information + * @throws NullPointerException if {@code pair} is {@code null} * @return this, for method chaining */ - public ContextedException replaceValue(String label, Object value) { - exceptionContext.replaceValue(label, value); + public ContextedException addContextValue(Pair pair) { + this.exceptionContext.addContextValue(pair); return this; } /** - * Retrieves a contextual data value associated with the label. + * Set information helpful to a developer in diagnosing and correcting + * the problem. For the information to be meaningful, the value passed + * should have a reasonable toString() implementation. Existing values + * with the same labels are removed before the new one is added. + *

+ * Note: This exception is only serializable if the object added as value is serializable. + *

* - * @param label the label to get the contextual value for, may be null - * @return the contextual value associated with the label, may be null + * @param label a textual label associated with information, {@code null} not recommended + * @param value information needed to understand exception, may be {@code null} + * @return this, for method chaining */ - public Object getValue(String label) { - return exceptionContext.getValue(label); + public ContextedException setContextValue(String label, Object value) { + exceptionContext.setContextValue(label, value); + return this; + } + + /** + * Set information helpful to a developer in diagnosing and correcting + * the problem. For the information to be meaningful, the value passed + * should have a reasonable toString() implementation. Existing values + * with the same labels are removed before the new one is added. + *

+ * Note: This exception is only serializable if the object added as value is serializable. + *

+ * + * @param pair a pair of textual label and information + * @throws NullPointerException if {@code pair} is {@code null} + * @return this, for method chaining + */ + public ContextedException setContextValue(Pair pair) { + this.exceptionContext.setContextValue(pair); + return this; } /** - * Retrieves the labels defined in the contextual data. - * - * @return the set of labels, never null + * {@inheritDoc} */ - public Set getLabelSet() { - return exceptionContext.getLabelSet(); + public List getContextValues(String label) { + return this.exceptionContext.getContextValues(label); + } + + /** + * {@inheritDoc} + */ + public Object getFirstContextValue(String label) { + return this.exceptionContext.getFirstContextValue(label); + } + + /** + * {@inheritDoc} + */ + public List> getContextEntries() { + return this.exceptionContext.getContextEntries(); + } + + /** + * {@inheritDoc} + */ + public Set getContextLabels() { + return exceptionContext.getContextLabels(); } /** diff --git a/src/main/java/org/apache/commons/lang3/exception/ContextedRuntimeException.java b/src/main/java/org/apache/commons/lang3/exception/ContextedRuntimeException.java index 093d9749d..e0c5436f0 100644 --- a/src/main/java/org/apache/commons/lang3/exception/ContextedRuntimeException.java +++ b/src/main/java/org/apache/commons/lang3/exception/ContextedRuntimeException.java @@ -16,8 +16,11 @@ */ package org.apache.commons.lang3.exception; +import java.util.List; import java.util.Set; +import org.apache.commons.lang3.tuple.Pair; + /** *

* A runtime exception that provides an easy and safe way to add contextual information. @@ -27,8 +30,8 @@ import java.util.Set; * Providing this data is tricky however, due to concerns over formatting and nulls. *

* The contexted exception approach allows the exception to be created together with a - * map of context values. This additional information is automatically included in the - * message and printed stack trace. + * list of context label-value pairs. This additional information is automatically included in + * the message and printed stack trace. *

* An checked version of this exception is provided by ContextedException. *

@@ -40,9 +43,9 @@ import java.util.Set; * ... * } catch (Exception e) { * throw new ContextedException("Error posting account transaction", e) - * .addValue("accountNumber", accountNumber) - * .addValue("amountPosted", amountPosted) - * .addValue("previousBalance", previousBalance) + * .addContextValue("accountNumber", accountNumber) + * .addContextValue("amountPosted", amountPosted) + * .addContextValue("previousBalance", previousBalance) * } * } * @@ -67,7 +70,7 @@ import java.util.Set; public class ContextedRuntimeException extends RuntimeException implements ExceptionContext { /** The serialization version. */ - private static final long serialVersionUID = 1459691936045811817L; + private static final long serialVersionUID = 20110706L; /** The context where the data is stored. */ private final ExceptionContext exceptionContext; @@ -120,6 +123,9 @@ public class ContextedRuntimeException extends RuntimeException implements Excep /** * Instantiates ContextedRuntimeException with cause, message, and ExceptionContext. + *

+ * Note: This exception is only serializable if the object added is serializable. + *

* * @param message the exception message, may be null * @param cause the underlying cause of the exception, may be null @@ -134,59 +140,105 @@ public class ContextedRuntimeException extends RuntimeException implements Excep } //----------------------------------------------------------------------- + /** * Adds information helpful to a developer in diagnosing and correcting * the problem. For the information to be meaningful, the value passed - * should have a reasonable toString() implementation. If the added label - * is already available, the label is appended with an index. + * should have a reasonable toString() implementation. Different values + * can be added with the same label multiple times. *

* Note: This exception is only serializable if the object added is serializable. *

* - * @param label a textual label associated with information, null not recommended - * @param value information needed to understand exception, may be null + * @param label a textual label associated with information, {@code null} not recommended + * @param value information needed to understand exception, may be {@code null} * @return this, for method chaining */ - public ContextedRuntimeException addValue(String label, Object value) { - exceptionContext.addValue(label, value); + public ContextedRuntimeException addContextValue(String label, Object value) { + exceptionContext.addContextValue(label, value); return this; } /** - * Replaces information helpful to a developer in diagnosing and correcting + * Adds information helpful to a developer in diagnosing and correcting * the problem. For the information to be meaningful, the value passed - * should have a reasonable toString() implementation. If the replaced - * label does not yet exist, it is simply added. + * should have a reasonable toString() implementation. Different values + * can be added with the same label multiple times. *

- * Note: This exception is only serializable if the object added is serializable. + * Note: This exception is only serializable if the object added as value is serializable. *

* - * @param label a textual label associated with information, null not recommended - * @param value information needed to understand exception, may be null + * @param pair a pair of textual label and information + * @throws NullPointerException if {@code pair} is {@code null} * @return this, for method chaining */ - public ContextedRuntimeException replaceValue(String label, Object value) { - exceptionContext.replaceValue(label, value); + public ContextedRuntimeException addContextValue(Pair pair) { + this.exceptionContext.addContextValue(pair); return this; } /** - * Retrieves a contextual data value associated with the label. + * Set information helpful to a developer in diagnosing and correcting + * the problem. For the information to be meaningful, the value passed + * should have a reasonable toString() implementation. Existing values + * with the same labels are removed before the new one is added. + *

+ * Note: This exception is only serializable if the object added as value is serializable. + *

* - * @param label the label to get the contextual value for, may be null - * @return the contextual value associated with the label, may be null + * @param label a textual label associated with information, {@code null} not recommended + * @param value information needed to understand exception, may be {@code null} + * @return this, for method chaining */ - public Object getValue(String label) { - return exceptionContext.getValue(label); + public ContextedRuntimeException setContextValue(String label, Object value) { + exceptionContext.setContextValue(label, value); + return this; + } + + /** + * Set information helpful to a developer in diagnosing and correcting + * the problem. For the information to be meaningful, the value passed + * should have a reasonable toString() implementation. Existing values + * with the same labels are removed before the new one is added. + *

+ * Note: This exception is only serializable if the object added as value is serializable. + *

+ * + * @param pair a pair of textual label and information + * @throws NullPointerException if {@code pair} is {@code null} + * @return this, for method chaining + */ + public ContextedRuntimeException setContextValue(Pair pair) { + this.exceptionContext.setContextValue(pair); + return this; } /** - * Retrieves the labels defined in the contextual data. - * - * @return the set of labels, never null + * {@inheritDoc} */ - public Set getLabelSet() { - return exceptionContext.getLabelSet(); + public List getContextValues(String label) { + return this.exceptionContext.getContextValues(label); + } + + /** + * {@inheritDoc} + */ + public Object getFirstContextValue(String label) { + return this.exceptionContext.getFirstContextValue(label); + } + + /** + * {@inheritDoc} + */ + public List> getContextEntries() { + return this.exceptionContext.getContextEntries(); + } + + /** + * {@inheritDoc} + */ + public Set getContextLabels() { + return exceptionContext.getContextLabels(); } /** diff --git a/src/main/java/org/apache/commons/lang3/exception/DefaultExceptionContext.java b/src/main/java/org/apache/commons/lang3/exception/DefaultExceptionContext.java index 6944433e7..31c887bf9 100644 --- a/src/main/java/org/apache/commons/lang3/exception/DefaultExceptionContext.java +++ b/src/main/java/org/apache/commons/lang3/exception/DefaultExceptionContext.java @@ -17,11 +17,15 @@ package org.apache.commons.lang3.exception; import java.io.Serializable; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; import java.util.Set; -import org.apache.commons.lang3.SystemUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; /** * Default implementation of the context storing the label-value pairs for contexted exceptions. @@ -31,73 +35,93 @@ import org.apache.commons.lang3.SystemUtils; * * @since 3.0 */ -class DefaultExceptionContext implements ExceptionContext, Serializable { +public class DefaultExceptionContext implements ExceptionContext, Serializable { /** The serialization version. */ - private static final long serialVersionUID = 293747957535772807L; - /** The ordered map storing the label-data pairs. */ - private Map contextValueMap = new LinkedHashMap(); + private static final long serialVersionUID = 20110706L; + /** The list storing the label-data pairs. */ + private List> contextValues = new ArrayList>(); /** - * Adds a contextual label-value pair into this context. - *

- * This label-value pair provides information useful for debugging. If the - * label already exists and the provided information is different, the - * label will be added with an appended index. - *

- * - * @param label the label of the item to add, null not recommended - * @param value the value of item to add, may be null - * @return this, for method chaining + * {@inheritDoc} */ - public ExceptionContext addValue(String label, Object value) { - String key = label; - int i = 0; - while (contextValueMap.containsKey(key)) { - Object information = contextValueMap.get(key); - if ((value == null && information == null) - || (value != null && value.equals(information))) { - return this; - } - key = label + "[" + ++i +"]"; + public DefaultExceptionContext addContextValue(String label, Object value) { + return addContextValue(new ImmutablePair(label, value)); + } + + /** + * {@inheritDoc} + */ + public DefaultExceptionContext addContextValue(Pair pair) { + if (pair == null) { + throw new NullPointerException(); } - contextValueMap.put(key, value); + contextValues.add(pair); return this; } /** - * Replaces a contextual label-value pair of this context. - *

- * This label-value pair provides information useful for debugging. If the - * label does not yet exists, a simply add operation is performed. - *

- * - * @param label the label of the item to add, null not recommended - * @param value the value of item to add, may be null - * @return this, for method chaining + * {@inheritDoc} */ - public ExceptionContext replaceValue(String label, Object value) { - contextValueMap.put(label, value); - return this; + public DefaultExceptionContext setContextValue(String label, Object value) { + return setContextValue(new ImmutablePair(label, value)); } /** - * Retrieves a contextual data value associated with the label. - * - * @param label the label to get the contextual value for, may be null - * @return the contextual value associated with the label, may be null + * {@inheritDoc} */ - public Object getValue(String label) { - return contextValueMap.get(label); + public DefaultExceptionContext setContextValue(Pair pair) { + final String label = pair.getKey(); // implicit NPE + for (final Iterator> iter = contextValues.iterator(); iter.hasNext();) { + final Pair p = iter.next(); + if (StringUtils.equals(label, p.getKey())) { + iter.remove(); + } + } + return addContextValue(pair); } /** - * Retrieves the labels defined in the contextual data. - * - * @return the set of labels, never null + * {@inheritDoc} */ - public Set getLabelSet() { - return contextValueMap.keySet(); + public List getContextValues(String label) { + final List values = new ArrayList(); + for (final Pair pair : contextValues) { + if (StringUtils.equals(label, pair.getKey())) { + values.add(pair.getValue()); + } + } + return values; + } + + /** + * {@inheritDoc} + */ + public Object getFirstContextValue(String label) { + for (final Pair pair : contextValues) { + if (StringUtils.equals(label, pair.getKey())) { + return pair.getValue(); + } + } + return null; + } + + /** + * {@inheritDoc} + */ + public Set getContextLabels() { + final Set labels = new HashSet(); + for (final Pair pair : contextValues) { + labels.add(pair.getKey()); + } + return labels; + } + + /** + * {@inheritDoc} + */ + public List> getContextEntries() { + return contextValues; } /** @@ -112,21 +136,19 @@ class DefaultExceptionContext implements ExceptionContext, Serializable { buffer.append(baseMessage); } - if (contextValueMap.size() > 0) { - if (buffer.length() > 0l) { - buffer.append(SystemUtils.LINE_SEPARATOR); + if (contextValues.size() > 0) { + if (buffer.length() > 0) { + buffer.append('\n'); } - buffer.append("Exception Context:"); - buffer.append(SystemUtils.LINE_SEPARATOR); - buffer.append("\t"); + buffer.append("Exception Context:\n"); Object value; String valueStr; - for (String label : contextValueMap.keySet()) { - buffer.append("["); - buffer.append(label); + for (final Pair pair : contextValues) { + buffer.append("\t["); + buffer.append(pair.getKey()); buffer.append("="); - value = this.contextValueMap.get(label); + value = pair.getValue(); if (value == null) { buffer.append("null"); } else { @@ -137,13 +159,10 @@ class DefaultExceptionContext implements ExceptionContext, Serializable { } buffer.append(valueStr); } - buffer.append("]"); - buffer.append(SystemUtils.LINE_SEPARATOR); - buffer.append("\t"); + buffer.append("]\n"); } buffer.append("---------------------------------"); } return buffer.toString(); } - } diff --git a/src/main/java/org/apache/commons/lang3/exception/ExceptionContext.java b/src/main/java/org/apache/commons/lang3/exception/ExceptionContext.java index 2fe74bf50..ce9323240 100644 --- a/src/main/java/org/apache/commons/lang3/exception/ExceptionContext.java +++ b/src/main/java/org/apache/commons/lang3/exception/ExceptionContext.java @@ -16,11 +16,18 @@ */ package org.apache.commons.lang3.exception; +import java.util.List; import java.util.Set; +import org.apache.commons.lang3.tuple.Pair; + /** * Allows the storage and retrieval of contextual information based on label-value * pairs for exceptions. + *

+ * Implementations are expected to manage the pairs in a list-style collection + * that keeps the pairs in the sequence of their addition. + *

* * @see ContextedException * @see ContextedRuntimeException @@ -31,52 +38,91 @@ public interface ExceptionContext { /** * Adds a contextual label-value pair into this context. *

- * This label-value pair provides information useful for debugging. If the - * provided label already exists, it depends on the implementation what - * happens with the new value. + * The pair will be added to the context, independently of an already + * existing pair with the same label. *

* - * @param label the label of the item to add, null not recommended - * @param value the value of item to add, may be null + * @param label the label of the item to add, {@code null} not recommended + * @param value the value of item to add, may be {@code null} * @return context itself to allow method chaining */ - public ExceptionContext addValue(String label, Object value); + public ExceptionContext addContextValue(String label, Object value); /** - * Replaces a contextual label-value pair of this context. + * Adds a contextual label-value pair into this context. *

- * This label-value pair provides information useful for debugging. If the - * label does not exist yet, it depends on the implementation what happens - * with the provided value. + * The pair will be added to the context, independently of an already + * existing pair with the same label. *

* - * @param label the label of the item to add, null not recommended - * @param value the value of item to add, may be null + * @param pair the label-value pair to add * @return context itself to allow method chaining + * @throws NullPointerException if pair is {@code null} */ - public ExceptionContext replaceValue(String label, Object value); + public ExceptionContext addContextValue(Pair pair); /** - * Retrieves a contextual data value associated with the label. + * Sets a contextual label-value pair of this context. + *

+ * The pair will be added normally, but any existing label-value pair with + * the same label is removed from the context. + *

* - * @param label the label to get the contextual value for, may be null - * @return the contextual value associated with the label, may be null + * @param label the label of the item to add, {@code null} not recommended + * @param value the value of item to add, may be {@code null} + * @return context itself to allow method chaining */ - public Object getValue(String label); + public ExceptionContext setContextValue(String label, Object value); + + /** + * Sets a contextual label-value pair of this context. + *

+ * The pair will be added normally, but any existing label-value pair with + * the same label is removed from the context. + *

+ * + * @param pair the label-value pair to add + * @return context itself to allow method chaining + * @throws NullPointerException if pair is {@code null} + */ + public ExceptionContext setContextValue(Pair pair); + + /** + * Retrieves contextual data values associated with the label. + * + * @param label the label to get the contextual values for, may be {@code null} + * @return the contextual values associated with the label, never {@code null} + */ + public List getContextValues(String label); + + /** + * Retrieves the first available contextual data value associated with the label. + * + * @param label the label to get the contextual value for, may be {@code null} + * @return the first contextual value associated with the label, may be {@code null} + */ + public Object getFirstContextValue(String label); /** * Retrieves the labels defined in the contextual data. * - * @return the set of labels, never null + * @return the set of labels, never {@code null} */ - public Set getLabelSet(); + public Set getContextLabels(); + + /** + * Retrieves the label-value pairs defined in the contextual data. + * + * @return the list of pairs, never {@code null} + */ + public List> getContextEntries(); /** * Implementors provide the given base message with context label/value item * information appended. * * @param baseMessage the base exception message without context information appended - * @return the exception message with context information appended, never null + * @return the exception message with context information appended, never {@code null} */ public String getFormattedExceptionMessage(String baseMessage); diff --git a/src/test/java/org/apache/commons/lang3/exception/AbstractExceptionContextTest.java b/src/test/java/org/apache/commons/lang3/exception/AbstractExceptionContextTest.java new file mode 100644 index 000000000..794410a68 --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/exception/AbstractExceptionContextTest.java @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang3.tuple.Pair; + +import junit.framework.TestCase; + + +/** + * Abstract test of an ExceptionContext implementation. + */ +public abstract class AbstractExceptionContextTest extends TestCase { + + protected static final String TEST_MESSAGE_2 = "This is monotonous"; + protected static final String TEST_MESSAGE = "Test Message"; + protected T exceptionContext; + + protected static class ObjectWithFaultyToString implements Serializable { + + private static final long serialVersionUID = 3495843995332310458L; + + @Override + public String toString() { + throw new RuntimeException("Crap"); + } + } + + @Override + protected void setUp() throws Exception { + exceptionContext + .addContextValue("test1", null) + .addContextValue("test2", "some value") + .addContextValue("test Date", new Date()) + .addContextValue("test Nbr", new Integer(5)) + .addContextValue("test Poorly written obj", new ObjectWithFaultyToString()); + } + + public void testAddContextValue() { + String message = exceptionContext.getFormattedExceptionMessage(TEST_MESSAGE); + assertTrue(message.indexOf(TEST_MESSAGE) >= 0); + assertTrue(message.indexOf("test1") >= 0); + assertTrue(message.indexOf("test2") >= 0); + assertTrue(message.indexOf("test Date") >= 0); + assertTrue(message.indexOf("test Nbr") >= 0); + assertTrue(message.indexOf("some value") >= 0); + assertTrue(message.indexOf("5") >= 0); + + assertTrue(exceptionContext.getFirstContextValue("test1") == null); + assertTrue(exceptionContext.getFirstContextValue("test2").equals("some value")); + + assertEquals(5, exceptionContext.getContextLabels().size()); + assertTrue(exceptionContext.getContextLabels().contains("test1")); + assertTrue(exceptionContext.getContextLabels().contains("test2")); + assertTrue(exceptionContext.getContextLabels().contains("test Date")); + assertTrue(exceptionContext.getContextLabels().contains("test Nbr")); + + exceptionContext.addContextValue("test2", "different value"); + assertEquals(5, exceptionContext.getContextLabels().size()); + assertTrue(exceptionContext.getContextLabels().contains("test2")); + + String contextMessage = exceptionContext.getFormattedExceptionMessage(null); + assertTrue(contextMessage.indexOf(TEST_MESSAGE) == -1); + } + + public void testSetContextValue() { + exceptionContext.addContextValue("test2", "different value"); + exceptionContext.setContextValue("test3", "3"); + + String message = exceptionContext.getFormattedExceptionMessage(TEST_MESSAGE); + assertTrue(message.indexOf(TEST_MESSAGE) >= 0); + assertTrue(message.indexOf("test Poorly written obj") >= 0); + assertTrue(message.indexOf("Crap") >= 0); + + assertTrue(exceptionContext.getFirstContextValue("crap") == null); + assertTrue(exceptionContext.getFirstContextValue("test Poorly written obj") instanceof ObjectWithFaultyToString); + + assertEquals(7, exceptionContext.getContextEntries().size()); + assertEquals(6, exceptionContext.getContextLabels().size()); + + assertTrue(exceptionContext.getContextLabels().contains("test Poorly written obj")); + assertTrue(!exceptionContext.getContextLabels().contains("crap")); + + exceptionContext.setContextValue("test Poorly written obj", "replacement"); + + assertEquals(7, exceptionContext.getContextEntries().size()); + assertEquals(6, exceptionContext.getContextLabels().size()); + + exceptionContext.setContextValue("test2", "another"); + + assertEquals(6, exceptionContext.getContextEntries().size()); + assertEquals(6, exceptionContext.getContextLabels().size()); + + String contextMessage = exceptionContext.getFormattedExceptionMessage(null); + assertTrue(contextMessage.indexOf(TEST_MESSAGE) == -1); + } + + public void testGetFirstContextValue() { + exceptionContext.addContextValue("test2", "different value"); + + assertTrue(exceptionContext.getFirstContextValue("test1") == null); + assertTrue(exceptionContext.getFirstContextValue("test2").equals("some value")); + assertTrue(exceptionContext.getFirstContextValue("crap") == null); + + exceptionContext.setContextValue("test2", "another"); + + assertTrue(exceptionContext.getFirstContextValue("test2").equals("another")); + } + + public void testGetContextValues() { + exceptionContext.addContextValue("test2", "different value"); + + assertEquals(exceptionContext.getContextValues("test1"), Collections.singletonList(null)); + assertEquals(exceptionContext.getContextValues("test2"), Arrays.asList("some value", "different value")); + + exceptionContext.setContextValue("test2", "another"); + + assertTrue(exceptionContext.getFirstContextValue("test2").equals("another")); + } + + public void testGetContextLabels() { + assertEquals(5, exceptionContext.getContextEntries().size()); + + exceptionContext.addContextValue("test2", "different value"); + + Set labels = exceptionContext.getContextLabels(); + assertEquals(6, exceptionContext.getContextEntries().size()); + assertEquals(5, labels.size()); + assertTrue(labels.contains("test1")); + assertTrue(labels.contains("test2")); + assertTrue(labels.contains("test Date")); + assertTrue(labels.contains("test Nbr")); + } + + public void testGetContextEntries() { + assertEquals(5, exceptionContext.getContextEntries().size()); + + exceptionContext.addContextValue("test2", "different value"); + + List> entries = exceptionContext.getContextEntries(); + assertEquals(6, entries.size()); + assertEquals("test1", entries.get(0).getKey()); + assertEquals("test2", entries.get(1).getKey()); + assertEquals("test Date", entries.get(2).getKey()); + assertEquals("test Nbr", entries.get(3).getKey()); + assertEquals("test Poorly written obj", entries.get(4).getKey()); + assertEquals("test2", entries.get(5).getKey()); + } +} diff --git a/src/test/java/org/apache/commons/lang3/exception/ContextedExceptionTest.java b/src/test/java/org/apache/commons/lang3/exception/ContextedExceptionTest.java index a4a362ab6..2497cad8d 100644 --- a/src/test/java/org/apache/commons/lang3/exception/ContextedExceptionTest.java +++ b/src/test/java/org/apache/commons/lang3/exception/ContextedExceptionTest.java @@ -16,51 +16,50 @@ */ package org.apache.commons.lang3.exception; -import java.io.Serializable; import java.util.Date; -import junit.framework.TestCase; - import org.apache.commons.lang3.StringUtils; /** * JUnit tests for ContextedException. */ -public class ContextedExceptionTest extends TestCase { +public class ContextedExceptionTest extends AbstractExceptionContextTest { - private static final String TEST_MESSAGE_2 = "This is monotonous"; - private static final String TEST_MESSAGE = "Test Message"; - private ContextedException contextedException; + @Override + public void setUp() throws Exception { + exceptionContext = new ContextedException(new Exception(TEST_MESSAGE)); + super.setUp(); + } public void testContextedException() { - contextedException = new ContextedException(); - String message = contextedException.getMessage(); - String trace = ExceptionUtils.getStackTrace(contextedException); + exceptionContext = new ContextedException(); + String message = exceptionContext.getMessage(); + String trace = ExceptionUtils.getStackTrace(exceptionContext); assertTrue(trace.indexOf("ContextedException")>=0); assertTrue(StringUtils.isEmpty(message)); } public void testContextedExceptionString() { - contextedException = new ContextedException(TEST_MESSAGE); - assertEquals(TEST_MESSAGE, contextedException.getMessage()); + exceptionContext = new ContextedException(TEST_MESSAGE); + assertEquals(TEST_MESSAGE, exceptionContext.getMessage()); - String trace = ExceptionUtils.getStackTrace(contextedException); + String trace = ExceptionUtils.getStackTrace(exceptionContext); assertTrue(trace.indexOf(TEST_MESSAGE)>=0); } public void testContextedExceptionThrowable() { - contextedException = new ContextedException(new Exception(TEST_MESSAGE)); - String message = contextedException.getMessage(); - String trace = ExceptionUtils.getStackTrace(contextedException); + exceptionContext = new ContextedException(new Exception(TEST_MESSAGE)); + String message = exceptionContext.getMessage(); + String trace = ExceptionUtils.getStackTrace(exceptionContext); assertTrue(trace.indexOf("ContextedException")>=0); assertTrue(trace.indexOf(TEST_MESSAGE)>=0); assertTrue(message.indexOf(TEST_MESSAGE)>=0); } public void testContextedExceptionStringThrowable() { - contextedException = new ContextedException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE)); - String message = contextedException.getMessage(); - String trace = ExceptionUtils.getStackTrace(contextedException); + exceptionContext = new ContextedException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE)); + String message = exceptionContext.getMessage(); + String trace = ExceptionUtils.getStackTrace(exceptionContext); assertTrue(trace.indexOf("ContextedException")>=0); assertTrue(trace.indexOf(TEST_MESSAGE)>=0); assertTrue(trace.indexOf(TEST_MESSAGE_2)>=0); @@ -68,97 +67,25 @@ public class ContextedExceptionTest extends TestCase { } public void testContextedExceptionStringThrowableContext() { - contextedException = new ContextedException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE), new DefaultExceptionContext()); - String message = contextedException.getMessage(); - String trace = ExceptionUtils.getStackTrace(contextedException); + exceptionContext = new ContextedException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE), new DefaultExceptionContext()); + String message = exceptionContext.getMessage(); + String trace = ExceptionUtils.getStackTrace(exceptionContext); assertTrue(trace.indexOf("ContextedException")>=0); assertTrue(trace.indexOf(TEST_MESSAGE)>=0); assertTrue(trace.indexOf(TEST_MESSAGE_2)>=0); assertTrue(message.indexOf(TEST_MESSAGE_2)>=0); } - public void testAddValue() { - contextedException = new ContextedException(new Exception(TEST_MESSAGE)) - .addValue("test1", null) - .addValue("test2", "some value") - .addValue("test Date", new Date()) - .addValue("test Nbr", new Integer(5)); - - String message = contextedException.getMessage(); - assertTrue(message.indexOf(TEST_MESSAGE)>=0); - assertTrue(message.indexOf("test1")>=0); - assertTrue(message.indexOf("test2")>=0); - assertTrue(message.indexOf("test Date")>=0); - assertTrue(message.indexOf("test Nbr")>=0); - assertTrue(message.indexOf("some value")>=0); - assertTrue(message.indexOf("5")>=0); - - assertTrue(contextedException.getValue("test1") == null); - assertTrue(contextedException.getValue("test2").equals("some value")); - - assertTrue(contextedException.getLabelSet().size() == 4); - assertTrue(contextedException.getLabelSet().contains("test1")); - assertTrue(contextedException.getLabelSet().contains("test2")); - assertTrue(contextedException.getLabelSet().contains("test Date")); - assertTrue(contextedException.getLabelSet().contains("test Nbr")); - - contextedException.addValue("test2", "different value"); - assertTrue(contextedException.getLabelSet().size() == 5); - assertTrue(contextedException.getLabelSet().contains("test2")); - assertTrue(contextedException.getLabelSet().contains("test2[1]")); - - String contextMessage = contextedException.getFormattedExceptionMessage(null); - assertTrue(contextMessage.indexOf(TEST_MESSAGE) == -1); - assertTrue(contextedException.getMessage().endsWith(contextMessage)); - } - - public void testReplaceValue() { - contextedException = new ContextedException(new Exception(TEST_MESSAGE)) - .addValue("test Poorly written obj", new ObjectWithFaultyToString()); - - String message = contextedException.getMessage(); - assertTrue(message.indexOf(TEST_MESSAGE)>=0); - assertTrue(message.indexOf("test Poorly written obj")>=0); - assertTrue(message.indexOf("Crap")>=0); - - assertTrue(contextedException.getValue("crap") == null); - assertTrue(contextedException.getValue("test Poorly written obj") instanceof ObjectWithFaultyToString); - - assertTrue(contextedException.getLabelSet().size() == 1); - assertTrue(contextedException.getLabelSet().contains("test Poorly written obj")); - - assertTrue(!contextedException.getLabelSet().contains("crap")); - - contextedException.replaceValue("test Poorly written obj", "replacement"); - - assertTrue(contextedException.getLabelSet().size() == 1); - - String contextMessage = contextedException.getFormattedExceptionMessage(null); - assertTrue(contextMessage.indexOf(TEST_MESSAGE) == -1); - assertTrue(contextedException.getMessage().endsWith(contextMessage)); - } - public void testNullExceptionPassing() { - contextedException = new ContextedException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE), null) - .addValue("test1", null) - .addValue("test2", "some value") - .addValue("test Date", new Date()) - .addValue("test Nbr", new Integer(5)) - .addValue("test Poorly written obj", new ObjectWithFaultyToString()); + exceptionContext = new ContextedException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE), null) + .addContextValue("test1", null) + .addContextValue("test2", "some value") + .addContextValue("test Date", new Date()) + .addContextValue("test Nbr", new Integer(5)) + .addContextValue("test Poorly written obj", new ObjectWithFaultyToString()); - String message = contextedException.getMessage(); + String message = exceptionContext.getMessage(); assertTrue(message != null); } - - static class ObjectWithFaultyToString implements Serializable { - - private static final long serialVersionUID = 3495843995332310458L; - - @Override - public String toString() { - throw new RuntimeException("Crap"); - } - - } } diff --git a/src/test/java/org/apache/commons/lang3/exception/ContextedRuntimeExceptionTest.java b/src/test/java/org/apache/commons/lang3/exception/ContextedRuntimeExceptionTest.java index a45d367aa..37ac67f19 100644 --- a/src/test/java/org/apache/commons/lang3/exception/ContextedRuntimeExceptionTest.java +++ b/src/test/java/org/apache/commons/lang3/exception/ContextedRuntimeExceptionTest.java @@ -18,50 +18,49 @@ package org.apache.commons.lang3.exception; import java.util.Date; -import junit.framework.TestCase; - import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.exception.ContextedExceptionTest.ObjectWithFaultyToString; /** * JUnit tests for ContextedRuntimeException. * */ -public class ContextedRuntimeExceptionTest extends TestCase { +public class ContextedRuntimeExceptionTest extends AbstractExceptionContextTest { - private static final String TEST_MESSAGE_2 = "This is monotonous"; - private static final String TEST_MESSAGE = "Test Message"; - private ContextedRuntimeException contextedRuntimeException; + @Override + protected void setUp() throws Exception { + exceptionContext = new ContextedRuntimeException(new Exception(TEST_MESSAGE)); + super.setUp(); + } public void testContextedException() { - contextedRuntimeException = new ContextedRuntimeException(); - String message = contextedRuntimeException.getMessage(); - String trace = ExceptionUtils.getStackTrace(contextedRuntimeException); + exceptionContext = new ContextedRuntimeException(); + String message = exceptionContext.getMessage(); + String trace = ExceptionUtils.getStackTrace(exceptionContext); assertTrue(trace.indexOf("ContextedException")>=0); assertTrue(StringUtils.isEmpty(message)); } public void testContextedExceptionString() { - contextedRuntimeException = new ContextedRuntimeException(TEST_MESSAGE); - assertEquals(TEST_MESSAGE, contextedRuntimeException.getMessage()); + exceptionContext = new ContextedRuntimeException(TEST_MESSAGE); + assertEquals(TEST_MESSAGE, exceptionContext.getMessage()); - String trace = ExceptionUtils.getStackTrace(contextedRuntimeException); + String trace = ExceptionUtils.getStackTrace(exceptionContext); assertTrue(trace.indexOf(TEST_MESSAGE)>=0); } public void testContextedExceptionThrowable() { - contextedRuntimeException = new ContextedRuntimeException(new Exception(TEST_MESSAGE)); - String message = contextedRuntimeException.getMessage(); - String trace = ExceptionUtils.getStackTrace(contextedRuntimeException); + exceptionContext = new ContextedRuntimeException(new Exception(TEST_MESSAGE)); + String message = exceptionContext.getMessage(); + String trace = ExceptionUtils.getStackTrace(exceptionContext); assertTrue(trace.indexOf("ContextedException")>=0); assertTrue(trace.indexOf(TEST_MESSAGE)>=0); assertTrue(message.indexOf(TEST_MESSAGE)>=0); } public void testContextedExceptionStringThrowable() { - contextedRuntimeException = new ContextedRuntimeException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE)); - String message = contextedRuntimeException.getMessage(); - String trace = ExceptionUtils.getStackTrace(contextedRuntimeException); + exceptionContext = new ContextedRuntimeException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE)); + String message = exceptionContext.getMessage(); + String trace = ExceptionUtils.getStackTrace(exceptionContext); assertTrue(trace.indexOf("ContextedException")>=0); assertTrue(trace.indexOf(TEST_MESSAGE)>=0); assertTrue(trace.indexOf(TEST_MESSAGE_2)>=0); @@ -69,85 +68,24 @@ public class ContextedRuntimeExceptionTest extends TestCase { } public void testContextedExceptionStringThrowableContext() { - contextedRuntimeException = new ContextedRuntimeException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE), new DefaultExceptionContext()); - String message = contextedRuntimeException.getMessage(); - String trace = ExceptionUtils.getStackTrace(contextedRuntimeException); + exceptionContext = new ContextedRuntimeException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE), new DefaultExceptionContext() {}); + String message = exceptionContext.getMessage(); + String trace = ExceptionUtils.getStackTrace(exceptionContext); assertTrue(trace.indexOf("ContextedException")>=0); assertTrue(trace.indexOf(TEST_MESSAGE)>=0); assertTrue(trace.indexOf(TEST_MESSAGE_2)>=0); assertTrue(message.indexOf(TEST_MESSAGE_2)>=0); } - public void testAddValue() { - contextedRuntimeException = new ContextedRuntimeException(new Exception(TEST_MESSAGE)) - .addValue("test1", null) - .addValue("test2", "some value") - .addValue("test Date", new Date()) - .addValue("test Nbr", new Integer(5)); - - String message = contextedRuntimeException.getMessage(); - assertTrue(message.indexOf(TEST_MESSAGE)>=0); - assertTrue(message.indexOf("test1")>=0); - assertTrue(message.indexOf("test2")>=0); - assertTrue(message.indexOf("test Date")>=0); - assertTrue(message.indexOf("test Nbr")>=0); - assertTrue(message.indexOf("some value")>=0); - assertTrue(message.indexOf("5")>=0); - - assertTrue(contextedRuntimeException.getValue("test1") == null); - assertTrue(contextedRuntimeException.getValue("test2").equals("some value")); - - assertTrue(contextedRuntimeException.getLabelSet().size() == 4); - assertTrue(contextedRuntimeException.getLabelSet().contains("test1")); - assertTrue(contextedRuntimeException.getLabelSet().contains("test2")); - assertTrue(contextedRuntimeException.getLabelSet().contains("test Date")); - assertTrue(contextedRuntimeException.getLabelSet().contains("test Nbr")); - - contextedRuntimeException.addValue("test2", "different value"); - assertTrue(contextedRuntimeException.getLabelSet().size() == 5); - assertTrue(contextedRuntimeException.getLabelSet().contains("test2")); - assertTrue(contextedRuntimeException.getLabelSet().contains("test2[1]")); - - String contextMessage = contextedRuntimeException.getFormattedExceptionMessage(null); - assertTrue(contextMessage.indexOf(TEST_MESSAGE) == -1); - assertTrue(contextedRuntimeException.getMessage().endsWith(contextMessage)); - } - - public void testReplaceValue() { - contextedRuntimeException = new ContextedRuntimeException(new Exception(TEST_MESSAGE)) - .addValue("test Poorly written obj", new ObjectWithFaultyToString()); - - String message = contextedRuntimeException.getMessage(); - assertTrue(message.indexOf(TEST_MESSAGE)>=0); - assertTrue(message.indexOf("test Poorly written obj")>=0); - assertTrue(message.indexOf("Crap")>=0); - - assertTrue(contextedRuntimeException.getValue("crap") == null); - assertTrue(contextedRuntimeException.getValue("test Poorly written obj") instanceof ObjectWithFaultyToString); - - assertTrue(contextedRuntimeException.getLabelSet().size() == 1); - assertTrue(contextedRuntimeException.getLabelSet().contains("test Poorly written obj")); - - assertTrue(!contextedRuntimeException.getLabelSet().contains("crap")); - - contextedRuntimeException.replaceValue("test Poorly written obj", "replacement"); - - assertTrue(contextedRuntimeException.getLabelSet().size() == 1); - - String contextMessage = contextedRuntimeException.getFormattedExceptionMessage(null); - assertTrue(contextMessage.indexOf(TEST_MESSAGE) == -1); - assertTrue(contextedRuntimeException.getMessage().endsWith(contextMessage)); - } - public void testNullExceptionPassing() { - contextedRuntimeException = new ContextedRuntimeException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE), null) - .addValue("test1", null) - .addValue("test2", "some value") - .addValue("test Date", new Date()) - .addValue("test Nbr", new Integer(5)) - .addValue("test Poorly written obj", new ObjectWithFaultyToString()); + exceptionContext = new ContextedRuntimeException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE), null) + .addContextValue("test1", null) + .addContextValue("test2", "some value") + .addContextValue("test Date", new Date()) + .addContextValue("test Nbr", new Integer(5)) + .addContextValue("test Poorly written obj", new ObjectWithFaultyToString()); - String message = contextedRuntimeException.getMessage(); + String message = exceptionContext.getMessage(); assertTrue(message != null); } } diff --git a/src/test/java/org/apache/commons/lang3/exception/DefaultExceptionContextTest.java b/src/test/java/org/apache/commons/lang3/exception/DefaultExceptionContextTest.java index ad3d48c42..4caef7a4d 100644 --- a/src/test/java/org/apache/commons/lang3/exception/DefaultExceptionContextTest.java +++ b/src/test/java/org/apache/commons/lang3/exception/DefaultExceptionContextTest.java @@ -16,92 +16,21 @@ */ package org.apache.commons.lang3.exception; -import java.util.Date; - -import junit.framework.TestCase; - -import org.apache.commons.lang3.exception.ContextedExceptionTest.ObjectWithFaultyToString; - /** * JUnit tests for DefaultExceptionContext. * */ -public class DefaultExceptionContextTest extends TestCase { - - private ExceptionContext defaultExceptionContext; - - public DefaultExceptionContextTest(String name) { - super(name); - } +public class DefaultExceptionContextTest extends AbstractExceptionContextTest { @Override - public void setUp() { - defaultExceptionContext = new DefaultExceptionContext() - .addValue("test1", null) - .addValue("test2", "some value") - .addValue("test Date", new Date()) - .addValue("test Nbr", new Integer(5)) - .addValue("test Poorly written obj", new ObjectWithFaultyToString()); + public void setUp() throws Exception { + exceptionContext = new DefaultExceptionContext(); + super.setUp(); } - public void testAddValue() { - defaultExceptionContext.addValue("test2", "different value"); - - String message = defaultExceptionContext.getFormattedExceptionMessage("This is an error"); - assertTrue(message.indexOf("This is an error")>=0); - assertTrue(message.indexOf("test1")>=0); - assertTrue(message.indexOf("test2")>=0); - assertTrue(message.indexOf("test2[1]")>=0); - assertTrue(message.indexOf("test Date")>=0); - assertTrue(message.indexOf("test Nbr")>=0); - assertTrue(message.indexOf("test Poorly written obj")>=0); - assertTrue(message.indexOf("some value")>=0); - assertTrue(message.indexOf("different value")>=0); - assertTrue(message.indexOf("5")>=0); - assertTrue(message.indexOf("Crap")>=0); - } - - public void testReplaceValue() { - defaultExceptionContext.replaceValue("test2", "different value"); - defaultExceptionContext.replaceValue("test3", "3"); - - String message = defaultExceptionContext.getFormattedExceptionMessage("This is an error"); - assertTrue(message.indexOf("This is an error")>=0); - assertTrue(message.indexOf("test1")>=0); - assertTrue(message.indexOf("test2")>=0); - assertTrue(message.indexOf("test3")>=0); - assertTrue(message.indexOf("test Date")>=0); - assertTrue(message.indexOf("test Nbr")>=0); - assertTrue(message.indexOf("test Poorly written obj")>=0); - assertTrue(message.indexOf("different value")>=0); - assertTrue(message.indexOf("5")>=0); - assertTrue(message.indexOf("Crap")>=0); - - assertTrue(message.indexOf("test2[1]")<0); - assertTrue(message.indexOf("some value")<0); -} - public void testFormattedExceptionMessageNull() { - defaultExceptionContext = new DefaultExceptionContext(); - defaultExceptionContext.getFormattedExceptionMessage(null); - } - - public void testGetValue() { - assertTrue(defaultExceptionContext.getValue("test1") == null); - assertTrue(defaultExceptionContext.getValue("test2").equals("some value")); - assertTrue(defaultExceptionContext.getValue("crap") == null); - assertTrue(defaultExceptionContext.getValue("test Poorly written obj") instanceof ObjectWithFaultyToString); - } - - public void testGetLabelSet() { - assertTrue(defaultExceptionContext.getLabelSet().size() == 5); - assertTrue(defaultExceptionContext.getLabelSet().contains("test1")); - assertTrue(defaultExceptionContext.getLabelSet().contains("test2")); - assertTrue(defaultExceptionContext.getLabelSet().contains("test Date")); - assertTrue(defaultExceptionContext.getLabelSet().contains("test Nbr")); - assertTrue(defaultExceptionContext.getLabelSet().contains("test Poorly written obj")); - - assertTrue(!defaultExceptionContext.getLabelSet().contains("crap")); + exceptionContext = new DefaultExceptionContext(); + exceptionContext.getFormattedExceptionMessage(null); } }