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 Hashtable<String, Object> readOnlyContexts = (Hashtable<String, Object>) field
480 .get(null);
481
482
483
484
485
486 if (lock == null) {
487
488
489 final Hashtable<String, Object> clone = new Hashtable<>(readOnlyContexts);
490 readOnlyContexts.clear();
491 return clone;
492 }
493
494 @SuppressWarnings("unchecked")
495 final Hashtable<String, Object> myLock = (Hashtable<String, Object>) lock;
496 readOnlyContexts.putAll(myLock);
497
498 return null;
499 } catch (final Exception e) {
500
501 return null;
502 }
503 }
504
505 static Object getFieldValue(Object object, String fieldName) throws IllegalAccessException {
506 return getAccessibleField(object, fieldName).get(object);
507 }
508
509 static void setFieldValue(Object object, String fieldName, Object value)
510 throws IllegalAccessException {
511 getAccessibleField(object, fieldName).set(object, value);
512 }
513
514 static boolean hasField(Object object, String fieldName) {
515 return getField(object, fieldName) != null;
516 }
517
518 private static Field getAccessibleField(Object object, String fieldName) {
519 final Field result = getField(object, fieldName);
520 assert result != null;
521 setFieldAccessible(result);
522 return result;
523 }
524
525 private static Field getField(Object object, String fieldName) {
526 assert fieldName != null;
527 Class<?> classe = object.getClass();
528 Field result = null;
529 do {
530 for (final Field field : classe.getDeclaredFields()) {
531 if (fieldName.equals(field.getName())) {
532 result = field;
533 break;
534 }
535 }
536 classe = classe.getSuperclass();
537 } while (result == null && classe != null);
538 return result;
539 }
540
541 private static void setFieldAccessible(final Field field) {
542 AccessController.doPrivileged(new PrivilegedAction<Object>() {
543
544 @Override
545 public Object run() {
546 field.setAccessible(true);
547 return null;
548 }
549 });
550 }
551
552 static void clearProxyCache() {
553 PROXY_CACHE.clear();
554 }
555
556 @SuppressWarnings("unchecked")
557 static <T> T createProxy(T object, InvocationHandler invocationHandler,
558 List<Class<?>> interfaces) {
559 final Class<?> objectClass = object.getClass();
560
561
562
563 Constructor<?> constructor = PROXY_CACHE.get(objectClass);
564 if (constructor == null) {
565 final Class<?>[] interfacesArray = getObjectInterfaces(objectClass, interfaces);
566 constructor = getProxyConstructor(objectClass, interfacesArray);
567
568
569 if (interfaces == null) {
570 PROXY_CACHE.put(objectClass, constructor);
571 }
572 }
573 try {
574 return (T) constructor.newInstance(new Object[] { invocationHandler });
575 } catch (final Exception e) {
576 throw new IllegalStateException(e);
577 }
578 }
579
580 private static Constructor<?> getProxyConstructor(Class<?> objectClass,
581 Class<?>[] interfacesArray) {
582 final ClassLoader classLoader = objectClass.getClassLoader();
583 try {
584 final Constructor<?> constructor = Proxy.getProxyClass(classLoader, interfacesArray)
585 .getConstructor(InvocationHandler.class);
586
587
588
589 constructor.setAccessible(true);
590 return constructor;
591 } catch (final NoSuchMethodException e) {
592 throw new IllegalStateException(e);
593 }
594 }
595
596 private static Class<?>[] getObjectInterfaces(Class<?> objectClass, List<Class<?>> interfaces) {
597
598
599
600 final List<Class<?>> myInterfaces;
601 if (interfaces == null) {
602 myInterfaces = new ArrayList<>(Arrays.asList(objectClass.getInterfaces()));
603 Class<?> classe = objectClass.getSuperclass();
604 while (classe != null) {
605 final Class<?>[] classInterfaces = classe.getInterfaces();
606 if (classInterfaces.length > 0) {
607 final List<Class<?>> superInterfaces = Arrays.asList(classInterfaces);
608
609 myInterfaces.removeAll(superInterfaces);
610 myInterfaces.addAll(superInterfaces);
611 }
612 classe = classe.getSuperclass();
613 }
614
615
616 myInterfaces.remove(Referenceable.class);
617 } else {
618 myInterfaces = interfaces;
619 }
620 return myInterfaces.toArray(new Class<?>[0]);
621 }
622 }
623