Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id F2656200C2D for ; Sat, 4 Mar 2017 21:18:22 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id EF894160B71; Sat, 4 Mar 2017 20:18:22 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id A0542160B61 for ; Sat, 4 Mar 2017 21:18:21 +0100 (CET) Received: (qmail 76507 invoked by uid 500); 4 Mar 2017 20:18:20 -0000 Mailing-List: contact commits-help@commons.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@commons.apache.org Delivered-To: mailing list commits@commons.apache.org Received: (qmail 76498 invoked by uid 99); 4 Mar 2017 20:18:20 -0000 Received: from Unknown (HELO svn01-us-west.apache.org) (209.188.14.144) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 04 Mar 2017 20:18:20 +0000 Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id 378073A059F for ; Sat, 4 Mar 2017 20:18:20 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1785520 - in /commons/proper/pool/trunk/src: changes/ main/java/org/apache/commons/pool2/impl/ test/java/org/apache/commons/pool2/impl/ Date: Sat, 04 Mar 2017 20:18:19 -0000 To: commits@commons.apache.org From: mattsicker@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20170304201820.378073A059F@svn01-us-west.apache.org> archived-at: Sat, 04 Mar 2017 20:18:23 -0000 Author: mattsicker Date: Sat Mar 4 20:18:19 2017 New Revision: 1785520 URL: http://svn.apache.org/viewvc?rev=1785520&view=rev Log: [POOL-320]: Use more efficient stack walking mechanisms for usage tracking Added: commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/CallStack.java commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/CallStackUtils.java commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/SecurityManagerCallStack.java commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/ThrowableCallStack.java commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/CallStackTest.java Modified: commons/proper/pool/trunk/src/changes/changes.xml commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/DefaultPooledObject.java commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObjectInfo.java Modified: commons/proper/pool/trunk/src/changes/changes.xml URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/changes/changes.xml?rev=1785520&r1=1785519&r2=1785520&view=diff ============================================================================== --- commons/proper/pool/trunk/src/changes/changes.xml (original) +++ commons/proper/pool/trunk/src/changes/changes.xml Sat Mar 4 20:18:19 2017 @@ -83,6 +83,9 @@ The type attribute can be add,u Ensure that a call to GKOP preparePool() takes account of other threads that might create objects concurrently, particularly the Evictor. + + Use more efficient stack walking mechanisms for usage tracking when possible. + Added: commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/CallStack.java URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/CallStack.java?rev=1785520&view=auto ============================================================================== --- commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/CallStack.java (added) +++ commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/CallStack.java Sat Mar 4 20:18:19 2017 @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.commons.pool2.impl; + +import org.apache.commons.pool2.PooledObject; +import org.apache.commons.pool2.UsageTracking; + +import java.io.PrintWriter; + +/** + * Strategy for obtaining and printing the current call stack. This is primarily useful for + * {@linkplain UsageTracking usage tracking} so that different JVMs and configurations can use more efficient strategies + * for obtaining the current call stack. + * + * @see CallStackUtils + * @since 2.4.3 + */ +public interface CallStack { + + /** + * Prints the current stack trace if available to a PrintWriter. The format is undefined and is primarily useful + * for debugging issues with {@link PooledObject} usage in user code. + * + * @param writer a PrintWriter to write the curren stack trace to if available + * @return true if a stack trace was available to print or false if nothing was printed + */ + boolean printStackTrace(final PrintWriter writer); + + /** + * Takes a snapshot of the current call stack. Subsequent calls to {@link #printStackTrace(PrintWriter)} will print + * out that stack trace until it is {@linkplain #clear() cleared}. + */ + void fillInStackTrace(); + + /** + * Clears the current stack trace snapshot. Subsequent calls to {@link #printStackTrace(PrintWriter)} will be + * no-ops until another call to {@link #fillInStackTrace()}. + */ + void clear(); +} Added: commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/CallStackUtils.java URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/CallStackUtils.java?rev=1785520&view=auto ============================================================================== --- commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/CallStackUtils.java (added) +++ commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/CallStackUtils.java Sat Mar 4 20:18:19 2017 @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.commons.pool2.impl; + +import java.security.AccessControlException; + +/** + * Utility methods for {@link CallStack}. + * + * @since 2.4.3 + */ +public final class CallStackUtils { + + private static final boolean CAN_CREATE_SECURITY_MANAGER; + + static { + CAN_CREATE_SECURITY_MANAGER = canCreateSecurityManager(); + } + + private static boolean canCreateSecurityManager() { + SecurityManager manager = System.getSecurityManager(); + if (manager == null) { + return true; + } + try { + manager.checkPermission(new RuntimePermission("createSecurityManager")); + return true; + } catch (final AccessControlException ignored) { + return false; + } + } + + /** + * Constructs a new {@link CallStack} using the fastest allowed strategy. + * + * @param messageFormat message (or format) to print first in stack traces + * @param useTimestamp if true, interpret message as a SimpleDateFormat and print the created timestamp; otherwise, + * print message format literally + * @return a new CallStack + */ + public static CallStack newCallStack(final String messageFormat, final boolean useTimestamp) { + return CAN_CREATE_SECURITY_MANAGER ? new SecurityManagerCallStack(messageFormat, useTimestamp) + : new ThrowableCallStack(messageFormat, useTimestamp); + } + + private CallStackUtils() { + } +} Modified: commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/DefaultPooledObject.java URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/DefaultPooledObject.java?rev=1785520&r1=1785519&r2=1785520&view=diff ============================================================================== --- commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/DefaultPooledObject.java (original) +++ commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/DefaultPooledObject.java Sat Mar 4 20:18:19 2017 @@ -16,15 +16,13 @@ */ package org.apache.commons.pool2.impl; -import java.io.PrintWriter; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Deque; - import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectState; import org.apache.commons.pool2.TrackedUse; +import java.io.PrintWriter; +import java.util.Deque; + /** * This wrapper is used to track the additional information, such as state, for * the pooled objects. @@ -46,8 +44,10 @@ public class DefaultPooledObject impl private volatile long lastUseTime = createTime; private volatile long lastReturnTime = createTime; private volatile boolean logAbandoned = false; - private volatile Exception borrowedBy = null; - private volatile Exception usedBy = null; + private final CallStack borrowedBy = CallStackUtils.newCallStack("'Pooled object created' " + + "yyyy-MM-dd HH:mm:ss Z 'by the following code has not been returned to the pool:'", true); + private final CallStack usedBy = CallStackUtils.newCallStack("The last code to use this object was:", + false); private volatile long borrowedCount = 0; /** @@ -193,7 +193,7 @@ public class DefaultPooledObject impl lastUseTime = lastBorrowTime; borrowedCount++; if (logAbandoned) { - borrowedBy = new AbandonedObjectCreatedException(); + borrowedBy.fillInStackTrace(); } return true; } else if (state == PooledObjectState.EVICTION) { @@ -218,7 +218,7 @@ public class DefaultPooledObject impl state == PooledObjectState.RETURNING) { state = PooledObjectState.IDLE; lastReturnTime = System.currentTimeMillis(); - borrowedBy = null; + borrowedBy.clear(); return true; } @@ -236,22 +236,13 @@ public class DefaultPooledObject impl @Override public void use() { lastUseTime = System.currentTimeMillis(); - usedBy = new Exception("The last code to use this object was:"); + usedBy.fillInStackTrace(); } @Override public void printStackTrace(final PrintWriter writer) { - boolean written = false; - final Exception borrowedByCopy = this.borrowedBy; - if (borrowedByCopy != null) { - borrowedByCopy.printStackTrace(writer); - written = true; - } - final Exception usedByCopy = this.usedBy; - if (usedByCopy != null) { - usedByCopy.printStackTrace(writer); - written = true; - } + boolean written = borrowedBy.printStackTrace(writer); + written |= usedBy.printStackTrace(writer); if (written) { writer.flush(); } @@ -287,42 +278,4 @@ public class DefaultPooledObject impl this.logAbandoned = logAbandoned; } - /** - * Used to track how an object was obtained from the pool (the stack trace - * of the exception will show which code borrowed the object) and when the - * object was borrowed. - */ - static class AbandonedObjectCreatedException extends Exception { - - private static final long serialVersionUID = 7398692158058772916L; - - /** Date format */ - //@GuardedBy("format") - private static final SimpleDateFormat format = new SimpleDateFormat - ("'Pooled object created' yyyy-MM-dd HH:mm:ss Z " + - "'by the following code has not been returned to the pool:'"); - - private final long _createdTime; - - /** - * Create a new instance. - *

- * @see Exception#Exception() - */ - public AbandonedObjectCreatedException() { - super(); - _createdTime = System.currentTimeMillis(); - } - - // Override getMessage to avoid creating objects and formatting - // dates unless the log message will actually be used. - @Override - public String getMessage() { - String msg; - synchronized(format) { - msg = format.format(new Date(_createdTime)); - } - return msg; - } - } } Added: commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/SecurityManagerCallStack.java URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/SecurityManagerCallStack.java?rev=1785520&view=auto ============================================================================== --- commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/SecurityManagerCallStack.java (added) +++ commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/SecurityManagerCallStack.java Sat Mar 4 20:18:19 2017 @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.commons.pool2.impl; + +import java.io.PrintWriter; +import java.lang.ref.WeakReference; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; + +/** + * CallStack strategy using a {@link SecurityManager}. Obtaining the current call stack is much faster via a + * SecurityManger, but access to the underlying method may be restricted by the current SecurityManager. In environments + * where a SecurityManager cannot be created, {@link ThrowableCallStack} should be used instead. + * + * @see RuntimePermission + * @see SecurityManager#getClassContext() + * @since 2.4.3 + */ +public class SecurityManagerCallStack implements CallStack { + + private final String messageFormat; + //@GuardedBy("dateFormat") + private final DateFormat dateFormat; + private final PrivateSecurityManager securityManager; + + private volatile Snapshot snapshot; + + public SecurityManagerCallStack(final String messageFormat, final boolean useTimestamp) { + this.messageFormat = messageFormat; + this.dateFormat = useTimestamp ? new SimpleDateFormat(messageFormat) : null; + this.securityManager = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public PrivateSecurityManager run() { + return new PrivateSecurityManager(); + } + }); + } + + @Override + public boolean printStackTrace(PrintWriter writer) { + final Snapshot snapshot = this.snapshot; + if (snapshot == null) { + return false; + } + final String message; + if (dateFormat == null) { + message = messageFormat; + } else { + synchronized (dateFormat) { + message = dateFormat.format(snapshot.timestamp); + } + } + writer.println(message); + for (final WeakReference> reference : snapshot.stack) { + writer.println(reference.get()); + } + return true; + } + + @Override + public void fillInStackTrace() { + snapshot = new Snapshot(securityManager.getCallStack()); + } + + @Override + public void clear() { + snapshot = null; + } + + private static class PrivateSecurityManager extends SecurityManager { + private List>> getCallStack() { + final Class[] classes = getClassContext(); + final List>> stack = new ArrayList>>(classes.length); + for (final Class klass : classes) { + stack.add(new WeakReference>(klass)); + } + return stack; + } + } + + private static class Snapshot { + private final long timestamp = System.currentTimeMillis(); + private final List>> stack; + + private Snapshot(List>> stack) { + this.stack = stack; + } + } +} Added: commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/ThrowableCallStack.java URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/ThrowableCallStack.java?rev=1785520&view=auto ============================================================================== --- commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/ThrowableCallStack.java (added) +++ commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/ThrowableCallStack.java Sat Mar 4 20:18:19 2017 @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.commons.pool2.impl; + +import java.io.PrintWriter; +import java.text.DateFormat; +import java.text.SimpleDateFormat; + +/** + * CallStack strategy that uses the stack trace from a {@link Throwable}. While being the most portable method of + * obtaining the current call stack, this is also the slowest way to do it. In environments where a new SecurityManager + * can be created, it is preferred to use {@link SecurityManagerCallStack}. + * + * @see Throwable#fillInStackTrace() + * @since 2.4.3 + */ +public class ThrowableCallStack implements CallStack { + + private final String messageFormat; + //@GuardedBy("dateFormat") + private final DateFormat dateFormat; + + private volatile Snapshot snapshot; + + public ThrowableCallStack(final String messageFormat, final boolean useTimestamp) { + this.messageFormat = messageFormat; + this.dateFormat = useTimestamp ? new SimpleDateFormat(messageFormat) : null; + } + + @Override + public synchronized boolean printStackTrace(PrintWriter writer) { + Snapshot snapshot = this.snapshot; + if (snapshot == null) { + return false; + } + final String message; + if (dateFormat == null) { + message = messageFormat; + } else { + synchronized (dateFormat) { + message = dateFormat.format(snapshot.timestamp); + } + } + writer.println(message); + snapshot.printStackTrace(writer); + return true; + } + + @Override + public void fillInStackTrace() { + snapshot = new Snapshot(); + } + + @Override + public void clear() { + snapshot = null; + } + + private static class Snapshot extends Throwable { + private final long timestamp = System.currentTimeMillis(); + } +} Added: commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/CallStackTest.java URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/CallStackTest.java?rev=1785520&view=auto ============================================================================== --- commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/CallStackTest.java (added) +++ commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/CallStackTest.java Sat Mar 4 20:18:19 2017 @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.commons.pool2.impl; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(Parameterized.class) +public class CallStackTest { + + private final CallStack stack; + private final StringWriter writer = new StringWriter(); + + public CallStackTest(CallStack stack) { + this.stack = stack; + } + + @Parameterized.Parameters + public static Object[] data() { + return new Object[]{ + new ThrowableCallStack("Test", false), + new SecurityManagerCallStack("Test", false) + }; + } + + @Test + public void testPrintClearedStackTraceIsNoOp() throws Exception { + stack.fillInStackTrace(); + stack.clear(); + stack.printStackTrace(new PrintWriter(writer)); + String stackTrace = writer.toString(); + assertEquals("", stackTrace); + } + + @Test + public void testPrintFilledStackTrace() throws Exception { + stack.fillInStackTrace(); + stack.printStackTrace(new PrintWriter(writer)); + String stackTrace = writer.toString(); + assertTrue(stackTrace.contains(getClass().getName())); + } +} \ No newline at end of file Modified: commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObjectInfo.java URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObjectInfo.java?rev=1785520&r1=1785519&r2=1785520&view=diff ============================================================================== --- commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObjectInfo.java (original) +++ commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObjectInfo.java Sat Mar 4 20:18:19 2017 @@ -16,14 +16,13 @@ */ package org.apache.commons.pool2.impl; -import java.text.SimpleDateFormat; -import java.util.Set; - -import org.apache.commons.pool2.impl.DefaultPooledObject.AbandonedObjectCreatedException; import org.apache.commons.pool2.impl.TestGenericObjectPool.SimpleFactory; import org.junit.Assert; import org.junit.Test; +import java.text.SimpleDateFormat; +import java.util.Set; + public class TestDefaultPooledObjectInfo { @Test @@ -120,17 +119,13 @@ public class TestDefaultPooledObjectInfo new GenericObjectPoolConfig(), abandonedConfig); - try { - pool.borrowObject(); - //pool.returnObject(s1); // Object not returned, causes abandoned object created exception - } catch (final AbandonedObjectCreatedException e) { - // do nothing. We will print the stack trace later - } + pool.borrowObject(); + //pool.returnObject(s1); // Object not returned, causes abandoned object created exception final Set strings = pool.listAllObjects(); final DefaultPooledObjectInfo s1Info = strings.iterator().next(); final String lastBorrowTrace = s1Info.getLastBorrowTrace(); - Assert.assertTrue(lastBorrowTrace.startsWith(AbandonedObjectCreatedException.class.getName())); + Assert.assertTrue(lastBorrowTrace.startsWith("Pooled object created")); } }