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    
018    package org.apache.logging.log4j.message;
019    
020    import java.io.IOException;
021    import java.io.ObjectInputStream;
022    import java.io.ObjectOutputStream;
023    import java.text.MessageFormat;
024    import java.util.Arrays;
025    import java.util.IllegalFormatException;
026    
027    import org.apache.logging.log4j.Logger;
028    import org.apache.logging.log4j.status.StatusLogger;
029    
030    /**
031     * Handles messages that consist of a format string conforming to java.text.MessageFormat.
032     *
033     * @serial In version 2.1, due to a bug in the serialization format, the serialization format was changed along with
034     * its {@code serialVersionUID} value.
035     */
036    public class MessageFormatMessage implements Message {
037    
038        private static final Logger LOGGER = StatusLogger.getLogger();
039    
040        private static final long serialVersionUID = 1L;
041    
042        private static final int HASHVAL = 31;
043    
044        private String messagePattern;
045        private transient Object[] parameters;
046        private String[] serializedParameters;
047        private transient String formattedMessage;
048        private transient Throwable throwable;
049    
050        public MessageFormatMessage(final String messagePattern, final Object... parameters) {
051            this.messagePattern = messagePattern;
052            this.parameters = parameters;
053            final int length = parameters == null ? 0 : parameters.length;
054            if (length > 0 && parameters[length - 1] instanceof Throwable) {
055                this.throwable = (Throwable) parameters[length - 1];
056            }
057        }
058    
059        /**
060         * Returns the formatted message.
061         * @return the formatted message.
062         */
063        @Override
064        public String getFormattedMessage() {
065            if (formattedMessage == null) {
066                formattedMessage = formatMessage(messagePattern, parameters);
067            }
068            return formattedMessage;
069        }
070    
071        /**
072         * Returns the message pattern.
073         * @return the message pattern.
074         */
075        @Override
076        public String getFormat() {
077            return messagePattern;
078        }
079    
080        /**
081         * Returns the message parameters.
082         * @return the message parameters.
083         */
084        @Override
085        public Object[] getParameters() {
086            if (parameters != null) {
087                return parameters;
088            }
089            return serializedParameters;
090        }
091    
092        protected String formatMessage(final String msgPattern, final Object... args) {
093            try {
094                return MessageFormat.format(msgPattern, args);
095            } catch (final IllegalFormatException ife) {
096                LOGGER.error("Unable to format msg: " + msgPattern, ife);
097                return msgPattern;
098            }
099        }
100    
101        @Override
102        public boolean equals(final Object o) {
103            if (this == o) {
104                return true;
105            }
106            if (o == null || getClass() != o.getClass()) {
107                return false;
108            }
109    
110            final MessageFormatMessage that = (MessageFormatMessage) o;
111    
112            if (messagePattern != null ? !messagePattern.equals(that.messagePattern) : that.messagePattern != null) {
113                return false;
114            }
115            return Arrays.equals(serializedParameters, that.serializedParameters);
116        }
117    
118        @Override
119        public int hashCode() {
120            int result = messagePattern != null ? messagePattern.hashCode() : 0;
121            result = HASHVAL * result + (serializedParameters != null ? Arrays.hashCode(serializedParameters) : 0);
122            return result;
123        }
124    
125    
126        @Override
127        public String toString() {
128            return "StringFormatMessage[messagePattern=" + messagePattern + ", args=" +
129                Arrays.toString(parameters) + ']';
130        }
131    
132        private void writeObject(final ObjectOutputStream out) throws IOException {
133            getFormattedMessage();
134            out.writeUTF(formattedMessage);
135            out.writeUTF(messagePattern);
136            final int length = parameters == null ? 0 : parameters.length;
137            out.writeInt(length);
138            serializedParameters = new String[length];
139            if (length > 0) {
140                for (int i = 0; i < length; i++) {
141                    serializedParameters[i] = String.valueOf(parameters[i]);
142                    out.writeUTF(serializedParameters[i]);
143                }
144            }
145        }
146    
147        private void readObject(final ObjectInputStream in) throws IOException {
148            parameters = null;
149            throwable = null;
150            formattedMessage = in.readUTF();
151            messagePattern = in.readUTF();
152            final int length = in.readInt();
153            serializedParameters = new String[length];
154            for (int i = 0; i < length; ++i) {
155                serializedParameters[i] = in.readUTF();
156            }
157        }
158    
159        /**
160         * Return the throwable passed to the Message.
161         *
162         * @return the Throwable.
163         */
164        @Override
165        public Throwable getThrowable() {
166            return throwable;
167        }
168    }