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 *      https://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.commons.pool2.impl;
018
019import java.io.PrintWriter;
020import java.text.DateFormat;
021import java.text.SimpleDateFormat;
022import java.time.Instant;
023import java.time.format.DateTimeFormatter;
024
025/**
026 * CallStack strategy that uses the stack trace from a {@link Throwable}. This strategy, while slower than the
027 * SecurityManager implementation, provides call stack method names and other metadata in addition to the call stack
028 * of classes.
029 *
030 * @see Throwable#fillInStackTrace()
031 * @since 2.4.3
032 */
033public class ThrowableCallStack implements CallStack {
034
035    /**
036     * A snapshot of a throwable.
037     */
038    private static final class Snapshot extends Throwable {
039
040        private static final long serialVersionUID = 1L;
041        private final Instant timestamp;
042
043        /**
044         * Constructs a new instance with its message set to the now instant.
045         */
046        private Snapshot() {
047            this(Instant.now());
048        }
049
050        /**
051         * Constructs a new instance and use the timestamp as the message with using {@link DateTimeFormatter#ISO_INSTANT} for more precision.
052         *
053         * @param timestamp normally the now instant.
054         */
055        private Snapshot(final Instant timestamp) {
056            super(timestamp.toString());
057            this.timestamp = timestamp;
058        }
059    }
060
061    private final String messageFormat;
062
063    // We keep the SimpleDateFormat for backward compatibility instead of a DateTimeFormatter.
064    //@GuardedBy("dateFormat")
065    private final DateFormat dateFormat;
066
067    private volatile Snapshot snapshot;
068
069    /**
070     * Creates a new instance.
071     *
072     * @param messageFormat message format
073     * @param useTimestamp whether to format the dates in the output message or not
074     */
075    public ThrowableCallStack(final String messageFormat, final boolean useTimestamp) {
076        this.messageFormat = messageFormat;
077        this.dateFormat = useTimestamp ? new SimpleDateFormat(messageFormat) : null;
078    }
079
080    @Override
081    public void clear() {
082        snapshot = null;
083    }
084
085    @Override
086    public void fillInStackTrace() {
087        snapshot = new Snapshot();
088    }
089
090    @Override
091    public synchronized boolean printStackTrace(final PrintWriter writer) {
092        final Snapshot snapshotRef = this.snapshot;
093        if (snapshotRef == null) {
094            return false;
095        }
096        final String message;
097        if (dateFormat == null) {
098            message = messageFormat;
099        } else {
100            synchronized (dateFormat) {
101                // The throwable message is in {@link DateTimeFormatter#ISO_INSTANT} format for more precision.
102                message = dateFormat.format(Long.valueOf(snapshotRef.timestamp.toEpochMilli()));
103            }
104        }
105        writer.println(message);
106        snapshotRef.printStackTrace(writer);
107        return true;
108    }
109}