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