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  
18  package org.apache.logging.log4j.message;
19  
20  import java.io.IOException;
21  import java.io.ObjectInputStream;
22  import java.io.ObjectOutputStream;
23  import java.text.MessageFormat;
24  import java.util.Arrays;
25  import java.util.IllegalFormatException;
26  import java.util.Locale;
27  
28  import org.apache.logging.log4j.Logger;
29  import org.apache.logging.log4j.status.StatusLogger;
30  
31  /**
32   * Handles messages that consist of a format string conforming to java.text.MessageFormat.
33   *
34   * @serial In version 2.1, due to a bug in the serialization format, the serialization format was changed along with
35   * its {@code serialVersionUID} value.
36   */
37  public class MessageFormatMessage implements Message {
38  
39      private static final Logger LOGGER = StatusLogger.getLogger();
40  
41      private static final long serialVersionUID = 1L;
42  
43      private static final int HASHVAL = 31;
44  
45      private String messagePattern;
46      private transient Object[] parameters;
47      private String[] serializedParameters;
48      private transient String formattedMessage;
49      private transient Throwable throwable;
50      private final Locale locale;
51  
52      /**
53       * Constructs a message.
54       * 
55       * @param locale the locale for this message format
56       * @param messagePattern the pattern for this message format
57       * @param parameters The objects to format
58       * @since 2.6
59       */
60      public MessageFormatMessage(final Locale locale, final String messagePattern, final Object... parameters) {
61          this.locale = locale;
62          this.messagePattern = messagePattern;
63          this.parameters = parameters;
64          final int length = parameters == null ? 0 : parameters.length;
65          if (length > 0 && parameters[length - 1] instanceof Throwable) {
66              this.throwable = (Throwable) parameters[length - 1];
67          }
68      }
69  
70      /**
71       * Constructs a message.
72       * 
73       * @param messagePattern the pattern for this message format
74       * @param parameters The objects to format
75       */
76      public MessageFormatMessage(final String messagePattern, final Object... parameters) {
77          this(Locale.getDefault(Locale.Category.FORMAT), messagePattern, parameters);
78      }
79  
80      /**
81       * Returns the formatted message.
82       * @return the formatted message.
83       */
84      @Override
85      public String getFormattedMessage() {
86          if (formattedMessage == null) {
87              formattedMessage = formatMessage(messagePattern, parameters);
88          }
89          return formattedMessage;
90      }
91  
92      /**
93       * Returns the message pattern.
94       * @return the message pattern.
95       */
96      @Override
97      public String getFormat() {
98          return messagePattern;
99      }
100 
101     /**
102      * Returns the message parameters.
103      * @return the message parameters.
104      */
105     @Override
106     public Object[] getParameters() {
107         if (parameters != null) {
108             return parameters;
109         }
110         return serializedParameters;
111     }
112 
113     protected String formatMessage(final String msgPattern, final Object... args) {
114         try {
115             final MessageFormat temp = new MessageFormat(msgPattern, locale);
116             return temp.format(args);
117         } catch (final IllegalFormatException ife) {
118             LOGGER.error("Unable to format msg: " + msgPattern, ife);
119             return msgPattern;
120         }
121     }
122 
123     @Override
124     public boolean equals(final Object o) {
125         if (this == o) {
126             return true;
127         }
128         if (o == null || getClass() != o.getClass()) {
129             return false;
130         }
131 
132         final MessageFormatMessage that = (MessageFormatMessage) o;
133 
134         if (messagePattern != null ? !messagePattern.equals(that.messagePattern) : that.messagePattern != null) {
135             return false;
136         }
137         return Arrays.equals(serializedParameters, that.serializedParameters);
138     }
139 
140     @Override
141     public int hashCode() {
142         int result = messagePattern != null ? messagePattern.hashCode() : 0;
143         result = HASHVAL * result + (serializedParameters != null ? Arrays.hashCode(serializedParameters) : 0);
144         return result;
145     }
146 
147 
148     @Override
149     public String toString() {
150         return getFormattedMessage();
151     }
152 
153     private void writeObject(final ObjectOutputStream out) throws IOException {
154         getFormattedMessage();
155         out.writeUTF(formattedMessage);
156         out.writeUTF(messagePattern);
157         final int length = parameters == null ? 0 : parameters.length;
158         out.writeInt(length);
159         serializedParameters = new String[length];
160         if (length > 0) {
161             for (int i = 0; i < length; i++) {
162                 serializedParameters[i] = String.valueOf(parameters[i]);
163                 out.writeUTF(serializedParameters[i]);
164             }
165         }
166     }
167 
168     private void readObject(final ObjectInputStream in) throws IOException {
169         parameters = null;
170         throwable = null;
171         formattedMessage = in.readUTF();
172         messagePattern = in.readUTF();
173         final int length = in.readInt();
174         serializedParameters = new String[length];
175         for (int i = 0; i < length; ++i) {
176             serializedParameters[i] = in.readUTF();
177         }
178     }
179 
180     /**
181      * Return the throwable passed to the Message.
182      *
183      * @return the Throwable.
184      */
185     @Override
186     public Throwable getThrowable() {
187         return throwable;
188     }
189 }