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