use of org.springsource.loaded.ReloadableType in project spring-loaded by spring-projects.
the class ClassPreProcessorAgentAdapter method transform.
/**
* @param loader the defining class loader
* @param className the name of class being loaded
* @param classBeingRedefined when hotswap is called
* @param protectionDomain the ProtectionDomain for the class represented by the bytes
* @param bytes the bytecode before weaving
* @return the weaved bytecode
*/
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException {
try {
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
log.info("> (loader=" + loader + " className=" + className + ", classBeingRedefined=" + classBeingRedefined + ", protectedDomain=" + (protectionDomain != null) + ", bytes= " + (bytes == null ? "null" : bytes.length));
}
if (classBeingRedefined != null) {
// pretend no-one attempted the reload by returning original bytes. The 'watcher' for the class
// should see the changes and pick them up. Should we force it here?
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(loader);
if (typeRegistry == null) {
return null;
}
boolean isRTN = typeRegistry.isReloadableTypeName(className);
if (isRTN) {
ReloadableType rtype = typeRegistry.getReloadableType(className, false);
// rtype.loadNewVersion(suffix, bytes);
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
log.info("Tricking HCR for " + className);
}
// returning original bytes
return rtype.bytesLoaded;
}
return null;
}
// ",classBeingRedefined=" + classBeingRedefined + ",protectionDomain=" + protectionDomain + ")");
return preProcessor.preProcess(loader, className, protectionDomain, bytes);
} catch (Throwable t) {
new RuntimeException("Reloading agent exited via exception, please raise a jira", t).printStackTrace();
return bytes;
}
}
use of org.springsource.loaded.ReloadableType in project spring-loaded by spring-projects.
the class SpringLoadedPreProcessor method preProcess.
/**
* Main entry point to Spring Loaded when it is running as an agent. This method will use the classLoader and the
* class name in order to determine whether the type should be made reloadable. Non-reloadable types will at least
* get their call sites rewritten.
*
* @param classLoader the classloader loading this type
* @param slashedClassName the slashed class name (e.g. java/lang/String) being loaded
* @param protectionDomain the protection domain for the loaded class
* @param bytes the class bytes for the class being loaded
* @return potentially modified bytes
*/
public byte[] preProcess(ClassLoader classLoader, String slashedClassName, ProtectionDomain protectionDomain, byte[] bytes) {
if (disabled) {
return bytes;
}
// TODO need configurable debug here, ability to dump any code before/after
for (Plugin plugin : getGlobalPlugins()) {
if (plugin instanceof LoadtimeInstrumentationPlugin) {
LoadtimeInstrumentationPlugin loadtimeInstrumentationPlugin = (LoadtimeInstrumentationPlugin) plugin;
if (loadtimeInstrumentationPlugin.accept(slashedClassName, classLoader, protectionDomain, bytes)) {
bytes = loadtimeInstrumentationPlugin.modify(slashedClassName, classLoader, bytes);
}
}
}
tryToEnsureSystemClassesInitialized(slashedClassName);
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(classLoader);
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
logPreProcess(classLoader, slashedClassName, typeRegistry);
}
if (typeRegistry == null) {
// A null type registry indicates nothing is being made reloadable for the classloader
if (classLoader == null && slashedClassName != null) {
// Indicates loading of a system class
if (systemClassesContainingReflection.contains(slashedClassName)) {
try {
// TODO [perf] why are we not using the cache here, is it because the list is so short?
RewriteResult rr = SystemClassReflectionRewriter.rewrite(slashedClassName, bytes);
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
log.info("System class rewritten: name=" + slashedClassName + " rewrite summary=" + rr.summarize());
}
systemClassesRequiringInitialization.put(slashedClassName, rr.bits);
return rr.bytes;
} catch (Exception re) {
re.printStackTrace();
}
} else if (slashedClassName.equals("java/lang/invoke/InnerClassLambdaMetafactory")) {
bytes = Java8.enhanceInnerClassLambdaMetaFactory(bytes);
return bytes;
} else if ((GlobalConfiguration.investigateSystemClassReflection || GlobalConfiguration.rewriteAllSystemClasses) && SystemClassReflectionInvestigator.investigate(slashedClassName, bytes, GlobalConfiguration.investigateSystemClassReflection) > 0) {
// This block can help when you suspect there is a system class using reflection and that
// class isn't on the 'shortlist' (in systemClassesContainingReflection). Basically turn on the
// options to trigger this investigation then add them to the shortlist if it looks like they need rewriting.
RewriteResult rr = SystemClassReflectionRewriter.rewrite(slashedClassName, bytes);
if (GlobalConfiguration.rewriteAllSystemClasses) {
systemClassesRequiringInitialization.put(slashedClassName, rr.bits);
return rr.bytes;
} else {
System.err.println("Type " + slashedClassName + " rewrite summary: " + rr.summarize());
return bytes;
}
}
}
return bytes;
}
// What happens here? The aim is to determine if the type should be made reloadable.
// 1. If NO, but something in this classloader might be, then rewrite the call sites.
// 2. If NO, and nothing in this classloader might be, return the original bytes.
// 3. If YES, make the type reloadable (including rewriting call sites)
ReloadableTypeNameDecision isReloadableTypeName = typeRegistry.isReloadableTypeName(slashedClassName, protectionDomain, bytes);
if (isReloadableTypeName.isReloadable && GlobalConfiguration.explainMode && log.isLoggable(Level.INFO)) {
log.info("[explanation] Based on the name, type " + slashedClassName + " is considered to be reloadable");
}
// }
if (isReloadableTypeName.isReloadable) {
if (!firstReloadableTypeHit) {
firstReloadableTypeHit = true;
// TODO move into the ctor for ReloadableType so that it can't block loading
tryToEnsureSystemClassesInitialized(slashedClassName);
}
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
log.info("processing " + slashedClassName + " as a reloadable type");
}
try {
// TODO decide one way or the other on slashed/dotted from preprocessor to infrastructure
String dottedClassName = slashedClassName.replace('/', '.');
String watchPath = getWatchPathFromProtectionDomain(protectionDomain, slashedClassName);
if (watchPath == null) {
// For a CGLIB generated type, we may still need to make the type reloadable. For example:
// type: com/vmware/rabbit/ApplicationContext$$EnhancerByCGLIB$$512eb60c
// codesource determined to be: file:/Users/aclement/springsource/tc-server-developer-2.1.1.RELEASE/spring-insight-instance/wtpwebapps/hello-rabbit-client/WEB-INF/lib/cglib-nodep-2.2.jar <no signer certificates>
// But if the type 'com/vmware/rabbit/ApplicationContext' is reloadable, then this should be too
boolean makeReloadableAnyway = false;
int cglibIndex = slashedClassName.indexOf("$$EnhancerBy");
if (cglibIndex != -1) {
String originalType = slashedClassName.substring(0, cglibIndex);
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
log.info("Appears to be a CGLIB type, checking if type " + originalType + " is reloadable");
}
TypeRegistry currentRegistry = typeRegistry;
while (currentRegistry != null) {
ReloadableType originalReloadable = currentRegistry.getReloadableType(originalType);
if (originalReloadable != null) {
makeReloadableAnyway = true;
break;
}
currentRegistry = currentRegistry.getParentRegistry();
}
// if (typeRegistry.isReloadableTypeName(originalType)) {
// if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
// log.info("Type " + originalType + " is reloadable, so making CGLIB type " + slashedClassName
// + " reloadable");
// }
// makeReloadableAnyway = true;
// }
}
int cglibIndex2 = makeReloadableAnyway ? -1 : slashedClassName.indexOf("$$FastClassByCGLIB");
if (cglibIndex2 != -1) {
String originalType = slashedClassName.substring(0, cglibIndex2);
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
log.info("Appears to be a CGLIB FastClass type, checking if type " + originalType + " is reloadable");
}
TypeRegistry currentRegistry = typeRegistry;
while (currentRegistry != null) {
ReloadableType originalReloadable = currentRegistry.getReloadableType(originalType);
if (originalReloadable != null) {
makeReloadableAnyway = true;
break;
}
currentRegistry = currentRegistry.getParentRegistry();
}
// if (typeRegistry.isReloadableTypeName(originalType)) {
// if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
// log.info("Type " + originalType + " is reloadable, so making CGLIB type " + slashedClassName
// + " reloadable");
// }
// makeReloadableAnyway = true;
// }
}
int proxyIndex = makeReloadableAnyway ? -1 : slashedClassName.indexOf("$Proxy");
if (proxyIndex == 0 || (proxyIndex > 0 && slashedClassName.charAt(proxyIndex - 1) == '/')) {
// Determine if the interfaces being implemented are reloadable
String[] interfacesImplemented = Utils.discoverInterfaces(bytes);
if (interfacesImplemented != null) {
for (int i = 0; i < interfacesImplemented.length; i++) {
TypeRegistry currentRegistry = typeRegistry;
while (currentRegistry != null) {
ReloadableType originalReloadable = currentRegistry.getReloadableType(interfacesImplemented[i]);
if (originalReloadable != null) {
makeReloadableAnyway = true;
break;
}
currentRegistry = currentRegistry.getParentRegistry();
}
// if (typeRegistry.isReloadableTypeName(interfacesImplemented[i])) {
// makeReloadableAnyway = true;
// }
}
}
}
// all classloaders below one loading reloadable stuff should also load reloadable stuff.
if (!makeReloadableAnyway && classLoader.getClass().getName().endsWith("GroovyClassLoader$InnerLoader")) {
makeReloadableAnyway = true;
}
if (!makeReloadableAnyway) {
// else {
if (GlobalConfiguration.verboseMode) {
Log.log("Cannot watch " + slashedClassName + ": not making it reloadable");
}
if (needsClientSideRewriting(slashedClassName)) {
bytes = typeRegistry.methodCallRewriteUseCacheIfAvailable(slashedClassName, bytes);
}
return bytes;
// }
}
}
ReloadableType rtype = typeRegistry.addType(dottedClassName, bytes);
if (rtype == null && GlobalConfiguration.callsideRewritingOn) {
// it is not a candidate for being made reloadable (maybe it is an annotation type)
// but we still need to rewrite call sites.
bytes = typeRegistry.methodCallRewrite(bytes);
} else {
if (GlobalConfiguration.fileSystemMonitoring && watchPath != null) {
typeRegistry.monitorForUpdates(rtype, watchPath);
}
return rtype.bytesLoaded;
}
} catch (RuntimeException re) {
log.throwing("SpringLoadedPreProcessor", "preProcess", re);
throw re;
}
} else {
try {
// with due to GroovyPlugin class that intercepts define in that infrastructure
if (needsClientSideRewriting(slashedClassName) && (classLoader == null || !classLoader.getClass().getName().equals("org.codehaus.groovy.runtime.callsite.CallSiteClassLoader"))) {
bytes = typeRegistry.methodCallRewriteUseCacheIfAvailable(slashedClassName, bytes);
}
} catch (Throwable t) {
log.log(Level.SEVERE, "Unexpected problem transforming call sites", t);
}
}
return bytes;
}
use of org.springsource.loaded.ReloadableType in project spring-loaded by spring-projects.
the class ClassReflectionTests method test_callReloadableMethodWithNonReloadableImplementation.
/**
* Scenario where method lookup spills over from the reloadable world into the non-reloadable world. This can only
* happen if are looking for a method that is declared on a reloadable type, but we need to find the implementation
* in a non-reloadable one.
*/
@Test
public void test_callReloadableMethodWithNonReloadableImplementation() throws Exception {
//Scenario requires a method that is reloadable but not implemented by a reloadable
//class. The followig circumstances trigger this condition:
// Situation involves three types:
// A non reloadable superClass
String superClassName = "reflection.NonReloadableSuperClass";
Class<?> superClass = nonReloadableClass(superClassName);
// A reloadable interface
String interfaceName = TARGET_PACKAGE + ".InterfaceTarget";
ReloadableType interfaceTarget = reloadableClass(interfaceName);
ClassPrinter.print(interfaceTarget.getBytesLoaded());
// A reloadable class
String subclassName = TARGET_PACKAGE + ".SubClassImplementsInterface";
ReloadableType subClass = reloadableClass(subclassName);
// These types relate to one another as follows:
//1) the method 'm' must be declared in the reloadable interface
String interfaceMethodName = "interfaceMethod";
Result r = getDeclaredMethod(interfaceTarget.getClazz(), interfaceMethodName);
assertMethod("public abstract java.lang.String " + interfaceName + "." + interfaceMethodName + "()", r);
Method m = (Method) r.returnValue;
//2) The reloadable type implements this interface (without providing its own implementation of m)
assertTrue(interfaceTarget.getClazz().isAssignableFrom(subClass.getClazz()));
try {
r = getDeclaredMethod(subClass.getClazz(), interfaceMethodName);
fail("Assuming that interface implementation is inherited, not directly implemented");
} catch (ResultException e) {
assertNoSuchMethodException(subclassName + "." + interfaceMethodName + "()", e);
}
//3) A non-reloadable superclass provides the actual implementation of m via inheritance
r = getDeclaredMethod(superClass, interfaceMethodName);
assertMethod("public java.lang.String " + superClassName + "." + interfaceMethodName + "()", r);
//4) Invoke the interface method on an instance of the subclass... should cause lookup
// to find implementation in non-reloadable superclass
Object subInstance = subClass.getClazz().newInstance();
r = invokeOn(subInstance, m);
assertEquals("NonReloadableSuperClass.interfaceMethod", r.returnValue);
}
use of org.springsource.loaded.ReloadableType in project spring-loaded by spring-projects.
the class CatcherTests method rewrite.
/*
* Details on catchers
*
* Four types of method in the super type to think about:
* - private
* - protected
* - default
* - public
*
* And things to keep in mind:
* - private methods are not overridable (invokespecial is used to call them)
* - visibility cannot be reduced, only widened
* - static methods are not overridable
*
* Catching rules:
* - don't need a catcher for a private method, there cannot be code out there that calls it with INVOKEVIRTUAL
* - visibility is preserved except for protected/default, which is widened to public - this enables the executor to call the
* catcher. Doesn't seem to have any side effects (doesn't limit the ability for an overriding method in a further
* subclass to have been declared initially protected).
*/
@Test
public void rewrite() throws Exception {
TypeRegistry typeRegistry = getTypeRegistry("catchers.B");
loadClass("catchers.A");
TypeDescriptor typeDescriptor = typeRegistry.getExtractor().extract(loadBytesForClass("catchers.B"), true);
checkDoesNotContain(typeDescriptor, "privateMethod");
checkDoesContain(typeDescriptor, "0x1 publicMethod");
checkDoesContain(typeDescriptor, "0x1 protectedMethod");
checkDoesContain(typeDescriptor, "0x1 defaultMethod");
ReloadableType rtype = typeRegistry.addType("catchers.B", loadBytesForClass("catchers.B"));
reload(rtype, "2");
}
use of org.springsource.loaded.ReloadableType in project spring-loaded by spring-projects.
the class CatcherTests method exerciseCatcher2.
/**
* Now we work with a mixed hierarchy. Type X declares the methods, type Y extends X does not, type Z extends Y
* does.
*/
@Test
public void exerciseCatcher2() throws Exception {
TypeRegistry registry = getTypeRegistry("catchers..*");
String x = "catchers.X";
String y = "catchers.Y";
String z = "catchers.Z";
ReloadableType rtypeX = registry.addType(x, loadBytesForClass(x));
ReloadableType rtypeY = registry.addType(y, loadBytesForClass(y));
ReloadableType rtypeZ = registry.addType(z, loadBytesForClass(z));
Class<?> clazz = loadRunner("catchers.Runner2");
Assert.assertEquals(1, runUnguarded(clazz, "runPublicX").returnValue);
// Y does not override
Assert.assertEquals(1, runUnguarded(clazz, "runPublicY").returnValue);
Assert.assertEquals(3, runUnguarded(clazz, "runPublicZ").returnValue);
Assert.assertEquals('a', runUnguarded(clazz, "runDefaultX").returnValue);
// Y does not override
Assert.assertEquals('a', runUnguarded(clazz, "runDefaultY").returnValue);
Assert.assertEquals('c', runUnguarded(clazz, "runDefaultZ").returnValue);
Assert.assertEquals(100L, runUnguarded(clazz, "runProtectedX").returnValue);
// Y does not override
Assert.assertEquals(100L, runUnguarded(clazz, "runProtectedY").returnValue);
Assert.assertEquals(300L, runUnguarded(clazz, "runProtectedZ").returnValue);
rtypeY.loadNewVersion("2", retrieveRename(y, y + "2"));
Assert.assertEquals(1, runUnguarded(clazz, "runPublicX").returnValue);
// now Y does
Assert.assertEquals(22, runUnguarded(clazz, "runPublicY").returnValue);
Assert.assertEquals(3, runUnguarded(clazz, "runPublicZ").returnValue);
Assert.assertEquals('a', runUnguarded(clazz, "runDefaultX").returnValue);
// now Y does
Assert.assertEquals('B', runUnguarded(clazz, "runDefaultY").returnValue);
Assert.assertEquals('c', runUnguarded(clazz, "runDefaultZ").returnValue);
// Runner2.runProtectedX invokes x.callProtectedMethod() which simply returns 'protectedMethod()'
Assert.assertEquals(100L, runUnguarded(clazz, "runProtectedX").returnValue);
// now Y does
Assert.assertEquals(200L, runUnguarded(clazz, "runProtectedY").returnValue);
Assert.assertEquals(300L, runUnguarded(clazz, "runProtectedZ").returnValue);
}
Aggregations