1
18 package net.bull.javamelody;
19
20 import java.lang.reflect.Constructor;
21 import java.lang.reflect.Field;
22 import java.lang.reflect.InvocationHandler;
23 import java.lang.reflect.Proxy;
24 import java.security.AccessController;
25 import java.security.PrivilegedAction;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collections;
29 import java.util.Hashtable;
30 import java.util.LinkedHashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.WeakHashMap;
34
35 import javax.naming.Context;
36 import javax.naming.InitialContext;
37 import javax.naming.NameClassPair;
38 import javax.naming.NamingException;
39 import javax.naming.NoInitialContextException;
40 import javax.naming.Referenceable;
41 import javax.servlet.ServletContext;
42 import javax.sql.DataSource;
43
44 import org.apache.tomcat.dbcp.dbcp.BasicDataSource;
45
46
50 final class JdbcWrapperHelper {
51 private static final String MAX_ACTIVE_PROPERTY_NAME = "maxActive";
52 private static final Map<String, DataSource> SPRING_DATASOURCES = new LinkedHashMap<>();
53 private static final Map<String, DataSource> JNDI_DATASOURCES_BACKUP = new LinkedHashMap<>();
54 private static final Map<String, DataSource> REWRAPPED_DATASOURCES_BACKUP = new LinkedHashMap<>();
55 private static final BasicDataSourcesProperties TOMCAT_BASIC_DATASOURCES_PROPERTIES = new BasicDataSourcesProperties();
56 private static final BasicDataSourcesProperties DBCP_BASIC_DATASOURCES_PROPERTIES = new BasicDataSourcesProperties();
57 private static final BasicDataSourcesProperties TOMCAT_JDBC_DATASOURCES_PROPERTIES = new BasicDataSourcesProperties();
58
59 private static final Map<Class<?>, Constructor<?>> PROXY_CACHE = Collections
60 .synchronizedMap(new WeakHashMap<Class<?>, Constructor<?>>());
61
62
66 private static class BasicDataSourcesProperties {
67 private final Map<String, Map<String, Object>> properties = new LinkedHashMap<>();
68
69 BasicDataSourcesProperties() {
70 super();
71 }
72
73 boolean isEmpty() {
74 return properties.isEmpty();
75 }
76
77 int getMaxActive() {
78 int result = 0;
79 for (final Map<String, Object> dataSourceProperties : properties.values()) {
80 final Integer maxActive = (Integer) dataSourceProperties
81 .get(MAX_ACTIVE_PROPERTY_NAME);
82 if (maxActive == null) {
83 return -1;
84 }
85 result += maxActive;
86 }
87 return result;
88 }
89
90 Map<String, Map<String, Object>> getDataSourcesProperties() {
91 final Map<String, Map<String, Object>> result = new LinkedHashMap<>();
92 for (final Map.Entry<String, Map<String, Object>> entry : properties.entrySet()) {
93 result.put(entry.getKey(), Collections.unmodifiableMap(entry.getValue()));
94 }
95 return Collections.unmodifiableMap(result);
96 }
97
98 void put(String dataSourceName, String key, Object value) {
99 Map<String, Object> dataSourceProperties = properties.get(dataSourceName);
100 if (dataSourceProperties == null) {
101 dataSourceProperties = new LinkedHashMap<>();
102 properties.put(dataSourceName, dataSourceProperties);
103 }
104 dataSourceProperties.put(key, value);
105 }
106 }
107
108 private JdbcWrapperHelper() {
109 super();
110 }
111
112 static void registerSpringDataSource(String name, DataSource dataSource) {
113 SPRING_DATASOURCES.put(name, dataSource);
114 }
115
116 static void registerRewrappedDataSource(String name, DataSource dataSource) {
117 REWRAPPED_DATASOURCES_BACKUP.put(name, dataSource);
118 }
119
120 static Map<String, DataSource> getRewrappedDataSources() {
121 return REWRAPPED_DATASOURCES_BACKUP;
122 }
123
124 static void rebindDataSource(ServletContext servletContext, String jndiName,
125 DataSource dataSource, DataSource dataSourceProxy) throws Throwable {
126 final Object lock = changeContextWritable(servletContext, null);
127 final InitialContext initialContext = new InitialContext();
128 initialContext.rebind(jndiName, dataSourceProxy);
129 JNDI_DATASOURCES_BACKUP.put(jndiName, dataSource);
130 changeContextWritable(servletContext, lock);
131 initialContext.close();
132 }
133
134 static void rebindInitialDataSources(ServletContext servletContext) throws Throwable {
135 try {
136 final InitialContext initialContext = new InitialContext();
137 for (final Map.Entry<String, DataSource> entry : JNDI_DATASOURCES_BACKUP.entrySet()) {
138 final String jndiName = entry.getKey();
139 final DataSource dataSource = entry.getValue();
140 final Object lock = changeContextWritable(servletContext, null);
141 initialContext.rebind(jndiName, dataSource);
142 changeContextWritable(servletContext, lock);
143 }
144 initialContext.close();
145 } finally {
146 JNDI_DATASOURCES_BACKUP.clear();
147 }
148 }
149
150 static Map<String, DataSource> getJndiAndSpringDataSources() throws NamingException {
151 Map<String, DataSource> dataSources;
152 try {
153 dataSources = new LinkedHashMap<>(getJndiDataSources());
154 } catch (final NoInitialContextException e) {
155 dataSources = new LinkedHashMap<>();
156 }
157 dataSources.putAll(SPRING_DATASOURCES);
158 return dataSources;
159 }
160
161 static Map<String, DataSource> getJndiDataSources() throws NamingException {
162 final Map<String, DataSource> dataSources = new LinkedHashMap<>(2);
163 final String datasourcesParameter = Parameter.DATASOURCES.getValue();
164 if (datasourcesParameter == null) {
165 dataSources.putAll(getJndiDataSourcesAt("java:comp/env/jdbc"));
166
167 dataSources.putAll(getJndiDataSourcesAt("java:/jdbc"));
168
169
170 dataSources.putAll(getJndiDataSourcesAt("java:global/jdbc"));
171
172 dataSources.putAll(getJndiDataSourcesAt("jdbc"));
173 } else if (!datasourcesParameter.trim().isEmpty()) {
174 final InitialContext initialContext = new InitialContext();
175 for (final String datasource : datasourcesParameter.split(",")) {
176 final String jndiName = datasource.trim();
177
178
179 final DataSource dataSource = (DataSource) initialContext.lookup(jndiName);
180 dataSources.put(jndiName, dataSource);
181 }
182 initialContext.close();
183 }
184 return Collections.unmodifiableMap(dataSources);
185 }
186
187 private static Map<String, DataSource> getJndiDataSourcesAt(String jndiPrefix)
188 throws NamingException {
189 final InitialContext initialContext = new InitialContext();
190 final Map<String, DataSource> dataSources = new LinkedHashMap<>(2);
191 try {
192 for (final NameClassPair nameClassPair : Collections
193 .list(initialContext.list(jndiPrefix))) {
194
195
196
197
198 final String jndiName;
199 if (nameClassPair.getName().startsWith("java:")) {
200
201 jndiName = nameClassPair.getName();
202 } else {
203 jndiName = jndiPrefix + '/' + nameClassPair.getName();
204 }
205 final Object value = initialContext.lookup(jndiName);
206 if (value instanceof DataSource) {
207 dataSources.put(jndiName, (DataSource) value);
208 }
209 }
210 } catch (final NamingException e) {
211
212
213 return dataSources;
214 }
215 initialContext.close();
216 return dataSources;
217 }
218
219 static int getMaxConnectionCount() {
220 if (!TOMCAT_BASIC_DATASOURCES_PROPERTIES.isEmpty()) {
221 return TOMCAT_BASIC_DATASOURCES_PROPERTIES.getMaxActive();
222 } else if (!DBCP_BASIC_DATASOURCES_PROPERTIES.isEmpty()) {
223 return DBCP_BASIC_DATASOURCES_PROPERTIES.getMaxActive();
224 } else if (!TOMCAT_JDBC_DATASOURCES_PROPERTIES.isEmpty()) {
225 return TOMCAT_JDBC_DATASOURCES_PROPERTIES.getMaxActive();
226 }
227 return -1;
228 }
229
230 static Map<String, Map<String, Object>> getBasicDataSourceProperties() {
231 if (!TOMCAT_BASIC_DATASOURCES_PROPERTIES.isEmpty()) {
232 return TOMCAT_BASIC_DATASOURCES_PROPERTIES.getDataSourcesProperties();
233 } else if (!DBCP_BASIC_DATASOURCES_PROPERTIES.isEmpty()) {
234 return DBCP_BASIC_DATASOURCES_PROPERTIES.getDataSourcesProperties();
235 } else if (!TOMCAT_JDBC_DATASOURCES_PROPERTIES.isEmpty()) {
236 return TOMCAT_JDBC_DATASOURCES_PROPERTIES.getDataSourcesProperties();
237 }
238 return Collections.emptyMap();
239 }
240
241
242 static void pullDataSourceProperties(String name, DataSource dataSource) {
243
244 final String dataSourceClassName = dataSource.getClass().getName();
245 if ("org.apache.tomcat.dbcp.dbcp.BasicDataSource".equals(dataSourceClassName)
246 && dataSource instanceof BasicDataSource) {
247 pullTomcatDbcpDataSourceProperties(name, dataSource);
248 } else if ("org.apache.tomcat.dbcp.dbcp2.BasicDataSource".equals(dataSourceClassName)
249 && dataSource instanceof org.apache.tomcat.dbcp.dbcp2.BasicDataSource) {
250 pullTomcatDbcp2DataSourceProperties(name, dataSource);
251 } else if ("org.apache.commons.dbcp.BasicDataSource".equals(dataSourceClassName)
252 && dataSource instanceof org.apache.commons.dbcp.BasicDataSource) {
253 pullCommonsDbcpDataSourceProperties(name, dataSource);
254 } else if ("org.apache.commons.dbcp2.BasicDataSource".equals(dataSourceClassName)
255 && dataSource instanceof org.apache.commons.dbcp2.BasicDataSource) {
256 pullCommonsDbcp2DataSourceProperties(name, dataSource);
257 } else if ("org.apache.tomcat.jdbc.pool.DataSource".equals(dataSourceClassName)
258 && dataSource instanceof org.apache.tomcat.jdbc.pool.DataSource) {
259 pullTomcatJdbcDataSourceProperties(name, dataSource);
260 }
261 }
262
263 private static void pullTomcatDbcpDataSourceProperties(String name, DataSource dataSource) {
264
265 final BasicDataSource tomcatDbcpDataSource = (BasicDataSource) dataSource;
266 final BasicDataSourcesProperties properties = TOMCAT_BASIC_DATASOURCES_PROPERTIES;
267
268
269
270
271 properties.put(name, MAX_ACTIVE_PROPERTY_NAME, tomcatDbcpDataSource.getMaxActive());
272 properties.put(name, "poolPreparedStatements",
273 tomcatDbcpDataSource.isPoolPreparedStatements());
274
275 properties.put(name, "defaultCatalog", tomcatDbcpDataSource.getDefaultCatalog());
276 properties.put(name, "defaultAutoCommit", tomcatDbcpDataSource.getDefaultAutoCommit());
277 properties.put(name, "defaultReadOnly", tomcatDbcpDataSource.getDefaultReadOnly());
278 properties.put(name, "defaultTransactionIsolation",
279 tomcatDbcpDataSource.getDefaultTransactionIsolation());
280 properties.put(name, "driverClassName", tomcatDbcpDataSource.getDriverClassName());
281 properties.put(name, "initialSize", tomcatDbcpDataSource.getInitialSize());
282 properties.put(name, "maxIdle", tomcatDbcpDataSource.getMaxIdle());
283 properties.put(name, "maxOpenPreparedStatements",
284 tomcatDbcpDataSource.getMaxOpenPreparedStatements());
285 properties.put(name, "maxWait", tomcatDbcpDataSource.getMaxWait());
286 properties.put(name, "minEvictableIdleTimeMillis",
287 tomcatDbcpDataSource.getMinEvictableIdleTimeMillis());
288 properties.put(name, "minIdle", tomcatDbcpDataSource.getMinIdle());
289 properties.put(name, "numTestsPerEvictionRun",
290 tomcatDbcpDataSource.getNumTestsPerEvictionRun());
291 properties.put(name, "testOnBorrow", tomcatDbcpDataSource.getTestOnBorrow());
292 properties.put(name, "testOnReturn", tomcatDbcpDataSource.getTestOnReturn());
293 properties.put(name, "testWhileIdle", tomcatDbcpDataSource.getTestWhileIdle());
294 properties.put(name, "timeBetweenEvictionRunsMillis",
295 tomcatDbcpDataSource.getTimeBetweenEvictionRunsMillis());
296 properties.put(name, "validationQuery", tomcatDbcpDataSource.getValidationQuery());
297 }
298
299 private static void pullCommonsDbcpDataSourceProperties(String name, DataSource dataSource) {
300
301 final org.apache.commons.dbcp.BasicDataSource dbcpDataSource = (org.apache.commons.dbcp.BasicDataSource) dataSource;
302 final BasicDataSourcesProperties properties = DBCP_BASIC_DATASOURCES_PROPERTIES;
303
304
305
306
307 properties.put(name, MAX_ACTIVE_PROPERTY_NAME, dbcpDataSource.getMaxActive());
308 properties.put(name, "poolPreparedStatements", dbcpDataSource.isPoolPreparedStatements());
309
310 properties.put(name, "defaultCatalog", dbcpDataSource.getDefaultCatalog());
311 properties.put(name, "defaultAutoCommit", dbcpDataSource.getDefaultAutoCommit());
312 properties.put(name, "defaultReadOnly", dbcpDataSource.getDefaultReadOnly());
313 properties.put(name, "defaultTransactionIsolation",
314 dbcpDataSource.getDefaultTransactionIsolation());
315 properties.put(name, "driverClassName", dbcpDataSource.getDriverClassName());
316 properties.put(name, "initialSize", dbcpDataSource.getInitialSize());
317 properties.put(name, "maxIdle", dbcpDataSource.getMaxIdle());
318 properties.put(name, "maxOpenPreparedStatements",
319 dbcpDataSource.getMaxOpenPreparedStatements());
320 properties.put(name, "maxWait", dbcpDataSource.getMaxWait());
321 properties.put(name, "minEvictableIdleTimeMillis",
322 dbcpDataSource.getMinEvictableIdleTimeMillis());
323 properties.put(name, "minIdle", dbcpDataSource.getMinIdle());
324 properties.put(name, "numTestsPerEvictionRun", dbcpDataSource.getNumTestsPerEvictionRun());
325 properties.put(name, "testOnBorrow", dbcpDataSource.getTestOnBorrow());
326 properties.put(name, "testOnReturn", dbcpDataSource.getTestOnReturn());
327 properties.put(name, "testWhileIdle", dbcpDataSource.getTestWhileIdle());
328 properties.put(name, "timeBetweenEvictionRunsMillis",
329 dbcpDataSource.getTimeBetweenEvictionRunsMillis());
330 properties.put(name, "validationQuery", dbcpDataSource.getValidationQuery());
331 }
332
333 private static void pullTomcatDbcp2DataSourceProperties(String name, DataSource dataSource) {
334
335 final org.apache.tomcat.dbcp.dbcp2.BasicDataSource tomcatDbcp2DataSource = (org.apache.tomcat.dbcp.dbcp2.BasicDataSource) dataSource;
336 final BasicDataSourcesProperties properties = TOMCAT_BASIC_DATASOURCES_PROPERTIES;
337
338
339
340
341 properties.put(name, MAX_ACTIVE_PROPERTY_NAME, tomcatDbcp2DataSource.getMaxTotal());
342 properties.put(name, "poolPreparedStatements",
343 tomcatDbcp2DataSource.isPoolPreparedStatements());
344
345 properties.put(name, "defaultCatalog", tomcatDbcp2DataSource.getDefaultCatalog());
346 properties.put(name, "defaultAutoCommit", tomcatDbcp2DataSource.getDefaultAutoCommit());
347 properties.put(name, "defaultReadOnly", tomcatDbcp2DataSource.getDefaultReadOnly());
348 properties.put(name, "defaultTransactionIsolation",
349 tomcatDbcp2DataSource.getDefaultTransactionIsolation());
350 properties.put(name, "driverClassName", tomcatDbcp2DataSource.getDriverClassName());
351 properties.put(name, "initialSize", tomcatDbcp2DataSource.getInitialSize());
352 properties.put(name, "maxIdle", tomcatDbcp2DataSource.getMaxIdle());
353 properties.put(name, "maxOpenPreparedStatements",
354 tomcatDbcp2DataSource.getMaxOpenPreparedStatements());
355 properties.put(name, "maxWait", tomcatDbcp2DataSource.getMaxWaitMillis());
356 properties.put(name, "minEvictableIdleTimeMillis",
357 tomcatDbcp2DataSource.getMinEvictableIdleTimeMillis());
358 properties.put(name, "minIdle", tomcatDbcp2DataSource.getMinIdle());
359 properties.put(name, "numTestsPerEvictionRun",
360 tomcatDbcp2DataSource.getNumTestsPerEvictionRun());
361 properties.put(name, "testOnBorrow", tomcatDbcp2DataSource.getTestOnBorrow());
362 properties.put(name, "testOnReturn", tomcatDbcp2DataSource.getTestOnReturn());
363 properties.put(name, "testWhileIdle", tomcatDbcp2DataSource.getTestWhileIdle());
364 properties.put(name, "timeBetweenEvictionRunsMillis",
365 tomcatDbcp2DataSource.getTimeBetweenEvictionRunsMillis());
366 properties.put(name, "validationQuery", tomcatDbcp2DataSource.getValidationQuery());
367 }
368
369 private static void pullCommonsDbcp2DataSourceProperties(String name, DataSource dataSource) {
370
371 final org.apache.commons.dbcp2.BasicDataSource dbcp2DataSource = (org.apache.commons.dbcp2.BasicDataSource) dataSource;
372 final BasicDataSourcesProperties properties = DBCP_BASIC_DATASOURCES_PROPERTIES;
373
374
375
376
377 properties.put(name, MAX_ACTIVE_PROPERTY_NAME, dbcp2DataSource.getMaxTotal());
378 properties.put(name, "poolPreparedStatements", dbcp2DataSource.isPoolPreparedStatements());
379
380 properties.put(name, "defaultCatalog", dbcp2DataSource.getDefaultCatalog());
381 properties.put(name, "defaultAutoCommit", dbcp2DataSource.getDefaultAutoCommit());
382 properties.put(name, "defaultReadOnly", dbcp2DataSource.getDefaultReadOnly());
383 properties.put(name, "defaultTransactionIsolation",
384 dbcp2DataSource.getDefaultTransactionIsolation());
385 properties.put(name, "driverClassName", dbcp2DataSource.getDriverClassName());
386 properties.put(name, "initialSize", dbcp2DataSource.getInitialSize());
387 properties.put(name, "maxIdle", dbcp2DataSource.getMaxIdle());
388 properties.put(name, "maxOpenPreparedStatements",
389 dbcp2DataSource.getMaxOpenPreparedStatements());
390 properties.put(name, "maxWait", dbcp2DataSource.getMaxWaitMillis());
391 properties.put(name, "minEvictableIdleTimeMillis",
392 dbcp2DataSource.getMinEvictableIdleTimeMillis());
393 properties.put(name, "minIdle", dbcp2DataSource.getMinIdle());
394 properties.put(name, "numTestsPerEvictionRun", dbcp2DataSource.getNumTestsPerEvictionRun());
395 properties.put(name, "testOnBorrow", dbcp2DataSource.getTestOnBorrow());
396 properties.put(name, "testOnReturn", dbcp2DataSource.getTestOnReturn());
397 properties.put(name, "testWhileIdle", dbcp2DataSource.getTestWhileIdle());
398 properties.put(name, "timeBetweenEvictionRunsMillis",
399 dbcp2DataSource.getTimeBetweenEvictionRunsMillis());
400 properties.put(name, "validationQuery", dbcp2DataSource.getValidationQuery());
401 }
402
403 private static void pullTomcatJdbcDataSourceProperties(String name, DataSource dataSource) {
404
405 final org.apache.tomcat.jdbc.pool.DataSource jdbcDataSource = (org.apache.tomcat.jdbc.pool.DataSource) dataSource;
406 final BasicDataSourcesProperties properties = TOMCAT_JDBC_DATASOURCES_PROPERTIES;
407
408
409
410
411 properties.put(name, MAX_ACTIVE_PROPERTY_NAME, jdbcDataSource.getMaxActive());
412
413 properties.put(name, "defaultCatalog", jdbcDataSource.getDefaultCatalog());
414 properties.put(name, "defaultAutoCommit", jdbcDataSource.getDefaultAutoCommit());
415 properties.put(name, "defaultReadOnly", jdbcDataSource.getDefaultReadOnly());
416 properties.put(name, "defaultTransactionIsolation",
417 jdbcDataSource.getDefaultTransactionIsolation());
418 properties.put(name, "driverClassName", jdbcDataSource.getDriverClassName());
419 properties.put(name, "connectionProperties", jdbcDataSource.getConnectionProperties());
420 properties.put(name, "initSQL", jdbcDataSource.getInitSQL());
421 properties.put(name, "initialSize", jdbcDataSource.getInitialSize());
422 properties.put(name, "maxIdle", jdbcDataSource.getMaxIdle());
423 properties.put(name, "maxWait", jdbcDataSource.getMaxWait());
424 properties.put(name, "maxAge", jdbcDataSource.getMaxAge());
425 properties.put(name, "faireQueue", jdbcDataSource.isFairQueue());
426 properties.put(name, "jmxEnabled", jdbcDataSource.isJmxEnabled());
427 properties.put(name, "minEvictableIdleTimeMillis",
428 jdbcDataSource.getMinEvictableIdleTimeMillis());
429 properties.put(name, "minIdle", jdbcDataSource.getMinIdle());
430 properties.put(name, "numTestsPerEvictionRun", jdbcDataSource.getNumTestsPerEvictionRun());
431 properties.put(name, "testOnBorrow", jdbcDataSource.isTestOnBorrow());
432 properties.put(name, "testOnConnect", jdbcDataSource.isTestOnConnect());
433 properties.put(name, "testOnReturn", jdbcDataSource.isTestOnReturn());
434 properties.put(name, "testWhileIdle", jdbcDataSource.isTestWhileIdle());
435 properties.put(name, "timeBetweenEvictionRunsMillis",
436 jdbcDataSource.getTimeBetweenEvictionRunsMillis());
437 properties.put(name, "validationInterval", jdbcDataSource.getValidationInterval());
438 properties.put(name, "validationQuery", jdbcDataSource.getValidationQuery());
439 properties.put(name, "validatorClassName", jdbcDataSource.getValidatorClassName());
440 }
441
442 @SuppressWarnings("all")
443 private static Object changeContextWritable(ServletContext servletContext, Object lock)
444 throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException,
445 NamingException {
446
447 assert servletContext != null;
448 final String serverInfo = servletContext.getServerInfo();
449 if (serverInfo.contains("jetty")) {
450
451 final Context jdbcContext = (Context) new InitialContext().lookup("java:comp");
452 final Field field = getAccessibleField(jdbcContext, "_env");
453 @SuppressWarnings("unchecked")
454 final Hashtable<Object, Object> env = (Hashtable<Object, Object>) field
455 .get(jdbcContext);
456 if (lock == null) {
457
458 Object result = env.remove("org.mortbay.jndi.lock");
459 if (result == null) {
460 result = env.remove("org.eclipse.jndi.lock");
461 }
462 return result;
463 }
464
465 env.put("org.mortbay.jndi.lock", lock);
466 env.put("org.eclipse.jndi.lock", lock);
467
468 return null;
469 }
470
471
472
473
474 try {
475 final Field field = Class.forName("org.apache.naming.ContextAccessController")
476 .getDeclaredField("readOnlyContexts");
477 setFieldAccessible(field);
478 @SuppressWarnings("unchecked")
479 final Map<String, Object> readOnlyContexts = (Map<String, Object>) field.get(null);
480
481
482
483
484
485 if (lock == null) {
486
487
488 final Hashtable<String, Object> clone = new Hashtable<>(readOnlyContexts);
489 readOnlyContexts.clear();
490 return clone;
491 }
492
493 @SuppressWarnings("unchecked")
494 final Hashtable<String, Object> myLock = (Hashtable<String, Object>) lock;
495 readOnlyContexts.putAll(myLock);
496
497 return null;
498 } catch (final Exception e) {
499
500 return null;
501 }
502 }
503
504 static Object getFieldValue(Object object, String fieldName) throws IllegalAccessException {
505 return getAccessibleField(object, fieldName).get(object);
506 }
507
508 static void setFieldValue(Object object, String fieldName, Object value)
509 throws IllegalAccessException {
510 getAccessibleField(object, fieldName).set(object, value);
511 }
512
513 static boolean hasField(Object object, String fieldName) {
514 return getField(object, fieldName) != null;
515 }
516
517 private static Field getAccessibleField(Object object, String fieldName) {
518 final Field result = getField(object, fieldName);
519 assert result != null;
520 setFieldAccessible(result);
521 return result;
522 }
523
524 private static Field getField(Object object, String fieldName) {
525 assert fieldName != null;
526 Class<?> classe = object.getClass();
527 Field result = null;
528 do {
529 for (final Field field : classe.getDeclaredFields()) {
530 if (fieldName.equals(field.getName())) {
531 result = field;
532 break;
533 }
534 }
535 classe = classe.getSuperclass();
536 } while (result == null && classe != null);
537 return result;
538 }
539
540 private static void setFieldAccessible(final Field field) {
541 AccessController.doPrivileged(new PrivilegedAction<Object>() {
542
543 @Override
544 public Object run() {
545 field.setAccessible(true);
546 return null;
547 }
548 });
549 }
550
551 static void clearProxyCache() {
552 PROXY_CACHE.clear();
553 }
554
555 @SuppressWarnings("unchecked")
556 static <T> T createProxy(T object, InvocationHandler invocationHandler,
557 List<Class<?>> interfaces) {
558 final Class<?> objectClass = object.getClass();
559
560
561
562 Constructor<?> constructor = PROXY_CACHE.get(objectClass);
563 if (constructor == null) {
564 final Class<?>[] interfacesArray = getObjectInterfaces(objectClass, interfaces);
565 constructor = getProxyConstructor(objectClass, interfacesArray);
566
567
568 if (interfaces == null) {
569 PROXY_CACHE.put(objectClass, constructor);
570 }
571 }
572 try {
573 return (T) constructor.newInstance(new Object[] { invocationHandler });
574 } catch (final Exception e) {
575 throw new IllegalStateException(e);
576 }
577 }
578
579 private static Constructor<?> getProxyConstructor(Class<?> objectClass,
580 Class<?>[] interfacesArray) {
581 final ClassLoader classLoader = objectClass.getClassLoader();
582 try {
583 final Constructor<?> constructor = Proxy.getProxyClass(classLoader, interfacesArray)
584 .getConstructor(InvocationHandler.class);
585
586
587
588 constructor.setAccessible(true);
589 return constructor;
590 } catch (final NoSuchMethodException e) {
591 throw new IllegalStateException(e);
592 }
593 }
594
595 private static Class<?>[] getObjectInterfaces(Class<?> objectClass, List<Class<?>> interfaces) {
596
597
598
599 final List<Class<?>> myInterfaces;
600 if (interfaces == null) {
601 myInterfaces = new ArrayList<>(Arrays.asList(objectClass.getInterfaces()));
602 Class<?> classe = objectClass.getSuperclass();
603 while (classe != null) {
604 final Class<?>[] classInterfaces = classe.getInterfaces();
605 if (classInterfaces.length > 0) {
606 final List<Class<?>> superInterfaces = Arrays.asList(classInterfaces);
607
608 myInterfaces.removeAll(superInterfaces);
609 myInterfaces.addAll(superInterfaces);
610 }
611 classe = classe.getSuperclass();
612 }
613
614
615 myInterfaces.remove(Referenceable.class);
616 } else {
617 myInterfaces = interfaces;
618 }
619 return myInterfaces.toArray(new Class<?>[0]);
620 }
621 }
622