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