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 */
017package org.apache.logging.log4j.message;
018
019import java.util.Arrays;
020
021import org.apache.logging.log4j.util.Constants;
022import org.apache.logging.log4j.util.PerformanceSensitive;
023import org.apache.logging.log4j.util.StringBuilders;
024
025/**
026 * Reusable parameterized message. This message is mutable and is not safe to be accessed or modified by multiple
027 * threads concurrently.
028 *
029 * @see ParameterizedMessage
030 * @since 2.6
031 */
032@PerformanceSensitive("allocation")
033public class ReusableParameterizedMessage implements ReusableMessage {
034
035    private static final int MIN_BUILDER_SIZE = 512;
036    private static final int MAX_PARMS = 10;
037    private static final long serialVersionUID = 7800075879295123856L;
038    private transient ThreadLocal<StringBuilder> buffer; // non-static: LOG4J2-1583
039
040    private String messagePattern;
041    private int argCount;
042    private int usedCount;
043    private final int[] indices = new int[256];
044    private transient Object[] varargs;
045    private transient Object[] params = new Object[MAX_PARMS];
046    private transient Throwable throwable;
047    transient boolean reserved = false; // LOG4J2-1583 prevent scrambled logs with nested logging calls
048
049    /**
050     * Creates a reusable message.
051     */
052    public ReusableParameterizedMessage() {
053    }
054
055    private Object[] getTrimmedParams() {
056        return varargs == null ? Arrays.copyOf(params, argCount) : varargs;
057    }
058
059    private Object[] getParams() {
060        return varargs == null ? params : varargs;
061    }
062
063    // see interface javadoc
064    @Override
065    public Object[] swapParameters(final Object[] emptyReplacement) {
066        Object[] result;
067        if (varargs == null) {
068            result = params;
069            if (emptyReplacement.length >= MAX_PARMS) {
070                params = emptyReplacement;
071            } else {
072                // Bad replacement! Too small, may blow up future 10-arg messages.
073                if (argCount <= emptyReplacement.length) {
074                    // copy params into the specified replacement array and return that
075                    System.arraycopy(params, 0, emptyReplacement, 0, argCount);
076                    result = emptyReplacement;
077                } else {
078                    // replacement array is too small for current content and future content: discard it
079                    params = new Object[MAX_PARMS];
080                }
081            }
082        } else {
083            // The returned array will be reused by the caller in future swapParameter() calls.
084            // Therefore we want to avoid returning arrays with less than 10 elements.
085            // If the vararg array is less than 10 params we just copy its content into the specified array
086            // and return it. This helps the caller to retain a reusable array of at least 10 elements.
087            // NOTE: LOG4J2-1688 unearthed the use case that an application array (not a varargs array) is passed
088            // as the argument array. This array should not be modified, so it cannot be passed to the caller
089            // who will at some point null out the elements in the array).
090            if (argCount <= emptyReplacement.length) {
091                result = emptyReplacement;
092            } else {
093                result = new Object[argCount]; // LOG4J2-1688
094            }
095            // copy params into the specified replacement array and return that
096            System.arraycopy(varargs, 0, result, 0, argCount);
097        }
098        return result;
099    }
100
101    // see interface javadoc
102    @Override
103    public short getParameterCount() {
104        return (short) argCount;
105    }
106
107    @Override
108    public Message memento() {
109        return new ParameterizedMessage(messagePattern, getTrimmedParams());
110    }
111
112    private void init(final String messagePattern, final int argCount, final Object[] paramArray) {
113        this.varargs = null;
114        this.messagePattern = messagePattern;
115        this.argCount = argCount;
116        final int placeholderCount = count(messagePattern, indices);
117        initThrowable(paramArray, argCount, placeholderCount);
118        this.usedCount = Math.min(placeholderCount, argCount);
119    }
120
121    private static int count(final String messagePattern, final int[] indices) {
122        try {
123            // try the fast path first
124            return ParameterFormatter.countArgumentPlaceholders2(messagePattern, indices);
125        } catch (final Exception ex) { // fallback if more than int[] length (256) parameter placeholders
126            return ParameterFormatter.countArgumentPlaceholders(messagePattern);
127        }
128    }
129
130    private void initThrowable(final Object[] params, final int argCount, final int usedParams) {
131        if (usedParams < argCount && params[argCount - 1] instanceof Throwable) {
132            this.throwable = (Throwable) params[argCount - 1];
133        } else {
134            this.throwable = null;
135        }
136    }
137
138    ReusableParameterizedMessage set(final String messagePattern, final Object... arguments) {
139        init(messagePattern, arguments == null ? 0 : arguments.length, arguments);
140        varargs = arguments;
141        return this;
142    }
143
144    ReusableParameterizedMessage set(final String messagePattern, final Object p0) {
145        params[0] = p0;
146        init(messagePattern, 1, params);
147        return this;
148    }
149
150    ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1) {
151        params[0] = p0;
152        params[1] = p1;
153        init(messagePattern, 2, params);
154        return this;
155    }
156
157    ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2) {
158        params[0] = p0;
159        params[1] = p1;
160        params[2] = p2;
161        init(messagePattern, 3, params);
162        return this;
163    }
164
165    ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3) {
166        params[0] = p0;
167        params[1] = p1;
168        params[2] = p2;
169        params[3] = p3;
170        init(messagePattern, 4, params);
171        return this;
172    }
173
174    ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) {
175        params[0] = p0;
176        params[1] = p1;
177        params[2] = p2;
178        params[3] = p3;
179        params[4] = p4;
180        init(messagePattern, 5, params);
181        return this;
182    }
183
184    ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) {
185        params[0] = p0;
186        params[1] = p1;
187        params[2] = p2;
188        params[3] = p3;
189        params[4] = p4;
190        params[5] = p5;
191        init(messagePattern, 6, params);
192        return this;
193    }
194
195    ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
196            final Object p6) {
197        params[0] = p0;
198        params[1] = p1;
199        params[2] = p2;
200        params[3] = p3;
201        params[4] = p4;
202        params[5] = p5;
203        params[6] = p6;
204        init(messagePattern, 7, params);
205        return this;
206    }
207
208    ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
209            final Object p6, final Object p7) {
210        params[0] = p0;
211        params[1] = p1;
212        params[2] = p2;
213        params[3] = p3;
214        params[4] = p4;
215        params[5] = p5;
216        params[6] = p6;
217        params[7] = p7;
218        init(messagePattern, 8, params);
219        return this;
220    }
221
222    ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
223            final Object p6, final Object p7, final Object p8) {
224        params[0] = p0;
225        params[1] = p1;
226        params[2] = p2;
227        params[3] = p3;
228        params[4] = p4;
229        params[5] = p5;
230        params[6] = p6;
231        params[7] = p7;
232        params[8] = p8;
233        init(messagePattern, 9, params);
234        return this;
235    }
236
237    ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
238            final Object p6, final Object p7, final Object p8, final Object p9) {
239        params[0] = p0;
240        params[1] = p1;
241        params[2] = p2;
242        params[3] = p3;
243        params[4] = p4;
244        params[5] = p5;
245        params[6] = p6;
246        params[7] = p7;
247        params[8] = p8;
248        params[9] = p9;
249        init(messagePattern, 10, params);
250        return this;
251    }
252
253    /**
254     * Returns the message pattern.
255     * @return the message pattern.
256     */
257    @Override
258    public String getFormat() {
259        return messagePattern;
260    }
261
262    /**
263     * Returns the message parameters.
264     * @return the message parameters.
265     */
266    @Override
267    public Object[] getParameters() {
268        return getTrimmedParams();
269    }
270
271    /**
272     * Returns the Throwable that was given as the last argument, if any.
273     * It will not survive serialization. The Throwable exists as part of the message
274     * primarily so that it can be extracted from the end of the list of parameters
275     * and then be added to the LogEvent. As such, the Throwable in the event should
276     * not be used once the LogEvent has been constructed.
277     *
278     * @return the Throwable, if any.
279     */
280    @Override
281    public Throwable getThrowable() {
282        return throwable;
283    }
284
285    /**
286     * Returns the formatted message.
287     * @return the formatted message.
288     */
289    @Override
290    public String getFormattedMessage() {
291        final StringBuilder sb = getBuffer();
292        formatTo(sb);
293        final String result = sb.toString();
294        StringBuilders.trimToMaxSize(sb, Constants.MAX_REUSABLE_MESSAGE_SIZE);
295        return result;
296    }
297
298    private StringBuilder getBuffer() {
299        if (buffer == null) {
300            buffer = new ThreadLocal<>();
301        }
302        StringBuilder result = buffer.get();
303        if (result == null) {
304            final int currentPatternLength = messagePattern == null ? 0 : messagePattern.length();
305            result = new StringBuilder(Math.max(MIN_BUILDER_SIZE, currentPatternLength * 2));
306            buffer.set(result);
307        }
308        result.setLength(0);
309        return result;
310    }
311
312    @Override
313    public void formatTo(final StringBuilder builder) {
314        if (indices[0] < 0) {
315            ParameterFormatter.formatMessage(builder, messagePattern, getParams(), argCount);
316        } else {
317            ParameterFormatter.formatMessage2(builder, messagePattern, getParams(), usedCount, indices);
318        }
319    }
320
321    /**
322     * Sets the reserved flag to true and returns this object.
323     * @return this object
324     * @since 2.7
325     */
326    ReusableParameterizedMessage reserve() {
327        reserved = true;
328        return this;
329    }
330
331    @Override
332    public String toString() {
333        return "ReusableParameterizedMessage[messagePattern=" + getFormat() + ", stringArgs=" +
334                Arrays.toString(getParameters()) + ", throwable=" + getThrowable() + ']';
335    }
336}