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