DefaultExceptionContext.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.lang3.exception;

  18. import java.io.Serializable;
  19. import java.util.ArrayList;
  20. import java.util.List;
  21. import java.util.Objects;
  22. import java.util.Set;
  23. import java.util.stream.Collectors;
  24. import java.util.stream.Stream;

  25. import org.apache.commons.lang3.StringUtils;
  26. import org.apache.commons.lang3.tuple.ImmutablePair;
  27. import org.apache.commons.lang3.tuple.Pair;

  28. /**
  29.  * Default implementation of the context storing the label-value pairs for contexted exceptions.
  30.  * <p>
  31.  * This implementation is serializable, however this is dependent on the values that
  32.  * are added also being serializable.
  33.  * </p>
  34.  *
  35.  * @see ContextedException
  36.  * @see ContextedRuntimeException
  37.  * @since 3.0
  38.  */
  39. public class DefaultExceptionContext implements ExceptionContext, Serializable {

  40.     /** The serialization version. */
  41.     private static final long serialVersionUID = 20110706L;

  42.     /** The list storing the label-data pairs. */
  43.     private final List<Pair<String, Object>> contextValues = new ArrayList<>();

  44.     /**
  45.      * Constructs a new instance.
  46.      */
  47.     public DefaultExceptionContext() {
  48.         // empty
  49.     }

  50.     /**
  51.      * {@inheritDoc}
  52.      */
  53.     @Override
  54.     public DefaultExceptionContext addContextValue(final String label, final Object value) {
  55.         contextValues.add(new ImmutablePair<>(label, value));
  56.         return this;
  57.     }

  58.     /**
  59.      * {@inheritDoc}
  60.      */
  61.     @Override
  62.     public List<Pair<String, Object>> getContextEntries() {
  63.         return contextValues;
  64.     }

  65.     /**
  66.      * {@inheritDoc}
  67.      */
  68.     @Override
  69.     public Set<String> getContextLabels() {
  70.         return stream().map(Pair::getKey).collect(Collectors.toSet());
  71.     }

  72.     /**
  73.      * {@inheritDoc}
  74.      */
  75.     @Override
  76.     public List<Object> getContextValues(final String label) {
  77.         return stream().filter(pair -> StringUtils.equals(label, pair.getKey())).map(Pair::getValue).collect(Collectors.toList());
  78.     }

  79.     /**
  80.      * {@inheritDoc}
  81.      */
  82.     @Override
  83.     public Object getFirstContextValue(final String label) {
  84.         return stream().filter(pair -> StringUtils.equals(label, pair.getKey())).findFirst().map(Pair::getValue).orElse(null);
  85.     }

  86.     /**
  87.      * Builds the message containing the contextual information.
  88.      *
  89.      * @param baseMessage  the base exception message <b>without</b> context information appended
  90.      * @return the exception message <b>with</b> context information appended, never null
  91.      */
  92.     @Override
  93.     public String getFormattedExceptionMessage(final String baseMessage) {
  94.         final StringBuilder buffer = new StringBuilder(256);
  95.         if (baseMessage != null) {
  96.             buffer.append(baseMessage);
  97.         }
  98.         if (!contextValues.isEmpty()) {
  99.             if (buffer.length() > 0) {
  100.                 buffer.append('\n');
  101.             }
  102.             buffer.append("Exception Context:\n");
  103.             int i = 0;
  104.             for (final Pair<String, Object> pair : contextValues) {
  105.                 buffer.append("\t[");
  106.                 buffer.append(++i);
  107.                 buffer.append(':');
  108.                 buffer.append(pair.getKey());
  109.                 buffer.append("=");
  110.                 final Object value = pair.getValue();
  111.                 try {
  112.                     buffer.append(Objects.toString(value));
  113.                 } catch (final Exception e) {
  114.                     buffer.append("Exception thrown on toString(): ");
  115.                     buffer.append(ExceptionUtils.getStackTrace(e));
  116.                 }
  117.                 buffer.append("]\n");
  118.             }
  119.             buffer.append("---------------------------------");
  120.         }
  121.         return buffer.toString();
  122.     }

  123.     /**
  124.      * {@inheritDoc}
  125.      */
  126.     @Override
  127.     public DefaultExceptionContext setContextValue(final String label, final Object value) {
  128.         contextValues.removeIf(p -> StringUtils.equals(label, p.getKey()));
  129.         addContextValue(label, value);
  130.         return this;
  131.     }

  132.     private Stream<Pair<String, Object>> stream() {
  133.         return contextValues.stream();
  134.     }

  135. }