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.logging.log4j.message;
18  
19  import java.util.Collections;
20  import java.util.Map;
21  import java.util.SortedMap;
22  import java.util.TreeMap;
23  
24  import org.apache.logging.log4j.util.EnglishEnums;
25  
26  /**
27   * Represents a Message that consists of a Map.
28   */
29  public class MapMessage implements MultiformatMessage {
30      /**
31       * When set as the format specifier causes the Map to be formatted as XML.
32       */
33  
34      public enum MapFormat {
35          /** The map should be formatted as XML. */
36          XML,
37          /** The map should be formatted as JSON. */
38          JSON,
39          /** The map should be formatted the same as documented by java.util.AbstractMap.toString(). */
40          JAVA
41      }
42  
43      private static final long serialVersionUID = -5031471831131487120L;
44  
45      private final SortedMap<String, String> data;
46  
47      /**
48       * Constructor.
49       */
50      public MapMessage() {
51          data = new TreeMap<String, String>();
52      }
53  
54      /**
55       * Constructor based on an existing Map.
56       * @param map The Map.
57       */
58      public MapMessage(final Map<String, String> map) {
59          this.data = map instanceof SortedMap ? (SortedMap<String, String>) map : new TreeMap<String, String>(map);
60      }
61  
62      @Override
63      public String[] getFormats() {
64          final String[] formats = new String[MapFormat.values().length];
65          int i = 0;
66          for (final MapFormat format : MapFormat.values()) {
67              formats[i++] = format.name();
68          }
69          return formats;
70      }
71  
72      /**
73       * Returns the data elements as if they were parameters on the logging event.
74       * @return the data elements.
75       */
76      @Override
77      public Object[] getParameters() {
78          return data.values().toArray();
79      }
80  
81      /**
82       * Returns the message.
83       * @return the message.
84       */
85      @Override
86      public String getFormat() {
87          return "";
88      }
89  
90      /**
91       * Returns the message data as an unmodifiable Map.
92       * @return the message data as an unmodifiable map.
93       */
94      public Map<String, String> getData() {
95          return Collections.unmodifiableMap(data);
96      }
97  
98      /**
99       * Clear the data.
100      */
101     public void clear() {
102         data.clear();
103     }
104 
105     /**
106      * Add an item to the data Map.
107      * @param key The name of the data item.
108      * @param value The value of the data item.
109      */
110     public void put(final String key, final String value) {
111         if (value == null) {
112             throw new IllegalArgumentException("No value provided for key " + key);
113         }
114         validate(key, value);
115         data.put(key, value);
116     }
117 
118     protected void validate(final String key, final String value) {
119 
120     }
121 
122     /**
123      * Add all the elements from the specified Map.
124      * @param map The Map to add.
125      */
126     public void putAll(final Map<String, String> map) {
127         data.putAll(map);
128     }
129 
130     /**
131      * Retrieve the value of the element with the specified key or null if the key is not present.
132      * @param key The name of the element.
133      * @return The value of the element or null if the key is not present.
134      */
135     public String get(final String key) {
136         return data.get(key);
137     }
138 
139     /**
140      * Remove the element with the specified name.
141      * @param key The name of the element.
142      * @return The previous value of the element.
143      */
144     public String remove(final String key) {
145         return data.remove(key);
146     }
147 
148     /**
149      * Format the Structured data as described in RFC 5424.
150      *
151      * @return The formatted String.
152      */
153     public String asString() {
154         return asString((MapFormat) null);
155     }
156 
157     public String asString(final String format) {
158         try {
159             return asString(EnglishEnums.valueOf(MapFormat.class, format));
160         } catch (final IllegalArgumentException ex) {
161             return asString();
162         }
163     }
164     /**
165      * Format the Structured data as described in RFC 5424.
166      *
167      * @param format The format identifier. Ignored in this implementation.
168      * @return The formatted String.
169      */
170     private String asString(final MapFormat format) {
171         final StringBuilder sb = new StringBuilder();
172         if (format == null) {
173             appendMap(sb);
174         } else {
175             switch (format) {
176                 case XML : {
177                     asXML(sb);
178                     break;
179                 }
180                 case JSON : {
181                     asJSON(sb);
182                     break;
183                 }
184                 case JAVA : {
185                     asJava(sb);
186                     break;
187                 }
188                 default : {
189                     appendMap(sb);
190                 }
191             }
192         }
193         return sb.toString();
194     }
195 
196     public void asXML(final StringBuilder sb) {
197         sb.append("<Map>\n");
198         for (final Map.Entry<String, String> entry : data.entrySet()) {
199             sb.append("  <Entry key=\"").append(entry.getKey()).append("\">").append(entry.getValue())
200               .append("</Entry>\n");
201         }
202         sb.append("</Map>");
203     }
204 
205     /**
206      * Format the message and return it.
207      * @return the formatted message.
208      */
209     @Override
210     public String getFormattedMessage() {
211         return asString();
212     }
213 
214     /**
215      *
216      * @param formats An array of Strings that provide extra information about how to format the message.
217      * MapMessage uses the first format specifier it recognizes. The supported formats are XML, JSON, and
218      * JAVA. The default format is key1="value1" key2="value2" as required by RFC 5424 messages.
219      *
220      * @return The formatted message.
221      */
222     @Override
223     public String getFormattedMessage(final String[] formats) {
224         if (formats == null || formats.length == 0) {
225             return asString();
226         }
227         for (final String format : formats) {
228             for (final MapFormat mapFormat : MapFormat.values()) {
229                 if (mapFormat.name().equalsIgnoreCase(format)) {
230                     return asString(mapFormat);
231                 }
232             }
233         }
234         return asString();
235 
236     }
237 
238     protected void appendMap(final StringBuilder sb) {
239         boolean first = true;
240         for (final Map.Entry<String, String> entry : data.entrySet()) {
241             if (!first) {
242                 sb.append(" ");
243             }
244             first = false;
245             sb.append(entry.getKey()).append("=\"").append(entry.getValue()).append("\"");
246         }
247     }
248 
249     protected void asJSON(final StringBuilder sb) {
250         boolean first = true;
251         sb.append("{");
252         for (final Map.Entry<String, String> entry : data.entrySet()) {
253             if (!first) {
254                 sb.append(", ");
255             }
256             first = false;
257             sb.append("\"").append(entry.getKey()).append("\":");
258             sb.append("\"").append(entry.getValue()).append("\"");
259         }
260         sb.append("}");
261     }
262 
263 
264     protected void asJava(final StringBuilder sb) {
265         boolean first = true;
266         sb.append("{");
267         for (final Map.Entry<String, String> entry : data.entrySet()) {
268             if (!first) {
269                 sb.append(", ");
270             }
271             first = false;
272             sb.append(entry.getKey()).append("=\"").append(entry.getValue()).append("\"");
273         }
274         sb.append("}");
275     }
276 
277     public MapMessage newInstance(final Map<String, String> map) {
278         return new MapMessage(map);
279     }
280 
281     @Override
282     public String toString() {
283         return asString();
284     }
285 
286     @Override
287     public boolean equals(final Object o) {
288         if (this == o) {
289             return true;
290         }
291         if (o == null || this.getClass() != o.getClass()) {
292             return false;
293         }
294 
295         final MapMessage that = (MapMessage) o;
296 
297         return this.data.equals(that.data);
298     }
299 
300     @Override
301     public int hashCode() {
302         return data.hashCode();
303     }
304 
305     /**
306      * Always returns null.
307      *
308      * @return null
309      */
310     @Override
311     public Throwable getThrowable() {
312         return null;
313     }
314 }