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.io.Serializable; 020 import java.util.Collections; 021 import java.util.Locale; 022 import java.util.Map; 023 import java.util.SortedMap; 024 import java.util.TreeMap; 025 026 import org.apache.logging.log4j.util.EnglishEnums; 027 028 /** 029 * Represents a Message that consists of a Map. 030 */ 031 public class MapMessage implements MultiformatMessage, Serializable { 032 /** 033 * When set as the format specifier causes the Map to be formatted as XML. 034 */ 035 036 public enum MapFormat { 037 /** The map should be formatted as XML. */ 038 XML, 039 /** The map should be formatted as JSON. */ 040 JSON, 041 /** The map should be formatted the same as documented by java.util.AbstractMap.toString(). */ 042 JAVA 043 } 044 045 private static final long serialVersionUID = -5031471831131487120L; 046 047 private final SortedMap<String, String> data; 048 049 /** 050 * Constructor. 051 */ 052 public MapMessage() { 053 data = new TreeMap<String, String>(); 054 } 055 056 /** 057 * Constructor based on an existing Map. 058 * @param map The Map. 059 */ 060 public MapMessage(Map<String, String> map) { 061 this.data = map instanceof SortedMap ? (SortedMap<String, String>) map : new TreeMap<String, String>(map); 062 } 063 064 public String[] getFormats() { 065 String[] formats = new String[MapFormat.values().length]; 066 int i = 0; 067 for (MapFormat format : MapFormat.values()) { 068 formats[i++] = format.name(); 069 } 070 return formats; 071 } 072 073 /** 074 * Returns the data elements as if they were parameters on the logging event. 075 * @return the data elements. 076 */ 077 public Object[] getParameters() { 078 return data.values().toArray(); 079 } 080 081 /** 082 * Returns the message. 083 * @return the message. 084 */ 085 public String getFormat() { 086 return ""; 087 } 088 089 /** 090 * Returns the message data as an unmodifiable Map. 091 * @return the message data as an unmodifiable map. 092 */ 093 public Map<String, String> getData() { 094 return Collections.unmodifiableMap(data); 095 } 096 097 /** 098 * Clear the data. 099 */ 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 }