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.IOException;
20  import java.io.ObjectInputStream;
21  import java.io.ObjectOutputStream;
22  import java.util.Arrays;
23  import java.util.IllegalFormatException;
24  import java.util.Locale;
25  
26  import org.apache.logging.log4j.Logger;
27  import org.apache.logging.log4j.status.StatusLogger;
28  
29  /**
30   * Handles messages that consist of a format string conforming to {@link java.util.Formatter}.
31   * 
32   * <h4>Note to implementors</h4>
33   * <p>
34   * This class implements the unrolled args API even though StringFormattedMessage does not. This leaves the room for
35   * StringFormattedMessage to unroll itself later.
36   * </p>
37   */
38  public class StringFormattedMessage implements Message {
39  
40      private static final Logger LOGGER = StatusLogger.getLogger();
41  
42      private static final long serialVersionUID = -665975803997290697L;
43  
44      private static final int HASHVAL = 31;
45  
46      private String messagePattern;
47      private transient Object[] argArray;
48      private String[] stringArgs;
49      private transient String formattedMessage;
50      private transient Throwable throwable;
51      private final Locale locale;
52      
53     /**
54      * Constructs a message.
55      * 
56      * @param locale the locale for this message format
57      * @param messagePattern the pattern for this message format
58      * @param arguments The objects to format
59      * @since 2.6
60      */
61      public StringFormattedMessage(final Locale locale, final String messagePattern, final Object... arguments) {
62          this.locale = locale;
63          this.messagePattern = messagePattern;
64          this.argArray = arguments;
65          if (arguments != null && arguments.length > 0 && arguments[arguments.length - 1] instanceof Throwable) {
66              this.throwable = (Throwable) arguments[arguments.length - 1];
67          }
68      }
69  
70      /**
71       * Constructs a message.
72       * 
73       * @param messagePattern the pattern for this message format
74       * @param arguments The objects to format
75       * @since 2.6
76       */
77      public StringFormattedMessage(final String messagePattern, final Object... arguments) {
78          this(Locale.getDefault(Locale.Category.FORMAT), messagePattern, arguments);
79      }
80  
81      /**
82       * Returns the formatted message.
83       * @return the formatted message.
84       */
85      @Override
86      public String getFormattedMessage() {
87          if (formattedMessage == null) {
88              formattedMessage = formatMessage(messagePattern, argArray);
89          }
90          return formattedMessage;
91      }
92  
93      /**
94       * Returns the message pattern.
95       * @return the message pattern.
96       */
97      @Override
98      public String getFormat() {
99          return messagePattern;
100     }
101 
102     /**
103      * Returns the message parameters.
104      * @return the message parameters.
105      */
106     @Override
107     public Object[] getParameters() {
108         if (argArray != null) {
109             return argArray;
110         }
111         return stringArgs;
112     }
113 
114     protected String formatMessage(final String msgPattern, final Object... args) {
115         try {
116             return String.format(locale, msgPattern, 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 StringFormattedMessage that = (StringFormattedMessage) o;
133 
134         if (messagePattern != null ? !messagePattern.equals(that.messagePattern) : that.messagePattern != null) {
135             return false;
136         }
137 
138         return Arrays.equals(stringArgs, that.stringArgs);
139     }
140 
141     @Override
142     public int hashCode() {
143         int result = messagePattern != null ? messagePattern.hashCode() : 0;
144         result = HASHVAL * result + (stringArgs != null ? Arrays.hashCode(stringArgs) : 0);
145         return result;
146     }
147 
148 
149     @Override
150     public String toString() {
151         return getFormattedMessage();
152     }
153 
154     private void writeObject(final ObjectOutputStream out) throws IOException {
155         out.defaultWriteObject();
156         getFormattedMessage();
157         out.writeUTF(formattedMessage);
158         out.writeUTF(messagePattern);
159         out.writeInt(argArray.length);
160         stringArgs = new String[argArray.length];
161         int i = 0;
162         for (final Object obj : argArray) {
163             final String string = String.valueOf(obj);
164             stringArgs[i] = string;
165             out.writeUTF(string);
166             ++i;
167         }
168     }
169 
170     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
171         in.defaultReadObject();
172         formattedMessage = in.readUTF();
173         messagePattern = in.readUTF();
174         final int length = in.readInt();
175         stringArgs = new String[length];
176         for (int i = 0; i < length; ++i) {
177             stringArgs[i] = in.readUTF();
178         }
179     }
180 
181     /**
182      * Return the throwable passed to the Message.
183      *
184      * @return the Throwable.
185      */
186     @Override
187     public Throwable getThrowable() {
188         return throwable;
189     }
190 }