View Javadoc
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  
19  import java.io.Serializable;
20  import java.util.ArrayList;
21  import java.util.List;
22  import java.util.Set;
23  import java.util.stream.Collectors;
24  import java.util.stream.Stream;
25  
26  import org.apache.commons.lang3.StringUtils;
27  import org.apache.commons.lang3.tuple.ImmutablePair;
28  import org.apache.commons.lang3.tuple.Pair;
29  
30  /**
31   * Default implementation of the context storing the label-value pairs for contexted exceptions.
32   * <p>
33   * This implementation is serializable, however this is dependent on the values that
34   * are added also being serializable.
35   * </p>
36   *
37   * @see ContextedException
38   * @see ContextedRuntimeException
39   * @since 3.0
40   */
41  public class DefaultExceptionContext implements ExceptionContext, Serializable {
42  
43      /** The serialization version. */
44      private static final long serialVersionUID = 20110706L;
45  
46      /** The list storing the label-data pairs. */
47      private final List<Pair<String, Object>> contextValues = new ArrayList<>();
48  
49      /**
50       * {@inheritDoc}
51       */
52      @Override
53      public DefaultExceptionContext addContextValue(final String label, final Object value) {
54          contextValues.add(new ImmutablePair<>(label, value));
55          return this;
56      }
57  
58      /**
59       * {@inheritDoc}
60       */
61      @Override
62      public List<Pair<String, Object>> getContextEntries() {
63          return contextValues;
64      }
65  
66      /**
67       * {@inheritDoc}
68       */
69      @Override
70      public Set<String> getContextLabels() {
71          return stream().map(Pair::getKey).collect(Collectors.toSet());
72      }
73  
74      /**
75       * {@inheritDoc}
76       */
77      @Override
78      public List<Object> getContextValues(final String label) {
79          return stream().filter(pair -> StringUtils.equals(label, pair.getKey())).map(Pair::getValue).collect(Collectors.toList());
80      }
81  
82      /**
83       * {@inheritDoc}
84       */
85      @Override
86      public Object getFirstContextValue(final String label) {
87          return stream().filter(pair -> StringUtils.equals(label, pair.getKey())).findFirst().map(Pair::getValue).orElse(null);
88      }
89  
90      /**
91       * Builds the message containing the contextual information.
92       *
93       * @param baseMessage  the base exception message <b>without</b> context information appended
94       * @return the exception message <b>with</b> context information appended, never null
95       */
96      @Override
97      public String getFormattedExceptionMessage(final String baseMessage) {
98          final StringBuilder buffer = new StringBuilder(256);
99          if (baseMessage != null) {
100             buffer.append(baseMessage);
101         }
102 
103         if (!contextValues.isEmpty()) {
104             if (buffer.length() > 0) {
105                 buffer.append('\n');
106             }
107             buffer.append("Exception Context:\n");
108 
109             int i = 0;
110             for (final Pair<String, Object> pair : contextValues) {
111                 buffer.append("\t[");
112                 buffer.append(++i);
113                 buffer.append(':');
114                 buffer.append(pair.getKey());
115                 buffer.append("=");
116                 final Object value = pair.getValue();
117                 if (value == null) {
118                     buffer.append("null");
119                 } else {
120                     String valueStr;
121                     try {
122                         valueStr = value.toString();
123                     } catch (final Exception e) {
124                         valueStr = "Exception thrown on toString(): " + ExceptionUtils.getStackTrace(e);
125                     }
126                     buffer.append(valueStr);
127                 }
128                 buffer.append("]\n");
129             }
130             buffer.append("---------------------------------");
131         }
132         return buffer.toString();
133     }
134 
135     /**
136      * {@inheritDoc}
137      */
138     @Override
139     public DefaultExceptionContext setContextValue(final String label, final Object value) {
140         contextValues.removeIf(p -> StringUtils.equals(label, p.getKey()));
141         addContextValue(label, value);
142         return this;
143     }
144 
145     private Stream<Pair<String, Object>> stream() {
146         return contextValues.stream();
147     }
148 
149 }