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 org.apache.logging.log4j.Logger;
020    import org.apache.logging.log4j.status.StatusLogger;
021    
022    import java.io.IOException;
023    import java.io.ObjectInputStream;
024    import java.io.ObjectOutputStream;
025    import java.util.Arrays;
026    import java.util.IllegalFormatException;
027    
028    /**
029     * Handles messages that consist of a format string conforming to 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            if (!Arrays.equals(stringArgs, that.stringArgs)) {
110                return false;
111            }
112    
113            return true;
114        }
115    
116        @Override
117        public int hashCode() {
118            int result = messagePattern != null ? messagePattern.hashCode() : 0;
119            result = HASHVAL * result + (stringArgs != null ? Arrays.hashCode(stringArgs) : 0);
120            return result;
121        }
122    
123    
124        @Override
125        public String toString() {
126            return "StringFormatMessage[messagePattern=" + messagePattern + ", args=" +
127                Arrays.toString(argArray) +  "]";
128        }
129    
130        private void writeObject(final ObjectOutputStream out) throws IOException {
131            out.defaultWriteObject();
132            getFormattedMessage();
133            out.writeUTF(formattedMessage);
134            out.writeUTF(messagePattern);
135            out.writeInt(argArray.length);
136            stringArgs = new String[argArray.length];
137            int i = 0;
138            for (final Object obj : argArray) {
139                stringArgs[i] = obj.toString();
140                ++i;
141            }
142        }
143    
144        private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
145            in.defaultReadObject();
146            formattedMessage = in.readUTF();
147            messagePattern = in.readUTF();
148            final int length = in.readInt();
149            stringArgs = new String[length];
150            for (int i = 0; i < length; ++i) {
151                stringArgs[i] = in.readUTF();
152            }
153        }
154    
155        /**
156         * Return the throwable passed to the Message.
157         *
158         * @return the Throwable.
159         */
160        @Override
161        public Throwable getThrowable() {
162            return throwable;
163        }
164    }