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
018package org.apache.logging.log4j.message;
019
020import java.io.IOException;
021import java.io.ObjectInputStream;
022import java.io.ObjectOutputStream;
023import java.text.MessageFormat;
024import java.util.Arrays;
025import java.util.IllegalFormatException;
026
027import org.apache.logging.log4j.Logger;
028import 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 */
036public 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}