001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017 package org.apache.logging.log4j.message; 018 019 import java.util.Collections; 020 import java.util.Map; 021 import java.util.SortedMap; 022 import java.util.TreeMap; 023 024 import org.apache.logging.log4j.util.EnglishEnums; 025 026 /** 027 * Represents a Message that consists of a Map. 028 */ 029 public class MapMessage implements MultiformatMessage { 030 /** 031 * When set as the format specifier causes the Map to be formatted as XML. 032 */ 033 034 public enum MapFormat { 035 /** The map should be formatted as XML. */ 036 XML, 037 /** The map should be formatted as JSON. */ 038 JSON, 039 /** The map should be formatted the same as documented by java.util.AbstractMap.toString(). */ 040 JAVA 041 } 042 043 private static final long serialVersionUID = -5031471831131487120L; 044 045 private final SortedMap<String, String> data; 046 047 /** 048 * Constructor. 049 */ 050 public MapMessage() { 051 data = new TreeMap<String, String>(); 052 } 053 054 /** 055 * Constructor based on an existing Map. 056 * @param map The Map. 057 */ 058 public MapMessage(final Map<String, String> map) { 059 this.data = map instanceof SortedMap ? (SortedMap<String, String>) map : new TreeMap<String, String>(map); 060 } 061 062 public String[] getFormats() { 063 final String[] formats = new String[MapFormat.values().length]; 064 int i = 0; 065 for (final MapFormat format : MapFormat.values()) { 066 formats[i++] = format.name(); 067 } 068 return formats; 069 } 070 071 /** 072 * Returns the data elements as if they were parameters on the logging event. 073 * @return the data elements. 074 */ 075 public Object[] getParameters() { 076 return data.values().toArray(); 077 } 078 079 /** 080 * Returns the message. 081 * @return the message. 082 */ 083 public String getFormat() { 084 return ""; 085 } 086 087 /** 088 * Returns the message data as an unmodifiable Map. 089 * @return the message data as an unmodifiable map. 090 */ 091 public Map<String, String> getData() { 092 return Collections.unmodifiableMap(data); 093 } 094 095 /** 096 * Clear the data. 097 */ 098 public void clear() { 099 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()) 197 .append("</Entry>\n"); 198 } 199 sb.append("</Map>"); 200 } 201 202 /** 203 * Format the message and return it. 204 * @return the formatted message. 205 */ 206 public String getFormattedMessage() { 207 return asString(); 208 } 209 210 /** 211 * 212 * @param formats An array of Strings that provide extra information about how to format the message. 213 * MapMessage uses the first format specifier it recognizes. The supported formats are XML, JSON, and 214 * JAVA. The default format is key1="value1" key2="value2" as required by RFC 5424 messages. 215 * 216 * @return The formatted message. 217 */ 218 public String getFormattedMessage(final String[] formats) { 219 if (formats == null || formats.length == 0) { 220 return asString(); 221 } 222 for (final String format : formats) { 223 for (final MapFormat mapFormat : MapFormat.values()) { 224 if (mapFormat.name().equalsIgnoreCase(format)) { 225 return asString(mapFormat); 226 } 227 } 228 } 229 return asString(); 230 231 } 232 233 protected void appendMap(final StringBuilder sb) { 234 boolean first = true; 235 for (final Map.Entry<String, String> entry : data.entrySet()) { 236 if (!first) { 237 sb.append(" "); 238 } 239 first = false; 240 sb.append(entry.getKey()).append("=\"").append(entry.getValue()).append("\""); 241 } 242 } 243 244 protected void asJSON(final StringBuilder sb) { 245 boolean first = true; 246 sb.append("{"); 247 for (final Map.Entry<String, String> entry : data.entrySet()) { 248 if (!first) { 249 sb.append(", "); 250 } 251 first = false; 252 sb.append("\"").append(entry.getKey()).append("\":"); 253 sb.append("\"").append(entry.getValue()).append("\""); 254 } 255 sb.append("}"); 256 } 257 258 259 protected void asJava(final StringBuilder sb) { 260 boolean first = true; 261 sb.append("{"); 262 for (final Map.Entry<String, String> entry : data.entrySet()) { 263 if (!first) { 264 sb.append(", "); 265 } 266 first = false; 267 sb.append(entry.getKey()).append("=\"").append(entry.getValue()).append("\""); 268 } 269 sb.append("}"); 270 } 271 272 public MapMessage newInstance(final Map<String, String> map) { 273 return new MapMessage(map); 274 } 275 276 @Override 277 public String toString() { 278 return asString(); 279 } 280 281 @Override 282 public boolean equals(final Object o) { 283 if (this == o) { 284 return true; 285 } 286 if (o == null || this.getClass() != o.getClass()) { 287 return false; 288 } 289 290 final MapMessage that = (MapMessage) o; 291 292 return this.data.equals(that.data); 293 } 294 295 @Override 296 public int hashCode() { 297 return data.hashCode(); 298 } 299 300 /** 301 * Always returns null. 302 * 303 * @return null 304 */ 305 public Throwable getThrowable() { 306 return null; 307 } 308 }