Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed 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.springsource.loaded.agent;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.springsource.loaded.GlobalConfiguration;
import org.springsource.loaded.LoadtimeInstrumentationPlugin;
import org.springsource.loaded.ReloadEventProcessorPlugin;
/**
* Reloading plugin for 'poking' JVM classes that are known to cache reflective state. Some of the behaviour is switched ON based on
* which classes are loaded. For example the Introspector clearing logic is only activated if the Introspector gets loaded.
*
* @author Andy Clement
* @since 0.7.3
*/
public class JVMPlugin implements ReloadEventProcessorPlugin, LoadtimeInstrumentationPlugin {
private boolean pluginBroken = false;
private boolean introspectorLoaded = false;
private boolean threadGroupContextLoaded = false;
private Field beanInfoCacheField;
private Field declaredMethodCacheField;
private Method putMethod;
private Class> threadGroupContextClass;
private Field threadGroupContext_contextsField; /* Map */
private Method threadGroupContext_removeBeanInfoMethod; /* removeBeanInfo(Class> type) { */
private void tidySerialization(Class> reloadedClass) {
// if (true) return;
try {
Class> clazz = Class.forName("java.io.ObjectStreamClass$Caches");
Field localDescsField = clazz.getDeclaredField("localDescs");
localDescsField.setAccessible(true);
ConcurrentMap cm = (ConcurrentMap)localDescsField.get(null);
// TODO [serialization] a bit extreme to wipe out everything
cm.clear();
// For some reason clearing the reflectors damages serialization - is it not a true cache?
// Field reflectorsField = clazz.getDeclaredField("reflectors");
// reflectorsField.setAccessible(true);
// cm = (ConcurrentMap)reflectorsField.get(null);
// cm.clear();
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
} catch (NoSuchFieldException e) {
throw new IllegalStateException(e);
} catch (SecurityException e) {
throw new IllegalStateException(e);
} catch (IllegalArgumentException e) {
throw new IllegalStateException(e);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
// private static class Caches {
// /** cache mapping local classes -> descriptors */
// static final ConcurrentMap> localDescs =
// new ConcurrentHashMap<>();
//
// /** cache mapping field group/local desc pairs -> field reflectors */
// static final ConcurrentMap> reflectors =
// new ConcurrentHashMap<>();
//
// /** queue for WeakReferences to local classes */
// private static final ReferenceQueue> localDescsQueue =
// new ReferenceQueue<>();
// /** queue for WeakReferences to field reflectors keys */
// private static final ReferenceQueue> reflectorsQueue =
// new ReferenceQueue<>();
// }
}
@SuppressWarnings({ "restriction", "unchecked" })
public void reloadEvent(String typename, Class> clazz, String encodedTimestamp) {
if (pluginBroken) {
return;
}
tidySerialization(clazz);
if (introspectorLoaded) {
// Clear out the Introspector BeanInfo cache entry that might exist for this class
boolean beanInfoCacheCleared = false;
// In Java7 the AppContext stuff is gone, replaced by a ThreadGroupContext.
// This code grabs the contexts map from the ThreadGroupContext object and clears out the bean info for the reloaded clazz
if (threadGroupContextLoaded) { // In Java 7
beanInfoCacheCleared = clearThreadGroupContext(clazz);
}
// GRAILS-9505 - had to introduce the flushFromCaches(). The appcontext we seem to be able to
// access from AppContext.getAppContext() isn't the same one the Introspector will be using
// so we can fail to clean up the cache. Strangely calling getAppContexts() and clearing them
// all (the code commented out below) doesn't fetch all the contexts. I'm sure it is a nuance of
// app context handling but for now the introspector call is sufficient.
// TODO doesn't this just only clear the beaninfocache for the thread the reload event
// is occurring on? which may not be the thread that was actually using the cache.
if (!beanInfoCacheCleared) {
try {
if (beanInfoCacheField == null) {
beanInfoCacheField = Introspector.class.getDeclaredField("BEANINFO_CACHE");
}
beanInfoCacheField.setAccessible(true);
Object key = beanInfoCacheField.get(null);
Map, BeanInfo> map = (Map, BeanInfo>) sun.awt.AppContext.getAppContext().get(key);
if (map != null) {
if (GlobalConfiguration.debugplugins) {
System.err.println("JVMPlugin: clearing out BeanInfo for " + clazz.getName());
}
map.remove(clazz);
}
// Set appcontexts = sun.awt.AppContext.getAppContexts();
// for (sun.awt.AppContext appcontext: appcontexts) {
// map = (Map, BeanInfo>) appcontext.get(key);
// if (map != null) {
// if (GlobalConfiguration.debugplugins) {
// System.err.println("JVMPlugin: clearing out BeanInfo for " + clazz.getName());
// }
// map.remove(clazz);
// }
// }
Introspector.flushFromCaches(clazz);
} catch (NoSuchFieldException nsfe) {
// this can happen on Java7 as the field isn't there any more, see the code above.
System.out.println("Reloading: JVMPlugin: warning: unable to clear BEANINFO_CACHE, cant find field");
} catch (Exception e) {
e.printStackTrace();
}
}
// Clear out the declaredMethodCache that may exist for this class
try {
if (declaredMethodCacheField == null) {
declaredMethodCacheField = Introspector.class.getDeclaredField("declaredMethodCache");
}
declaredMethodCacheField.setAccessible(true);
Object theCache = declaredMethodCacheField.get(null);
if (putMethod == null) {
putMethod = theCache.getClass().getDeclaredMethod("put", Object.class, Object.class);
}
putMethod.setAccessible(true);
if (GlobalConfiguration.debugplugins) {
System.err.println("JVMPlugin: clearing out declaredMethodCache in Introspector for class " + clazz.getName());
}
putMethod.invoke(theCache, clazz, null);
} catch (NoSuchFieldException nsfe) {
pluginBroken = true;
System.out
.println("Reloading: JVMPlugin: warning: unable to clear declaredMethodCache, cant find field (JDK update may fix it)");
} catch (Exception e) {
e.printStackTrace();
}
}
}
private boolean clearThreadGroupContext(Class> clazz) {
boolean beanInfoCacheCleared = false;
try {
if (threadGroupContextClass == null) {
threadGroupContextClass = Class.forName("java.beans.ThreadGroupContext", true,
Introspector.class.getClassLoader());
}
if (threadGroupContextClass != null) {
if (threadGroupContext_contextsField == null) {
threadGroupContext_contextsField = threadGroupContextClass.getDeclaredField("contexts");
threadGroupContext_removeBeanInfoMethod = threadGroupContextClass.getDeclaredMethod("removeBeanInfo",
Class.class);
}
if (threadGroupContext_contextsField != null) {
threadGroupContext_contextsField.setAccessible(true);
Object threadGroupContext_contextsField_value = threadGroupContext_contextsField.get(null);
if (threadGroupContext_contextsField_value == null) {
beanInfoCacheCleared = true;
} else {
if (threadGroupContext_contextsField_value instanceof Map) {
// Indicates Java 7 up to rev21
Map, ?> m = (Map, ?>) threadGroupContext_contextsField_value;
Collection> threadGroupContexts = m.values();
for (Object o : threadGroupContexts) {
threadGroupContext_removeBeanInfoMethod.setAccessible(true);
threadGroupContext_removeBeanInfoMethod.invoke(o, clazz);
}
beanInfoCacheCleared = true;
} else {
// At update Java7u21 it changes
Class weakIdentityMapClazz = threadGroupContext_contextsField.getType();
Field tableField = weakIdentityMapClazz.getDeclaredField("table");
tableField.setAccessible(true);
Reference>[] refs = (Reference[])tableField.get(threadGroupContext_contextsField_value);
Field valueField = null;
if (refs!=null) {
for (int i=0; i r = refs[i];
Object o = (r==null?null:r.get());
if (o!=null) {
if (valueField==null) {
valueField = r.getClass().getDeclaredField("value");
}
valueField.setAccessible(true);
Object threadGroupContext = valueField.get(r);
threadGroupContext_removeBeanInfoMethod.setAccessible(true);
threadGroupContext_removeBeanInfoMethod.invoke(threadGroupContext, clazz);
}
}
}
beanInfoCacheCleared = true;
}
}
}
}
} catch (Throwable t) {
System.err.println("Unexpected problem clearing ThreadGroupContext beaninfo: ");
t.printStackTrace();
}
return beanInfoCacheCleared;
}
public boolean accept(String slashedTypeName, ClassLoader classLoader, ProtectionDomain protectionDomain, byte[] bytes) {
if (slashedTypeName!=null) {
if (slashedTypeName.equals("java/beans/Introspector")) {
introspectorLoaded = true;
} else if (slashedTypeName.equals("java/beans/ThreadGroupContext")) {
threadGroupContextLoaded = true;
}
}
return false;
}
public byte[] modify(String slashedClassName, ClassLoader classLoader, byte[] bytes) {
return null;
}
public boolean shouldRerunStaticInitializer(String typename, Class> clazz, String encodedTimestamp) {
return false;
}
}