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