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.Map;
020
021 import org.apache.logging.log4j.util.EnglishEnums;
022
023 /**
024 * Represents a Message that conforms to RFC 5424 (http://tools.ietf.org/html/rfc5424).
025 */
026 public class StructuredDataMessage extends MapMessage {
027
028 private static final long serialVersionUID = 1703221292892071920L;
029 private static final int MAX_LENGTH = 32;
030 private static final int HASHVAL = 31;
031
032 private StructuredDataId id;
033
034 private String message;
035
036 private String type;
037
038 /**
039 * Supported formats.
040 */
041 public enum Format {
042 /** The map should be formatted as XML. */
043 XML,
044 /** Full message format includes the type and message. */
045 FULL
046 }
047
048 /**
049 * Constructor based on a String id.
050 * @param id The String id.
051 * @param msg The message.
052 * @param type The message type.
053 */
054 public StructuredDataMessage(final String id, final String msg, final String type) {
055 this.id = new StructuredDataId(id, null, null);
056 this.message = msg;
057 this.type = type;
058 }
059 /**
060 * Constructor based on a String id.
061 * @param id The String id.
062 * @param msg The message.
063 * @param type The message type.
064 * @param data The StructuredData map.
065 */
066 public StructuredDataMessage(final String id, final String msg, final String type,
067 final Map<String, String> data) {
068 super(data);
069 this.id = new StructuredDataId(id, null, null);
070 this.message = msg;
071 this.type = type;
072 }
073
074 /**
075 * Constructor based on a StructuredDataId.
076 * @param id The StructuredDataId.
077 * @param msg The message.
078 * @param type The message type.
079 */
080 public StructuredDataMessage(final StructuredDataId id, final String msg, final String type) {
081 this.id = id;
082 this.message = msg;
083 this.type = type;
084 }
085
086 /**
087 * Constructor based on a StructuredDataId.
088 * @param id The StructuredDataId.
089 * @param msg The message.
090 * @param type The message type.
091 * @param data The StructuredData map.
092 */
093 public StructuredDataMessage(final StructuredDataId id, final String msg, final String type,
094 final Map<String, String> data) {
095 super(data);
096 this.id = id;
097 this.message = msg;
098 this.type = type;
099 }
100
101
102 /**
103 * Constructor based on a StructuredDataMessage.
104 * @param msg The StructuredDataMessage.
105 * @param map The StructuredData map.
106 */
107 private StructuredDataMessage(final StructuredDataMessage msg, final Map<String, String> map) {
108 super(map);
109 this.id = msg.id;
110 this.message = msg.message;
111 this.type = msg.type;
112 }
113
114
115 /**
116 * Basic constructor.
117 */
118 protected StructuredDataMessage() {
119
120 }
121
122 /**
123 * Returns the supported formats.
124 * @return An array of the supported format names.
125 */
126 @Override
127 public String[] getFormats() {
128 final String[] formats = new String[Format.values().length];
129 int i = 0;
130 for (final Format format : Format.values()) {
131 formats[i++] = format.name();
132 }
133 return formats;
134 }
135
136 /**
137 * Returns the id.
138 * @return the StructuredDataId.
139 */
140 public StructuredDataId getId() {
141 return id;
142 }
143
144 /**
145 * Set the id from a String.
146 * @param id The String id.
147 */
148 protected void setId(final String id) {
149 this.id = new StructuredDataId(id, null, null);
150 }
151
152 /**
153 * Set the id.
154 * @param id The StructuredDataId.
155 */
156 protected void setId(final StructuredDataId id) {
157 this.id = id;
158 }
159
160 /**
161 * Set the type.
162 * @return the type.
163 */
164 public String getType() {
165 return type;
166 }
167
168 protected void setType(final String type) {
169 if (type.length() > MAX_LENGTH) {
170 throw new IllegalArgumentException("Structured data type exceeds maximum length of 32 characters: " + type);
171 }
172 this.type = type;
173 }
174 /**
175 * Returns the message.
176 * @return the message.
177 */
178 @Override
179 public String getFormat() {
180 return message;
181 }
182
183 protected void setMessageFormat(final String msg) {
184 this.message = msg;
185 }
186
187
188 @Override
189 protected void validate(final String key, final String value) {
190 if (key.length() > MAX_LENGTH) {
191 throw new IllegalArgumentException("Structured data keys are limited to 32 characters. key: " + key +
192 " value: " + value);
193 }
194 }
195
196 /**
197 * Format the Structured data as described in RFC 5424.
198 *
199 * @return The formatted String.
200 */
201 @Override
202 public String asString() {
203 return asString(Format.FULL, null);
204 }
205
206 /**
207 * Format the Structured data as described in RFC 5424.
208 *
209 * @param format The format identifier. Ignored in this implementation.
210 * @return The formatted String.
211 */
212
213 @Override
214 public String asString(final String format) {
215 try {
216 return asString(EnglishEnums.valueOf(Format.class, format), null);
217 } catch (final IllegalArgumentException ex) {
218 return asString();
219 }
220 }
221
222 /**
223 * Format the Structured data as described in RFC 5424.
224 *
225 * @param format "full" will include the type and message. null will return only the STRUCTURED-DATA as
226 * described in RFC 5424
227 * @param structuredDataId The SD-ID as described in RFC 5424. If null the value in the StructuredData
228 * will be used.
229 * @return The formatted String.
230 */
231 public final String asString(final Format format, final StructuredDataId structuredDataId) {
232 final StringBuilder sb = new StringBuilder();
233 final boolean full = Format.FULL.equals(format);
234 if (full) {
235 final String type = getType();
236 if (type == null) {
237 return sb.toString();
238 }
239 sb.append(getType()).append(" ");
240 }
241 StructuredDataId id = getId();
242 if (id != null) {
243 id = id.makeId(structuredDataId);
244 } else {
245 id = structuredDataId;
246 }
247 if (id == null || id.getName() == null) {
248 return sb.toString();
249 }
250 sb.append("[");
251 sb.append(id);
252 sb.append(" ");
253 appendMap(sb);
254 sb.append("]");
255 if (full) {
256 final String msg = getFormat();
257 if (msg != null) {
258 sb.append(" ").append(msg);
259 }
260 }
261 return sb.toString();
262 }
263
264 /**
265 * Format the message and return it.
266 * @return the formatted message.
267 */
268 @Override
269 public String getFormattedMessage() {
270 return asString(Format.FULL, null);
271 }
272
273 /**
274 * Format the message according the the specified format.
275 * @param formats An array of Strings that provide extra information about how to format the message.
276 * StructuredDataMessage accepts only a format of "FULL" which will cause the event type to be
277 * prepended and the event message to be appended. Specifying any other value will cause only the
278 * StructuredData to be included. The default is "FULL".
279 *
280 * @return the formatted message.
281 */
282 @Override
283 public String getFormattedMessage(final String[] formats) {
284 if (formats != null && formats.length > 0) {
285 for (final String format : formats) {
286 if (Format.XML.name().equalsIgnoreCase(format)) {
287 return asXML();
288 } else if (Format.FULL.name().equalsIgnoreCase(format)) {
289 return asString(Format.FULL, null);
290 }
291 }
292 return asString(null, null);
293 } else {
294 return asString(Format.FULL, null);
295 }
296 }
297
298 private String asXML() {
299 final StringBuilder sb = new StringBuilder();
300 final StructuredDataId id = getId();
301 if (id == null || id.getName() == null || type == null) {
302 return sb.toString();
303 }
304 sb.append("<StructuredData>\n");
305 sb.append("<type>").append(type).append("</type>\n");
306 sb.append("<id>").append(id).append("</id>\n");
307 super.asXML(sb);
308 sb.append("</StructuredData>\n");
309 return sb.toString();
310 }
311
312 @Override
313 public String toString() {
314 return asString(null, null);
315 }
316
317
318 @Override
319 public MapMessage newInstance(final Map<String, String> map) {
320 return new StructuredDataMessage(this, map);
321 }
322
323 @Override
324 public boolean equals(final Object o) {
325 if (this == o) {
326 return true;
327 }
328 if (o == null || getClass() != o.getClass()) {
329 return false;
330 }
331
332 final StructuredDataMessage that = (StructuredDataMessage) o;
333
334 if (!super.equals(o)) {
335 return false;
336 }
337 if (type != null ? !type.equals(that.type) : that.type != null) {
338 return false;
339 }
340 if (id != null ? !id.equals(that.id) : that.id != null) {
341 return false;
342 }
343 if (message != null ? !message.equals(that.message) : that.message != null) {
344 return false;
345 }
346
347 return true;
348 }
349
350 @Override
351 public int hashCode() {
352 int result = super.hashCode();
353 result = HASHVAL * result + (type != null ? type.hashCode() : 0);
354 result = HASHVAL * result + (id != null ? id.hashCode() : 0);
355 result = HASHVAL * result + (message != null ? message.hashCode() : 0);
356 return result;
357 }
358 }