Skip to content

Commit 5a2f5cb

Browse files
committed
Add an option to JdkProxySource allowing to unwrap UndeclaredThrowableException
1 parent 50ad5b5 commit 5a2f5cb

8 files changed

+153
-38
lines changed

src/main/java/org/apache/commons/pool3/proxy/BaseProxyHandler.java

+17-6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
package org.apache.commons.pool3.proxy;
1818

19+
import java.lang.reflect.InvocationTargetException;
1920
import java.lang.reflect.Method;
2021

2122
import org.apache.commons.pool3.UsageTracking;
@@ -32,18 +33,21 @@ class BaseProxyHandler<T> {
3233

3334
private volatile T pooledObject;
3435
private final UsageTracking<T> usageTracking;
36+
private final boolean unwrapInvocationTargetException;
3537

3638
/**
3739
* Constructs a new wrapper for the given pooled object.
3840
*
39-
* @param pooledObject The object to wrap
40-
* @param usageTracking The instance, if any (usually the object pool) to
41-
* be provided with usage tracking information for this
42-
* wrapped object
41+
* @param pooledObject The object to wrap
42+
* @param usageTracking The instance, if any (usually the object pool) to
43+
* be provided with usage tracking information for this
44+
* wrapped object
45+
* @param unwrapInvocationTargetException True to make the proxy throw {@link InvocationTargetException#getTargetException()} instead of {@link InvocationTargetException}
4346
*/
44-
BaseProxyHandler(final T pooledObject, final UsageTracking<T> usageTracking) {
47+
BaseProxyHandler(final T pooledObject, final UsageTracking<T> usageTracking, boolean unwrapInvocationTargetException) {
4548
this.pooledObject = pooledObject;
4649
this.usageTracking = usageTracking;
50+
this.unwrapInvocationTargetException = unwrapInvocationTargetException;
4751
}
4852

4953
/**
@@ -73,7 +77,14 @@ Object doInvoke(final Method method, final Object[] args) throws Throwable {
7377
if (usageTracking != null) {
7478
usageTracking.use(object);
7579
}
76-
return method.invoke(object, args);
80+
try {
81+
return method.invoke(object, args);
82+
} catch (InvocationTargetException e) {
83+
if (unwrapInvocationTargetException) {
84+
throw e.getTargetException();
85+
}
86+
throw e;
87+
}
7788
}
7889

7990
/**

src/main/java/org/apache/commons/pool3/proxy/CglibProxyHandler.java

+8-6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
package org.apache.commons.pool3.proxy;
1818

19+
import java.lang.reflect.InvocationTargetException;
1920
import java.lang.reflect.Method;
2021

2122
import org.apache.commons.pool3.UsageTracking;
@@ -36,13 +37,14 @@ final class CglibProxyHandler<T> extends BaseProxyHandler<T>
3637
/**
3738
* Constructs a CGLib proxy instance.
3839
*
39-
* @param pooledObject The object to wrap
40-
* @param usageTracking The instance, if any (usually the object pool) to
41-
* be provided with usage tracking information for this
42-
* wrapped object
40+
* @param pooledObject The object to wrap
41+
* @param usageTracking The instance, if any (usually the object pool) to
42+
* be provided with usage tracking information for this
43+
* wrapped object
44+
* @param unwrapInvocationTargetException True to make the proxy throw {@link InvocationTargetException#getTargetException()} instead of {@link InvocationTargetException}
4345
*/
44-
CglibProxyHandler(final T pooledObject, final UsageTracking<T> usageTracking) {
45-
super(pooledObject, usageTracking);
46+
CglibProxyHandler(final T pooledObject, final UsageTracking<T> usageTracking, boolean unwrapInvocationTargetException) {
47+
super(pooledObject, usageTracking, unwrapInvocationTargetException);
4648
}
4749

4850
@Override

src/main/java/org/apache/commons/pool3/proxy/CglibProxySource.java

+15-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
package org.apache.commons.pool3.proxy;
1818

19+
import java.lang.reflect.InvocationTargetException;
1920
import org.apache.commons.pool3.UsageTracking;
2021

2122
import net.sf.cglib.proxy.Enhancer;
@@ -31,14 +32,26 @@
3132
public class CglibProxySource<T> implements ProxySource<T> {
3233

3334
private final Class<? extends T> superclass;
35+
private final boolean unwrapInvocationTargetException;
36+
37+
/**
38+
* Constructs a new proxy source for the given class.
39+
*
40+
* @param superclass The class to proxy
41+
* @param unwrapInvocationTargetException True to make the proxy throw {@link InvocationTargetException#getTargetException()} instead of {@link InvocationTargetException}
42+
*/
43+
public CglibProxySource(final Class<? extends T> superclass, boolean unwrapInvocationTargetException) {
44+
this.superclass = superclass;
45+
this.unwrapInvocationTargetException = unwrapInvocationTargetException;
46+
}
3447

3548
/**
3649
* Constructs a new proxy source for the given class.
3750
*
3851
* @param superclass The class to proxy
3952
*/
4053
public CglibProxySource(final Class<? extends T> superclass) {
41-
this.superclass = superclass;
54+
this(superclass, false);
4255
}
4356

4457
@SuppressWarnings("unchecked") // Case to T on return
@@ -48,7 +61,7 @@ public T createProxy(final T pooledObject, final UsageTracking<T> usageTracking)
4861
enhancer.setSuperclass(superclass);
4962

5063
final CglibProxyHandler<T> proxyInterceptor =
51-
new CglibProxyHandler<>(pooledObject, usageTracking);
64+
new CglibProxyHandler<>(pooledObject, usageTracking, unwrapInvocationTargetException);
5265
enhancer.setCallback(proxyInterceptor);
5366

5467
return (T) enhancer.create();

src/main/java/org/apache/commons/pool3/proxy/JdkProxyHandler.java

+8-6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.apache.commons.pool3.proxy;
1818

1919
import java.lang.reflect.InvocationHandler;
20+
import java.lang.reflect.InvocationTargetException;
2021
import java.lang.reflect.Method;
2122

2223
import org.apache.commons.pool3.UsageTracking;
@@ -34,13 +35,14 @@ final class JdkProxyHandler<T> extends BaseProxyHandler<T>
3435
/**
3536
* Constructs a Java reflection proxy instance.
3637
*
37-
* @param pooledObject The object to wrap
38-
* @param usageTracking The instance, if any (usually the object pool) to
39-
* be provided with usage tracking information for this
40-
* wrapped object
38+
* @param pooledObject The object to wrap
39+
* @param usageTracking The instance, if any (usually the object pool) to
40+
* be provided with usage tracking information for this
41+
* wrapped object
42+
* @param unwrapInvocationTargetException True to make the proxy throw {@link InvocationTargetException#getTargetException()} instead of {@link InvocationTargetException}
4143
*/
42-
JdkProxyHandler(final T pooledObject, final UsageTracking<T> usageTracking) {
43-
super(pooledObject, usageTracking);
44+
JdkProxyHandler(final T pooledObject, final UsageTracking<T> usageTracking, boolean unwrapInvocationTargetException) {
45+
super(pooledObject, usageTracking, unwrapInvocationTargetException);
4446
}
4547

4648
@Override

src/main/java/org/apache/commons/pool3/proxy/JdkProxySource.java

+16-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
package org.apache.commons.pool3.proxy;
1818

19+
import java.lang.reflect.InvocationTargetException;
1920
import java.lang.reflect.Proxy;
2021
import java.util.Arrays;
2122

@@ -32,24 +33,37 @@ public class JdkProxySource<T> implements ProxySource<T> {
3233

3334
private final ClassLoader classLoader;
3435
private final Class<?>[] interfaces;
36+
private final boolean unwrapInvocationTargetException;
3537

3638
/**
3739
* Constructs a new proxy source for the given interfaces.
3840
*
3941
* @param classLoader The class loader with which to create the proxy
4042
* @param interfaces The interfaces to proxy
43+
* @param unwrapInvocationTargetException True to make the proxy throw {@link InvocationTargetException#getTargetException()} instead of {@link InvocationTargetException}
4144
*/
42-
public JdkProxySource(final ClassLoader classLoader, final Class<?>[] interfaces) {
45+
public JdkProxySource(final ClassLoader classLoader, final Class<?>[] interfaces, boolean unwrapInvocationTargetException) {
4346
this.classLoader = classLoader;
4447
// Defensive copy
4548
this.interfaces = Arrays.copyOf(interfaces, interfaces.length);
49+
this.unwrapInvocationTargetException = unwrapInvocationTargetException;
50+
}
51+
52+
/**
53+
* Constructs a new proxy source for the given interfaces.
54+
*
55+
* @param classLoader The class loader with which to create the proxy
56+
* @param interfaces The interfaces to proxy
57+
*/
58+
public JdkProxySource(final ClassLoader classLoader, final Class<?>[] interfaces) {
59+
this(classLoader, interfaces, false);
4660
}
4761

4862
@SuppressWarnings("unchecked") // Cast to T on return.
4963
@Override
5064
public T createProxy(final T pooledObject, final UsageTracking<T> usageTracking) {
5165
return (T) Proxy.newProxyInstance(classLoader, interfaces,
52-
new JdkProxyHandler<>(pooledObject, usageTracking));
66+
new JdkProxyHandler<>(pooledObject, usageTracking, unwrapInvocationTargetException));
5367
}
5468

5569
@SuppressWarnings("unchecked")

src/test/java/org/apache/commons/pool3/proxy/AbstractTestProxiedObjectPool.java

+85-12
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@
2020
import static org.junit.jupiter.api.Assertions.assertNotNull;
2121
import static org.junit.jupiter.api.Assertions.assertThrows;
2222
import static org.junit.jupiter.api.Assertions.assertTrue;
23+
import static org.junit.jupiter.api.Assertions.fail;
2324

2425
import java.io.PrintWriter;
2526
import java.io.StringWriter;
27+
import java.lang.reflect.InvocationTargetException;
28+
import java.lang.reflect.UndeclaredThrowableException;
2629
import java.time.Duration;
27-
2830
import org.apache.commons.pool3.BasePooledObjectFactory;
2931
import org.apache.commons.pool3.ObjectPool;
3032
import org.apache.commons.pool3.PooledObject;
@@ -45,9 +47,15 @@ protected interface TestObject {
4547
private static final class TestObjectFactory extends
4648
BasePooledObjectFactory<TestObject, RuntimeException> {
4749

50+
private final RuntimeException exceptionToThrow;
51+
52+
private TestObjectFactory(RuntimeException exceptionToThrow) {
53+
this.exceptionToThrow = exceptionToThrow;
54+
}
55+
4856
@Override
4957
public TestObject create() {
50-
return new TestObjectImpl();
58+
return new TestObjectImpl(exceptionToThrow);
5159
}
5260
@Override
5361
public PooledObject<TestObject> wrap(final TestObject value) {
@@ -57,32 +65,42 @@ public PooledObject<TestObject> wrap(final TestObject value) {
5765

5866
private static final class TestObjectImpl implements TestObject {
5967

68+
private final RuntimeException exceptionToThrow;
6069
private String data;
6170

71+
private TestObjectImpl(RuntimeException exceptionToThrow) {
72+
this.exceptionToThrow = exceptionToThrow;
73+
}
74+
6275
@Override
6376
public String getData() {
77+
if (exceptionToThrow != null) {
78+
throw exceptionToThrow;
79+
}
6480
return data;
6581
}
6682

6783
@Override
6884
public void setData(final String data) {
85+
if (exceptionToThrow != null) {
86+
throw exceptionToThrow;
87+
}
6988
this.data = data;
7089
}
7190
}
7291
private static final String DATA1 = "data1";
7392

7493
private static final Duration ABANDONED_TIMEOUT_SECS = Duration.ofSeconds(3);
7594

76-
private ObjectPool<TestObject, RuntimeException> pool;
77-
7895
private StringWriter log;
7996

80-
protected abstract ProxySource<TestObject> getproxySource();
97+
protected abstract ProxySource<TestObject> getproxySource(boolean unwrapInvocationTargetException);
8198

82-
@BeforeEach
83-
public void setUp() {
84-
log = new StringWriter();
99+
private ProxiedObjectPool<TestObject, RuntimeException> createProxiedObjectPool() {
100+
return createProxiedObjectPool(false, null);
101+
}
85102

103+
private ProxiedObjectPool<TestObject, RuntimeException> createProxiedObjectPool(boolean unwrapInvocationTargetException, RuntimeException exceptionToThrow) {
86104
final PrintWriter pw = new PrintWriter(log);
87105
final AbandonedConfig abandonedConfig = new AbandonedConfig();
88106
abandonedConfig.setLogAbandoned(true);
@@ -94,16 +112,23 @@ public void setUp() {
94112
final GenericObjectPoolConfig<TestObject> config = new GenericObjectPoolConfig<>();
95113
config.setMaxTotal(3);
96114

97-
final PooledObjectFactory<TestObject, RuntimeException> factory = new TestObjectFactory();
115+
final PooledObjectFactory<TestObject, RuntimeException> factory = new TestObjectFactory(exceptionToThrow);
98116

99-
@SuppressWarnings("resource")
100-
final ObjectPool<TestObject, RuntimeException> innerPool = new GenericObjectPool<>(factory, config, abandonedConfig);
117+
ObjectPool<TestObject, RuntimeException> innerPool = new GenericObjectPool<>(factory, config, abandonedConfig);
101118

102-
pool = new ProxiedObjectPool<>(innerPool, getproxySource());
119+
return new ProxiedObjectPool<>(innerPool, getproxySource(unwrapInvocationTargetException));
120+
}
121+
122+
@BeforeEach
123+
public void setUp() {
124+
log = new StringWriter();
103125
}
104126

105127
@Test
106128
public void testAccessAfterInvalidate() {
129+
@SuppressWarnings("resource")
130+
ObjectPool<TestObject, RuntimeException> pool = createProxiedObjectPool();
131+
107132
final TestObject obj = pool.borrowObject();
108133
assertNotNull(obj);
109134

@@ -122,6 +147,9 @@ public void testAccessAfterInvalidate() {
122147

123148
@Test
124149
public void testAccessAfterReturn() {
150+
@SuppressWarnings("resource")
151+
ObjectPool<TestObject, RuntimeException> pool = createProxiedObjectPool();
152+
125153
final TestObject obj = pool.borrowObject();
126154
assertNotNull(obj);
127155

@@ -139,6 +167,9 @@ public void testAccessAfterReturn() {
139167

140168
@Test
141169
public void testBorrowObject() {
170+
@SuppressWarnings("resource")
171+
ObjectPool<TestObject, RuntimeException> pool = createProxiedObjectPool();
172+
142173
final TestObject obj = pool.borrowObject();
143174
assertNotNull(obj);
144175

@@ -151,6 +182,9 @@ public void testBorrowObject() {
151182

152183
@Test
153184
public void testPassThroughMethods01() {
185+
@SuppressWarnings("resource")
186+
ObjectPool<TestObject, RuntimeException> pool = createProxiedObjectPool();
187+
154188
assertEquals(0, pool.getNumActive());
155189
assertEquals(0, pool.getNumIdle());
156190

@@ -167,6 +201,8 @@ public void testPassThroughMethods01() {
167201

168202
@Test
169203
public void testPassThroughMethods02() {
204+
ObjectPool<TestObject, RuntimeException> pool = createProxiedObjectPool();
205+
170206
pool.close();
171207

172208
assertThrows(IllegalStateException.class,
@@ -175,6 +211,9 @@ public void testPassThroughMethods02() {
175211

176212
@Test
177213
public void testUsageTracking() throws InterruptedException {
214+
@SuppressWarnings("resource")
215+
ObjectPool<TestObject, RuntimeException> pool = createProxiedObjectPool();
216+
178217
final TestObject obj = pool.borrowObject();
179218
assertNotNull(obj);
180219

@@ -193,4 +232,38 @@ public void testUsageTracking() throws InterruptedException {
193232
assertTrue(logOutput.contains("The last code to use this object was"));
194233
}
195234

235+
@Test
236+
public void testUnwrapInvocationTargetExceptionTrue() {
237+
@SuppressWarnings("resource")
238+
ObjectPool<TestObject, RuntimeException> pool = createProxiedObjectPool(true, new MyException());
239+
240+
TestObject object = pool.borrowObject();
241+
try {
242+
object.getData();
243+
fail("Expected to throw %s".formatted(MyException.class));
244+
} catch (MyException e) {
245+
// As expected
246+
}
247+
}
248+
249+
@Test
250+
public void testUnwrapInvocationTargetExceptionFalse() {
251+
@SuppressWarnings("resource")
252+
ObjectPool<TestObject, RuntimeException> pool = createProxiedObjectPool(false, new MyException());
253+
254+
TestObject object = pool.borrowObject();
255+
Exception caughtException = null;
256+
try {
257+
object.getData();
258+
} catch (Exception e) {
259+
caughtException = e;
260+
}
261+
262+
if (caughtException instanceof UndeclaredThrowableException || caughtException instanceof InvocationTargetException) {
263+
return;
264+
}
265+
fail("Expected to catch %s or %s but got %s instead".formatted(UndeclaredThrowableException.class, InvocationTargetException.class, caughtException));
266+
}
267+
268+
private static class MyException extends RuntimeException {}
196269
}

0 commit comments

Comments
 (0)