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, ParameterVisitable, Clearable {
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 if (argCount <= emptyReplacement.length) {
072                // Bad replacement! Too small, may blow up future 10-arg messages.
073                // copy params into the specified replacement array and return that
074                System.arraycopy(params, 0, emptyReplacement, 0, argCount);
075                // Do not retain references to objects in the reusable params array.
076                for (int i = 0; i < argCount; i++) {
077                    params[i] = null;
078                }
079                result = emptyReplacement;
080            } else {
081                // replacement array is too small for current content and future content: discard it
082                params = new Object[MAX_PARMS];
083            }
084        } else {
085            // The returned array will be reused by the caller in future swapParameter() calls.
086            // Therefore we want to avoid returning arrays with less than 10 elements.
087            // If the vararg array is less than 10 params we just copy its content into the specified array
088            // and return it. This helps the caller to retain a reusable array of at least 10 elements.
089            // NOTE: LOG4J2-1688 unearthed the use case that an application array (not a varargs array) is passed
090            // as the argument array. This array should not be modified, so it cannot be passed to the caller
091            // who will at some point null out the elements in the array).
092            if (argCount <= emptyReplacement.length) {
093                result = emptyReplacement;
094            } else {
095                result = new Object[argCount]; // LOG4J2-1688
096            }
097            // copy params into the specified replacement array and return that
098            System.arraycopy(varargs, 0, result, 0, argCount);
099        }
100        return result;
101    }
102
103    // see interface javadoc
104    @Override
105    public short getParameterCount() {
106        return (short) argCount;
107    }
108
109    @Override
110    public <S> void forEachParameter(final ParameterConsumer<S> action, final S state) {
111        final Object[] parameters = getParams();
112        for (short i = 0; i < argCount; i++) {
113            action.accept(parameters[i], i, state);
114        }
115    }
116
117    @Override
118    public Message memento() {
119        return new ParameterizedMessage(messagePattern, getTrimmedParams());
120    }
121
122    private void init(final String messagePattern, final int argCount, final Object[] paramArray) {
123        this.varargs = null;
124        this.messagePattern = messagePattern;
125        this.argCount = argCount;
126        final int placeholderCount = count(messagePattern, indices);
127        initThrowable(paramArray, argCount, placeholderCount);
128        this.usedCount = Math.min(placeholderCount, argCount);
129    }
130
131    private static int count(final String messagePattern, final int[] indices) {
132        try {
133            // try the fast path first
134            return ParameterFormatter.countArgumentPlaceholders2(messagePattern, indices);
135        } catch (final Exception ex) { // fallback if more than int[] length (256) parameter placeholders
136            return ParameterFormatter.countArgumentPlaceholders(messagePattern);
137        }
138    }
139
140    private void initThrowable(final Object[] params, final int argCount, final int usedParams) {
141        if (usedParams < argCount && params[argCount - 1] instanceof Throwable) {
142            this.throwable = (Throwable) params[argCount - 1];
143        } else {
144            this.throwable = null;
145        }
146    }
147
148    ReusableParameterizedMessage set(final String messagePattern, final Object... arguments) {
149        init(messagePattern, arguments == null ? 0 : arguments.length, arguments);
150        varargs = arguments;
151        return this;
152    }
153
154    ReusableParameterizedMessage set(final String messagePattern, final Object p0) {
155        params[0] = p0;
156        init(messagePattern, 1, params);
157        return this;
158    }
159
160    ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1) {
161        params[0] = p0;
162        params[1] = p1;
163        init(messagePattern, 2, params);
164        return this;
165    }
166
167    ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2) {
168        params[0] = p0;
169        params[1] = p1;
170        params[2] = p2;
171        init(messagePattern, 3, params);
172        return this;
173    }
174
175    ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3) {
176        params[0] = p0;
177        params[1] = p1;
178        params[2] = p2;
179        params[3] = p3;
180        init(messagePattern, 4, 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) {
185        params[0] = p0;
186        params[1] = p1;
187        params[2] = p2;
188        params[3] = p3;
189        params[4] = p4;
190        init(messagePattern, 5, params);
191        return this;
192    }
193
194    ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) {
195        params[0] = p0;
196        params[1] = p1;
197        params[2] = p2;
198        params[3] = p3;
199        params[4] = p4;
200        params[5] = p5;
201        init(messagePattern, 6, params);
202        return this;
203    }
204
205    ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
206            final Object p6) {
207        params[0] = p0;
208        params[1] = p1;
209        params[2] = p2;
210        params[3] = p3;
211        params[4] = p4;
212        params[5] = p5;
213        params[6] = p6;
214        init(messagePattern, 7, params);
215        return this;
216    }
217
218    ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
219            final Object p6, final Object p7) {
220        params[0] = p0;
221        params[1] = p1;
222        params[2] = p2;
223        params[3] = p3;
224        params[4] = p4;
225        params[5] = p5;
226        params[6] = p6;
227        params[7] = p7;
228        init(messagePattern, 8, params);
229        return this;
230    }
231
232    ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
233            final Object p6, final Object p7, final Object p8) {
234        params[0] = p0;
235        params[1] = p1;
236        params[2] = p2;
237        params[3] = p3;
238        params[4] = p4;
239        params[5] = p5;
240        params[6] = p6;
241        params[7] = p7;
242        params[8] = p8;
243        init(messagePattern, 9, params);
244        return this;
245    }
246
247    ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
248            final Object p6, final Object p7, final Object p8, final Object p9) {
249        params[0] = p0;
250        params[1] = p1;
251        params[2] = p2;
252        params[3] = p3;
253        params[4] = p4;
254        params[5] = p5;
255        params[6] = p6;
256        params[7] = p7;
257        params[8] = p8;
258        params[9] = p9;
259        init(messagePattern, 10, params);
260        return this;
261    }
262
263    /**
264     * Returns the message pattern.
265     * @return the message pattern.
266     */
267    @Override
268    public String getFormat() {
269        return messagePattern;
270    }
271
272    /**
273     * Returns the message parameters.
274     * @return the message parameters.
275     */
276    @Override
277    public Object[] getParameters() {
278        return getTrimmedParams();
279    }
280
281    /**
282     * Returns the Throwable that was given as the last argument, if any.
283     * It will not survive serialization. The Throwable exists as part of the message
284     * primarily so that it can be extracted from the end of the list of parameters
285     * and then be added to the LogEvent. As such, the Throwable in the event should
286     * not be used once the LogEvent has been constructed.
287     *
288     * @return the Throwable, if any.
289     */
290    @Override
291    public Throwable getThrowable() {
292        return throwable;
293    }
294
295    /**
296     * Returns the formatted message.
297     * @return the formatted message.
298     */
299    @Override
300    public String getFormattedMessage() {
301        final StringBuilder sb = getBuffer();
302        formatTo(sb);
303        final String result = sb.toString();
304        StringBuilders.trimToMaxSize(sb, Constants.MAX_REUSABLE_MESSAGE_SIZE);
305        return result;
306    }
307
308    private StringBuilder getBuffer() {
309        if (buffer == null) {
310            buffer = new ThreadLocal<>();
311        }
312        StringBuilder result = buffer.get();
313        if (result == null) {
314            final int currentPatternLength = messagePattern == null ? 0 : messagePattern.length();
315            result = new StringBuilder(Math.max(MIN_BUILDER_SIZE, currentPatternLength * 2));
316            buffer.set(result);
317        }
318        result.setLength(0);
319        return result;
320    }
321
322    @Override
323    public void formatTo(final StringBuilder builder) {
324        if (indices[0] < 0) {
325            ParameterFormatter.formatMessage(builder, messagePattern, getParams(), argCount);
326        } else {
327            ParameterFormatter.formatMessage2(builder, messagePattern, getParams(), usedCount, indices);
328        }
329    }
330
331    /**
332     * Sets the reserved flag to true and returns this object.
333     * @return this object
334     * @since 2.7
335     */
336    ReusableParameterizedMessage reserve() {
337        reserved = true;
338        return this;
339    }
340
341    @Override
342    public String toString() {
343        return "ReusableParameterizedMessage[messagePattern=" + getFormat() + ", stringArgs=" +
344                Arrays.toString(getParameters()) + ", throwable=" + getThrowable() + ']';
345    }
346
347    @Override
348    public void clear() { // LOG4J2-1583
349        // This method does not clear parameter values, those are expected to be swapped to a
350        // reusable message, which is responsible for clearing references.
351        reserved = false;
352        varargs = null;
353        messagePattern = null;
354        throwable = null;
355    }
356}