carriage returns removed from sources
This commit is contained in:
180
springloaded/gradlew.bat
vendored
180
springloaded/gradlew.bat
vendored
@@ -1,90 +1,90 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
Manifest-Version: 1.0
|
||||
Specification-Title: SpringLoaded Agent
|
||||
Specification-Version: 1.0.0
|
||||
Specification-Vendor: SpringSource
|
||||
Implementation-Title: org.springsource.loaded
|
||||
Implementation-Version: 1.0.0
|
||||
Implementation-Vendor: SpringSource
|
||||
Premain-Class: org.springsource.loaded.agent.SpringLoadedAgent
|
||||
Agent-Class: org.springsource.loaded.agent.SpringLoadedAgent
|
||||
Can-Redefine-Classes: true
|
||||
Manifest-Version: 1.0
|
||||
Specification-Title: SpringLoaded Agent
|
||||
Specification-Version: 1.0.0
|
||||
Specification-Vendor: SpringSource
|
||||
Implementation-Title: org.springsource.loaded
|
||||
Implementation-Version: 1.0.0
|
||||
Implementation-Vendor: SpringSource
|
||||
Premain-Class: org.springsource.loaded.agent.SpringLoadedAgent
|
||||
Agent-Class: org.springsource.loaded.agent.SpringLoadedAgent
|
||||
Can-Redefine-Classes: true
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Represents '*' type pattern.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class AnyTypePattern extends TypePattern {
|
||||
|
||||
public AnyTypePattern() {
|
||||
}
|
||||
|
||||
protected boolean internalMatches(String input) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "text:*";
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Represents '*' type pattern.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class AnyTypePattern extends TypePattern {
|
||||
|
||||
public AnyTypePattern() {
|
||||
}
|
||||
|
||||
protected boolean internalMatches(String input) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "text:*";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,324 +1,324 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.Handle;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
/**
|
||||
* Modify a class by changing it from one name to another. References to other types can also be changed. Basically used
|
||||
* in the test suite.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class ClassRenamer {
|
||||
|
||||
/**
|
||||
* Rename a type - changing it to specified new name (which should be the dotted form of the name). Retargets are an
|
||||
* optional sequence of retargets to also perform during the rename. Retargets take the form of "a.b:a.c" which will
|
||||
* change all references to a.b to a.c.
|
||||
*
|
||||
* @param dottedNewName dotted name, e.g. com.foo.Bar
|
||||
* @param classbytes the bytecode for the class to be renamed
|
||||
* @param retargets retarget rules for references, of the form "a.b:b.a","c.d:d.c"
|
||||
* @return bytecode for the modified class
|
||||
*/
|
||||
public static byte[] rename(String dottedNewName, byte[] classbytes, String... retargets) {
|
||||
ClassReader fileReader = new ClassReader(classbytes);
|
||||
RenameAdapter renameAdapter = new RenameAdapter(dottedNewName, retargets);
|
||||
fileReader.accept(renameAdapter, 0);
|
||||
byte[] renamed = renameAdapter.getBytes();
|
||||
return renamed;
|
||||
}
|
||||
|
||||
static class RenameAdapter extends ClassVisitor implements Opcodes {
|
||||
|
||||
private ClassWriter cw;
|
||||
|
||||
private String oldname;
|
||||
|
||||
private String newname;
|
||||
|
||||
private Map<String, String> retargets = new HashMap<String, String>();
|
||||
|
||||
public RenameAdapter(String newname, String[] retargets) {
|
||||
super(ASM5, new ClassWriter(0));
|
||||
cw = (ClassWriter) cv;
|
||||
this.newname = newname.replace('.', '/');
|
||||
if (retargets != null) {
|
||||
for (String retarget : retargets) {
|
||||
int i = retarget.indexOf(":");
|
||||
this.retargets.put(retarget.substring(0, i).replace('.', '/'),
|
||||
retarget.substring(i + 1).replace('.', '/'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
private String retargetIfNecessary(String string) {
|
||||
String value = retargets.get(string);
|
||||
return value == null ? string : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
oldname = name;
|
||||
if (superName != null) {
|
||||
superName = retargetIfNecessary(superName);
|
||||
}
|
||||
if (interfaces != null) {
|
||||
for (int i = 0; i < interfaces.length; i++) {
|
||||
interfaces[i] = retargetIfNecessary(interfaces[i]);
|
||||
}
|
||||
}
|
||||
super.visit(version, access, newname, signature, superName, interfaces);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInnerClass(String name, String outername, String innerName, int access) {
|
||||
super.visitInnerClass(renameRetargetIfNecessary(name), renameRetargetIfNecessary(outername),
|
||||
renameRetargetIfNecessary(innerName), access);
|
||||
}
|
||||
|
||||
private String renameRetargetIfNecessary(String string) {
|
||||
String value = retargets.get(string);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
if (string != null && string.indexOf(oldname) != -1) {
|
||||
return string.replace(oldname, newname);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
|
||||
String[] exceptions) {
|
||||
if (descriptor.indexOf(oldname) != -1) {
|
||||
descriptor = descriptor.replace(oldname, newname);
|
||||
}
|
||||
else {
|
||||
if (descriptor.indexOf(oldname) != -1) {
|
||||
descriptor = descriptor.replace(oldname, newname);
|
||||
}
|
||||
for (String s : retargets.keySet()) {
|
||||
if (descriptor.indexOf(s) != -1) {
|
||||
descriptor = descriptor.replace(s, retargets.get(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
MethodVisitor mv = super.visitMethod(flags, name, descriptor, signature, exceptions);
|
||||
return new RenameMethodAdapter(mv, oldname, newname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
|
||||
if (desc.indexOf(oldname) != -1) {
|
||||
desc = desc.replace(oldname, newname);
|
||||
}
|
||||
else {
|
||||
for (String s : retargets.keySet()) {
|
||||
if (desc.indexOf(s) != -1) {
|
||||
desc = desc.replace(s, retargets.get(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visitField(access, name, desc, signature, value);
|
||||
}
|
||||
|
||||
class RenameMethodAdapter extends MethodVisitor implements Opcodes {
|
||||
|
||||
String oldname;
|
||||
|
||||
String newname;
|
||||
|
||||
public RenameMethodAdapter(MethodVisitor mv, String oldname, String newname) {
|
||||
super(ASM5, mv);
|
||||
this.oldname = oldname;
|
||||
this.newname = newname;
|
||||
}
|
||||
|
||||
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
|
||||
if (owner.equals(oldname)) {
|
||||
owner = newname;
|
||||
}
|
||||
else {
|
||||
String retarget = retargets.get(owner);
|
||||
if (retarget != null) {
|
||||
owner = retarget;
|
||||
}
|
||||
}
|
||||
if (desc.indexOf(oldname) != -1) {
|
||||
desc = desc.replace(oldname, newname);
|
||||
}
|
||||
else {
|
||||
desc = checkIfShouldBeRewritten(desc);
|
||||
}
|
||||
mv.visitFieldInsn(opcode, owner, name, desc);
|
||||
}
|
||||
|
||||
public void visitTypeInsn(int opcode, String type) {
|
||||
if (type.equals(oldname)) {
|
||||
type = newname;
|
||||
}
|
||||
else {
|
||||
String retarget = retargets.get(type);
|
||||
if (retarget != null) {
|
||||
type = retarget;
|
||||
}
|
||||
else {
|
||||
if (type.startsWith("[")) {
|
||||
if (type.indexOf(oldname) != -1) {
|
||||
type = type.replaceFirst(oldname, newname);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mv.visitTypeInsn(opcode, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLdcInsn(Object obj) {
|
||||
// System.out.println("Possibly remapping "+obj);
|
||||
if (obj instanceof Type) {
|
||||
Type t = (Type) obj;
|
||||
String s = t.getInternalName();
|
||||
String retarget = retargets.get(s);
|
||||
if (retarget != null) {
|
||||
mv.visitLdcInsn(Type.getObjectType(retarget));
|
||||
}
|
||||
else {
|
||||
mv.visitLdcInsn(obj);
|
||||
}
|
||||
}
|
||||
else if (obj instanceof String) {
|
||||
String s = (String) obj;
|
||||
String retarget = retargets.get(s.replace('.', '/'));
|
||||
if (retarget != null) {
|
||||
mv.visitLdcInsn(retarget.replace('/', '.'));
|
||||
}
|
||||
else {
|
||||
String oldnameDotted = oldname.replace('/', '.');
|
||||
if (s.equals(oldnameDotted)) {
|
||||
String nname = newname.replace('/', '.');
|
||||
mv.visitLdcInsn(nname);
|
||||
return;
|
||||
}
|
||||
else if (s.startsWith("[")) {
|
||||
// might be array of oldname
|
||||
if (s.indexOf(oldnameDotted) != -1) {
|
||||
mv.visitLdcInsn(s.replaceFirst(oldnameDotted, newname.replace('/', '.')));
|
||||
return;
|
||||
}
|
||||
}
|
||||
mv.visitLdcInsn(obj);
|
||||
}
|
||||
}
|
||||
else {
|
||||
mv.visitLdcInsn(obj);
|
||||
}
|
||||
}
|
||||
|
||||
private String toString(Handle bsm) {
|
||||
return "[" + bsm.getTag() + "]" + bsm.getOwner() + "." + bsm.getName() + bsm.getDesc();
|
||||
}
|
||||
|
||||
private String toString(Object[] os) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
if (os != null) {
|
||||
buf.append("[");
|
||||
for (int i = 0; i < os.length; i++) {
|
||||
if (i > 0)
|
||||
buf.append(",");
|
||||
buf.append(os[i]);
|
||||
}
|
||||
buf.append("]");
|
||||
}
|
||||
else {
|
||||
return "null";
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private Handle retargetHandle(Handle oldHandle) {
|
||||
int tag = oldHandle.getTag();
|
||||
String owner = oldHandle.getOwner();
|
||||
String name = oldHandle.getName();
|
||||
String desc = oldHandle.getDesc();
|
||||
// System.out.println("handle: owner: "+owner);
|
||||
// System.out.println("handle: name: "+name);
|
||||
// System.out.println("handle: desc: "+desc);
|
||||
owner = renameRetargetIfNecessary(owner);
|
||||
desc = renameRetargetIfNecessary(desc);
|
||||
Handle newHandle = new Handle(tag, owner, name, desc);
|
||||
return newHandle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
|
||||
// Example:
|
||||
// visitInvokeDynamicInsn(name=m,desc=()Lbasic/LambdaA2$Foo;,
|
||||
// bsm=[6]java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;,
|
||||
// bsmArgs=[()I,basic/LambdaA2.lambda$run$1()I (6),()I])
|
||||
desc = renameRetargetIfNecessary(desc);
|
||||
if (bsmArgs[1] instanceof Handle) {
|
||||
bsmArgs[1] = retargetHandle((Handle) bsmArgs[1]);
|
||||
}
|
||||
mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
|
||||
}
|
||||
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
|
||||
if (owner.equals(oldname)) {
|
||||
owner = newname;
|
||||
}
|
||||
else {
|
||||
owner = retargetIfNecessary(owner);
|
||||
}
|
||||
if (desc.indexOf(oldname) != -1) {
|
||||
desc = desc.replace(oldname, newname);
|
||||
}
|
||||
else {
|
||||
desc = checkIfShouldBeRewritten(desc);
|
||||
}
|
||||
mv.visitMethodInsn(opcode, owner, name, desc, itf);
|
||||
}
|
||||
|
||||
private String checkIfShouldBeRewritten(String desc) {
|
||||
for (String s : retargets.keySet()) {
|
||||
if (desc.indexOf(s) != -1) {
|
||||
desc = desc.replace(s, retargets.get(s));
|
||||
}
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.Handle;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
/**
|
||||
* Modify a class by changing it from one name to another. References to other types can also be changed. Basically used
|
||||
* in the test suite.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class ClassRenamer {
|
||||
|
||||
/**
|
||||
* Rename a type - changing it to specified new name (which should be the dotted form of the name). Retargets are an
|
||||
* optional sequence of retargets to also perform during the rename. Retargets take the form of "a.b:a.c" which will
|
||||
* change all references to a.b to a.c.
|
||||
*
|
||||
* @param dottedNewName dotted name, e.g. com.foo.Bar
|
||||
* @param classbytes the bytecode for the class to be renamed
|
||||
* @param retargets retarget rules for references, of the form "a.b:b.a","c.d:d.c"
|
||||
* @return bytecode for the modified class
|
||||
*/
|
||||
public static byte[] rename(String dottedNewName, byte[] classbytes, String... retargets) {
|
||||
ClassReader fileReader = new ClassReader(classbytes);
|
||||
RenameAdapter renameAdapter = new RenameAdapter(dottedNewName, retargets);
|
||||
fileReader.accept(renameAdapter, 0);
|
||||
byte[] renamed = renameAdapter.getBytes();
|
||||
return renamed;
|
||||
}
|
||||
|
||||
static class RenameAdapter extends ClassVisitor implements Opcodes {
|
||||
|
||||
private ClassWriter cw;
|
||||
|
||||
private String oldname;
|
||||
|
||||
private String newname;
|
||||
|
||||
private Map<String, String> retargets = new HashMap<String, String>();
|
||||
|
||||
public RenameAdapter(String newname, String[] retargets) {
|
||||
super(ASM5, new ClassWriter(0));
|
||||
cw = (ClassWriter) cv;
|
||||
this.newname = newname.replace('.', '/');
|
||||
if (retargets != null) {
|
||||
for (String retarget : retargets) {
|
||||
int i = retarget.indexOf(":");
|
||||
this.retargets.put(retarget.substring(0, i).replace('.', '/'),
|
||||
retarget.substring(i + 1).replace('.', '/'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
private String retargetIfNecessary(String string) {
|
||||
String value = retargets.get(string);
|
||||
return value == null ? string : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
oldname = name;
|
||||
if (superName != null) {
|
||||
superName = retargetIfNecessary(superName);
|
||||
}
|
||||
if (interfaces != null) {
|
||||
for (int i = 0; i < interfaces.length; i++) {
|
||||
interfaces[i] = retargetIfNecessary(interfaces[i]);
|
||||
}
|
||||
}
|
||||
super.visit(version, access, newname, signature, superName, interfaces);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInnerClass(String name, String outername, String innerName, int access) {
|
||||
super.visitInnerClass(renameRetargetIfNecessary(name), renameRetargetIfNecessary(outername),
|
||||
renameRetargetIfNecessary(innerName), access);
|
||||
}
|
||||
|
||||
private String renameRetargetIfNecessary(String string) {
|
||||
String value = retargets.get(string);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
if (string != null && string.indexOf(oldname) != -1) {
|
||||
return string.replace(oldname, newname);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
|
||||
String[] exceptions) {
|
||||
if (descriptor.indexOf(oldname) != -1) {
|
||||
descriptor = descriptor.replace(oldname, newname);
|
||||
}
|
||||
else {
|
||||
if (descriptor.indexOf(oldname) != -1) {
|
||||
descriptor = descriptor.replace(oldname, newname);
|
||||
}
|
||||
for (String s : retargets.keySet()) {
|
||||
if (descriptor.indexOf(s) != -1) {
|
||||
descriptor = descriptor.replace(s, retargets.get(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
MethodVisitor mv = super.visitMethod(flags, name, descriptor, signature, exceptions);
|
||||
return new RenameMethodAdapter(mv, oldname, newname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
|
||||
if (desc.indexOf(oldname) != -1) {
|
||||
desc = desc.replace(oldname, newname);
|
||||
}
|
||||
else {
|
||||
for (String s : retargets.keySet()) {
|
||||
if (desc.indexOf(s) != -1) {
|
||||
desc = desc.replace(s, retargets.get(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visitField(access, name, desc, signature, value);
|
||||
}
|
||||
|
||||
class RenameMethodAdapter extends MethodVisitor implements Opcodes {
|
||||
|
||||
String oldname;
|
||||
|
||||
String newname;
|
||||
|
||||
public RenameMethodAdapter(MethodVisitor mv, String oldname, String newname) {
|
||||
super(ASM5, mv);
|
||||
this.oldname = oldname;
|
||||
this.newname = newname;
|
||||
}
|
||||
|
||||
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
|
||||
if (owner.equals(oldname)) {
|
||||
owner = newname;
|
||||
}
|
||||
else {
|
||||
String retarget = retargets.get(owner);
|
||||
if (retarget != null) {
|
||||
owner = retarget;
|
||||
}
|
||||
}
|
||||
if (desc.indexOf(oldname) != -1) {
|
||||
desc = desc.replace(oldname, newname);
|
||||
}
|
||||
else {
|
||||
desc = checkIfShouldBeRewritten(desc);
|
||||
}
|
||||
mv.visitFieldInsn(opcode, owner, name, desc);
|
||||
}
|
||||
|
||||
public void visitTypeInsn(int opcode, String type) {
|
||||
if (type.equals(oldname)) {
|
||||
type = newname;
|
||||
}
|
||||
else {
|
||||
String retarget = retargets.get(type);
|
||||
if (retarget != null) {
|
||||
type = retarget;
|
||||
}
|
||||
else {
|
||||
if (type.startsWith("[")) {
|
||||
if (type.indexOf(oldname) != -1) {
|
||||
type = type.replaceFirst(oldname, newname);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mv.visitTypeInsn(opcode, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLdcInsn(Object obj) {
|
||||
// System.out.println("Possibly remapping "+obj);
|
||||
if (obj instanceof Type) {
|
||||
Type t = (Type) obj;
|
||||
String s = t.getInternalName();
|
||||
String retarget = retargets.get(s);
|
||||
if (retarget != null) {
|
||||
mv.visitLdcInsn(Type.getObjectType(retarget));
|
||||
}
|
||||
else {
|
||||
mv.visitLdcInsn(obj);
|
||||
}
|
||||
}
|
||||
else if (obj instanceof String) {
|
||||
String s = (String) obj;
|
||||
String retarget = retargets.get(s.replace('.', '/'));
|
||||
if (retarget != null) {
|
||||
mv.visitLdcInsn(retarget.replace('/', '.'));
|
||||
}
|
||||
else {
|
||||
String oldnameDotted = oldname.replace('/', '.');
|
||||
if (s.equals(oldnameDotted)) {
|
||||
String nname = newname.replace('/', '.');
|
||||
mv.visitLdcInsn(nname);
|
||||
return;
|
||||
}
|
||||
else if (s.startsWith("[")) {
|
||||
// might be array of oldname
|
||||
if (s.indexOf(oldnameDotted) != -1) {
|
||||
mv.visitLdcInsn(s.replaceFirst(oldnameDotted, newname.replace('/', '.')));
|
||||
return;
|
||||
}
|
||||
}
|
||||
mv.visitLdcInsn(obj);
|
||||
}
|
||||
}
|
||||
else {
|
||||
mv.visitLdcInsn(obj);
|
||||
}
|
||||
}
|
||||
|
||||
private String toString(Handle bsm) {
|
||||
return "[" + bsm.getTag() + "]" + bsm.getOwner() + "." + bsm.getName() + bsm.getDesc();
|
||||
}
|
||||
|
||||
private String toString(Object[] os) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
if (os != null) {
|
||||
buf.append("[");
|
||||
for (int i = 0; i < os.length; i++) {
|
||||
if (i > 0)
|
||||
buf.append(",");
|
||||
buf.append(os[i]);
|
||||
}
|
||||
buf.append("]");
|
||||
}
|
||||
else {
|
||||
return "null";
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private Handle retargetHandle(Handle oldHandle) {
|
||||
int tag = oldHandle.getTag();
|
||||
String owner = oldHandle.getOwner();
|
||||
String name = oldHandle.getName();
|
||||
String desc = oldHandle.getDesc();
|
||||
// System.out.println("handle: owner: "+owner);
|
||||
// System.out.println("handle: name: "+name);
|
||||
// System.out.println("handle: desc: "+desc);
|
||||
owner = renameRetargetIfNecessary(owner);
|
||||
desc = renameRetargetIfNecessary(desc);
|
||||
Handle newHandle = new Handle(tag, owner, name, desc);
|
||||
return newHandle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
|
||||
// Example:
|
||||
// visitInvokeDynamicInsn(name=m,desc=()Lbasic/LambdaA2$Foo;,
|
||||
// bsm=[6]java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;,
|
||||
// bsmArgs=[()I,basic/LambdaA2.lambda$run$1()I (6),()I])
|
||||
desc = renameRetargetIfNecessary(desc);
|
||||
if (bsmArgs[1] instanceof Handle) {
|
||||
bsmArgs[1] = retargetHandle((Handle) bsmArgs[1]);
|
||||
}
|
||||
mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
|
||||
}
|
||||
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
|
||||
if (owner.equals(oldname)) {
|
||||
owner = newname;
|
||||
}
|
||||
else {
|
||||
owner = retargetIfNecessary(owner);
|
||||
}
|
||||
if (desc.indexOf(oldname) != -1) {
|
||||
desc = desc.replace(oldname, newname);
|
||||
}
|
||||
else {
|
||||
desc = checkIfShouldBeRewritten(desc);
|
||||
}
|
||||
mv.visitMethodInsn(opcode, owner, name, desc, itf);
|
||||
}
|
||||
|
||||
private String checkIfShouldBeRewritten(String desc) {
|
||||
for (String s : retargets.keySet()) {
|
||||
if (desc.indexOf(s) != -1) {
|
||||
desc = desc.replace(s, retargets.get(s));
|
||||
}
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,260 +1,260 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
/**
|
||||
* Common constants used throughout Spring Loaded.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public interface Constants extends Opcodes {
|
||||
|
||||
public static final Integer DEFAULT_INT = Integer.valueOf(0);
|
||||
|
||||
public static final Byte DEFAULT_BYTE = Byte.valueOf((byte) 0);
|
||||
|
||||
public static final Character DEFAULT_CHAR = Character.valueOf((char) 0);
|
||||
|
||||
public static final Short DEFAULT_SHORT = Short.valueOf((short) 0);
|
||||
|
||||
public static final Long DEFAULT_LONG = Long.valueOf(0);
|
||||
|
||||
public static final Float DEFAULT_FLOAT = Float.valueOf(0);
|
||||
|
||||
public static final Double DEFAULT_DOUBLE = Double.valueOf(0);
|
||||
|
||||
public static final Boolean DEFAULT_BOOLEAN = Boolean.FALSE;
|
||||
|
||||
static String magicDescriptorForGeneratedCtors = "org.springsource.loaded.C";
|
||||
|
||||
// TODO change r$ to _sl or sl throughout?
|
||||
static String PREFIX = "r$";
|
||||
|
||||
static String tRegistryType = "org/springsource/loaded/TypeRegistry";
|
||||
|
||||
static String lRegistryType = "L" + tRegistryType + ";";
|
||||
|
||||
static String tDynamicallyDispatchable = "org/springsource/loaded/__DynamicallyDispatchable";
|
||||
|
||||
static String lDynamicallyDispatchable = "L" + tDynamicallyDispatchable + ";";
|
||||
|
||||
static String tReloadableType = "org/springsource/loaded/ReloadableType";
|
||||
|
||||
static String lReloadableType = "L" + tReloadableType + ";";
|
||||
|
||||
static String tInstanceStateManager = "org/springsource/loaded/ISMgr";
|
||||
|
||||
static String lInstanceStateManager = "L" + tInstanceStateManager + ";";
|
||||
|
||||
static String tStaticStateManager = "org/springsource/loaded/SSMgr";
|
||||
|
||||
static String lStaticStateManager = "L" + tStaticStateManager + ";";
|
||||
|
||||
static String fReloadableTypeFieldName = PREFIX + "type";
|
||||
|
||||
// Static field holding map and accessors
|
||||
static String fStaticFieldsName = PREFIX + "sfields";
|
||||
|
||||
static String mStaticFieldSetterName = PREFIX + "sets";
|
||||
|
||||
static String mStaticFieldSetterDescriptor = "(Ljava/lang/Object;Ljava/lang/String;)V";
|
||||
|
||||
static String mStaticFieldGetterName = PREFIX + "gets";
|
||||
|
||||
// Instance field holding map and accessors
|
||||
static String fInstanceFieldsName = PREFIX + "fields";
|
||||
|
||||
static String mInstanceFieldSetterName = PREFIX + "set";
|
||||
|
||||
static String mInstanceFieldSetterDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)V";
|
||||
|
||||
static String mInstanceFieldGetterName = PREFIX + "get";
|
||||
|
||||
static String mInstanceFieldGetterDescriptor = "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;";
|
||||
|
||||
static String mStaticFieldInterceptionRequired = "staticFieldInterceptionRequired";
|
||||
|
||||
static String mInstanceFieldInterceptionRequired = "instanceFieldInterceptionRequired";
|
||||
|
||||
// method called to see if the target of what is about to be called has changed
|
||||
static String mChangedForInvocationName = "anyChanges";
|
||||
|
||||
static String mChangedForInvokeStaticName = "istcheck";
|
||||
|
||||
static String mChangedForInvokeInterfaceName = "iincheck";
|
||||
|
||||
static String mChangedForInvokeDynamicName = "idycheck";
|
||||
|
||||
static String mChangedForInvokeVirtualName = "ivicheck";
|
||||
|
||||
static String mChangedForInvokeSpecialName = "ispcheck";
|
||||
|
||||
static String mPerformInvokeDynamicName = "idyrun";
|
||||
|
||||
static String descriptorChangedForInvokeSpecialName = "(ILjava/lang/String;)Lorg/springsource/loaded/__DynamicallyDispatchable;";
|
||||
|
||||
static String mChangedForConstructorName = "ccheck";
|
||||
|
||||
static int WAS_INVOKESTATIC = 0x0001;
|
||||
|
||||
static int WAS_INVOKEVIRTUAL = 0x0002;
|
||||
|
||||
// Dynamic dispatch method
|
||||
static String mDynamicDispatchName = "__execute";
|
||||
|
||||
static String mDynamicDispatchDescriptor = "([Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;";
|
||||
|
||||
static String mInitializerName = "___init___";
|
||||
|
||||
static String mStaticInitializerName = "___clinit___";
|
||||
|
||||
static int ACC_PUBLIC_ABSTRACT = Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT;
|
||||
|
||||
static int ACC_PRIVATE_STATIC = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC;
|
||||
|
||||
static int ACC_PUBLIC_STATIC = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC;
|
||||
|
||||
static int ACC_PUBLIC_STATIC_FINAL = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
|
||||
|
||||
static int ACC_PUBLIC_INTERFACE = Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT;
|
||||
|
||||
static int ACC_PUBLIC_STATIC_SYNTHETIC = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC;
|
||||
|
||||
static int ACC_PUBLIC_SYNTHETIC = Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC;
|
||||
|
||||
static int ACC_PUBLIC_PROTECTED = Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED;
|
||||
|
||||
static int ACC_PUBLIC_PRIVATE_PROTECTED = Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED;
|
||||
|
||||
static int ACC_PRIVATE_STATIC_SYNTHETIC = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC;
|
||||
|
||||
static int ACC_PRIVATE_PROTECTED = Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED;
|
||||
|
||||
static int ACC_PRIVATE_STATIC_FINAL = ACC_FINAL | ACC_STATIC | ACC_PRIVATE;
|
||||
|
||||
static String[] NO_STRINGS = new String[0];
|
||||
|
||||
static Method[] NO_METHODS = new Method[0];
|
||||
|
||||
static Field[] NO_FIELDS = new Field[0];
|
||||
|
||||
//Name pattern used to recognise names of Executor classes.
|
||||
static Pattern executorClassNamePattern = Pattern.compile("\\$\\$E[0-9,a-z,A-Z]+$");
|
||||
|
||||
static final String jlObject = "java/lang/Object";
|
||||
|
||||
//
|
||||
public static int JLC_GETDECLAREDFIELDS = 0x0001;
|
||||
|
||||
public static int JLC_GETDECLAREDFIELD = 0x0002;
|
||||
|
||||
public static int JLC_GETFIELD = 0x0004;
|
||||
|
||||
public static int JLC_GETDECLAREDMETHODS = 0x0008;
|
||||
|
||||
public static int JLC_GETDECLAREDMETHOD = 0x0010;
|
||||
|
||||
public static int JLC_GETMETHOD = 0x0020;
|
||||
|
||||
public static int JLC_GETDECLAREDCONSTRUCTOR = 0x0040;
|
||||
|
||||
public static int JLC_GETMODIFIERS = 0x0080;
|
||||
|
||||
public static int JLC_GETMETHODS = 0x0100;
|
||||
|
||||
public static int JLC_GETCONSTRUCTOR = 0x0200;
|
||||
|
||||
public static int JLC_GETDECLAREDCONSTRUCTORS = 0x0400;
|
||||
|
||||
public static int JLRM_INVOKE = 0x0800;
|
||||
|
||||
public static int JLRF_GET = 0x1000;
|
||||
|
||||
public static int JLRF_GETLONG = 0x2000;
|
||||
|
||||
public static int JLOS_HASSTATICINITIALIZER = 0x4000;
|
||||
|
||||
// For rewritten reflection in system classes, these are used:
|
||||
// The member names are used for fields *and* methods
|
||||
static final String jlcgdfs = "__sljlcgdfs";
|
||||
|
||||
static final String jlcgdfsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Field;";
|
||||
|
||||
static final String jlcgdf = "__sljlcgdf";
|
||||
|
||||
static final String jlcgdfDescriptor = "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Field;";
|
||||
|
||||
static final String jlcgf = "__sljlcgf";
|
||||
|
||||
static final String jlcgfDescriptor = "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Field;";
|
||||
|
||||
static final String jlcgdms = "__sljlcgdms";
|
||||
|
||||
static final String jlcgdmsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Method;";
|
||||
|
||||
static final String jlcgdm = "__sljlcgdm";
|
||||
|
||||
static final String jlcgdmDescriptor = "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;";
|
||||
|
||||
static final String jlcgm = "__sljlcgm";
|
||||
|
||||
static final String jlcgmDescriptor = "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;";
|
||||
|
||||
static final String jlcgdc = "__sljlcgdc";
|
||||
|
||||
static final String jlcgdcDescriptor = "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;";
|
||||
|
||||
static final String jlcgc = "__sljlcgc";
|
||||
|
||||
static final String jlcgcDescriptor = "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;";
|
||||
|
||||
static final String jlcgmods = "__sljlcgmods";
|
||||
|
||||
static final String jlcgmodsDescriptor = "(Ljava/lang/Class;)I";
|
||||
|
||||
static final String jlcgms = "__sljlcgms";
|
||||
|
||||
static final String jlcgmsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Method;";
|
||||
|
||||
// TODO migrate those above to this slightly more comprehensible format
|
||||
static final String jlcGetDeclaredConstructorsMember = "__sljlcgdcs";
|
||||
|
||||
static final String jlcGetDeclaredConstructorsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Constructor;";
|
||||
|
||||
static final String jlrmInvokeMember = "__sljlrmi";
|
||||
|
||||
static final String jlrmInvokeDescriptor = "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;";
|
||||
|
||||
static final String jlrfGetMember = "__sljlrfg";
|
||||
|
||||
static final String jlrfGetDescriptor = "(Ljava/lang/reflect/Field;Ljava/lang/Object;)Ljava/lang/Object;";
|
||||
|
||||
static final String jlrfGetLongMember = "__sljlrfgl";
|
||||
|
||||
static final String jlrfGetLongDescriptor = "(Ljava/lang/reflect/Field;Ljava/lang/Object;)J";
|
||||
|
||||
static final String jloObjectStream_hasInitializerMethod = "__sljlos_him";
|
||||
|
||||
static final String methodSuffixSuperDispatcher = "_$superdispatcher$";
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
/**
|
||||
* Common constants used throughout Spring Loaded.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public interface Constants extends Opcodes {
|
||||
|
||||
public static final Integer DEFAULT_INT = Integer.valueOf(0);
|
||||
|
||||
public static final Byte DEFAULT_BYTE = Byte.valueOf((byte) 0);
|
||||
|
||||
public static final Character DEFAULT_CHAR = Character.valueOf((char) 0);
|
||||
|
||||
public static final Short DEFAULT_SHORT = Short.valueOf((short) 0);
|
||||
|
||||
public static final Long DEFAULT_LONG = Long.valueOf(0);
|
||||
|
||||
public static final Float DEFAULT_FLOAT = Float.valueOf(0);
|
||||
|
||||
public static final Double DEFAULT_DOUBLE = Double.valueOf(0);
|
||||
|
||||
public static final Boolean DEFAULT_BOOLEAN = Boolean.FALSE;
|
||||
|
||||
static String magicDescriptorForGeneratedCtors = "org.springsource.loaded.C";
|
||||
|
||||
// TODO change r$ to _sl or sl throughout?
|
||||
static String PREFIX = "r$";
|
||||
|
||||
static String tRegistryType = "org/springsource/loaded/TypeRegistry";
|
||||
|
||||
static String lRegistryType = "L" + tRegistryType + ";";
|
||||
|
||||
static String tDynamicallyDispatchable = "org/springsource/loaded/__DynamicallyDispatchable";
|
||||
|
||||
static String lDynamicallyDispatchable = "L" + tDynamicallyDispatchable + ";";
|
||||
|
||||
static String tReloadableType = "org/springsource/loaded/ReloadableType";
|
||||
|
||||
static String lReloadableType = "L" + tReloadableType + ";";
|
||||
|
||||
static String tInstanceStateManager = "org/springsource/loaded/ISMgr";
|
||||
|
||||
static String lInstanceStateManager = "L" + tInstanceStateManager + ";";
|
||||
|
||||
static String tStaticStateManager = "org/springsource/loaded/SSMgr";
|
||||
|
||||
static String lStaticStateManager = "L" + tStaticStateManager + ";";
|
||||
|
||||
static String fReloadableTypeFieldName = PREFIX + "type";
|
||||
|
||||
// Static field holding map and accessors
|
||||
static String fStaticFieldsName = PREFIX + "sfields";
|
||||
|
||||
static String mStaticFieldSetterName = PREFIX + "sets";
|
||||
|
||||
static String mStaticFieldSetterDescriptor = "(Ljava/lang/Object;Ljava/lang/String;)V";
|
||||
|
||||
static String mStaticFieldGetterName = PREFIX + "gets";
|
||||
|
||||
// Instance field holding map and accessors
|
||||
static String fInstanceFieldsName = PREFIX + "fields";
|
||||
|
||||
static String mInstanceFieldSetterName = PREFIX + "set";
|
||||
|
||||
static String mInstanceFieldSetterDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)V";
|
||||
|
||||
static String mInstanceFieldGetterName = PREFIX + "get";
|
||||
|
||||
static String mInstanceFieldGetterDescriptor = "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;";
|
||||
|
||||
static String mStaticFieldInterceptionRequired = "staticFieldInterceptionRequired";
|
||||
|
||||
static String mInstanceFieldInterceptionRequired = "instanceFieldInterceptionRequired";
|
||||
|
||||
// method called to see if the target of what is about to be called has changed
|
||||
static String mChangedForInvocationName = "anyChanges";
|
||||
|
||||
static String mChangedForInvokeStaticName = "istcheck";
|
||||
|
||||
static String mChangedForInvokeInterfaceName = "iincheck";
|
||||
|
||||
static String mChangedForInvokeDynamicName = "idycheck";
|
||||
|
||||
static String mChangedForInvokeVirtualName = "ivicheck";
|
||||
|
||||
static String mChangedForInvokeSpecialName = "ispcheck";
|
||||
|
||||
static String mPerformInvokeDynamicName = "idyrun";
|
||||
|
||||
static String descriptorChangedForInvokeSpecialName = "(ILjava/lang/String;)Lorg/springsource/loaded/__DynamicallyDispatchable;";
|
||||
|
||||
static String mChangedForConstructorName = "ccheck";
|
||||
|
||||
static int WAS_INVOKESTATIC = 0x0001;
|
||||
|
||||
static int WAS_INVOKEVIRTUAL = 0x0002;
|
||||
|
||||
// Dynamic dispatch method
|
||||
static String mDynamicDispatchName = "__execute";
|
||||
|
||||
static String mDynamicDispatchDescriptor = "([Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;";
|
||||
|
||||
static String mInitializerName = "___init___";
|
||||
|
||||
static String mStaticInitializerName = "___clinit___";
|
||||
|
||||
static int ACC_PUBLIC_ABSTRACT = Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT;
|
||||
|
||||
static int ACC_PRIVATE_STATIC = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC;
|
||||
|
||||
static int ACC_PUBLIC_STATIC = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC;
|
||||
|
||||
static int ACC_PUBLIC_STATIC_FINAL = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
|
||||
|
||||
static int ACC_PUBLIC_INTERFACE = Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT;
|
||||
|
||||
static int ACC_PUBLIC_STATIC_SYNTHETIC = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC;
|
||||
|
||||
static int ACC_PUBLIC_SYNTHETIC = Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC;
|
||||
|
||||
static int ACC_PUBLIC_PROTECTED = Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED;
|
||||
|
||||
static int ACC_PUBLIC_PRIVATE_PROTECTED = Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED;
|
||||
|
||||
static int ACC_PRIVATE_STATIC_SYNTHETIC = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC;
|
||||
|
||||
static int ACC_PRIVATE_PROTECTED = Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED;
|
||||
|
||||
static int ACC_PRIVATE_STATIC_FINAL = ACC_FINAL | ACC_STATIC | ACC_PRIVATE;
|
||||
|
||||
static String[] NO_STRINGS = new String[0];
|
||||
|
||||
static Method[] NO_METHODS = new Method[0];
|
||||
|
||||
static Field[] NO_FIELDS = new Field[0];
|
||||
|
||||
//Name pattern used to recognise names of Executor classes.
|
||||
static Pattern executorClassNamePattern = Pattern.compile("\\$\\$E[0-9,a-z,A-Z]+$");
|
||||
|
||||
static final String jlObject = "java/lang/Object";
|
||||
|
||||
//
|
||||
public static int JLC_GETDECLAREDFIELDS = 0x0001;
|
||||
|
||||
public static int JLC_GETDECLAREDFIELD = 0x0002;
|
||||
|
||||
public static int JLC_GETFIELD = 0x0004;
|
||||
|
||||
public static int JLC_GETDECLAREDMETHODS = 0x0008;
|
||||
|
||||
public static int JLC_GETDECLAREDMETHOD = 0x0010;
|
||||
|
||||
public static int JLC_GETMETHOD = 0x0020;
|
||||
|
||||
public static int JLC_GETDECLAREDCONSTRUCTOR = 0x0040;
|
||||
|
||||
public static int JLC_GETMODIFIERS = 0x0080;
|
||||
|
||||
public static int JLC_GETMETHODS = 0x0100;
|
||||
|
||||
public static int JLC_GETCONSTRUCTOR = 0x0200;
|
||||
|
||||
public static int JLC_GETDECLAREDCONSTRUCTORS = 0x0400;
|
||||
|
||||
public static int JLRM_INVOKE = 0x0800;
|
||||
|
||||
public static int JLRF_GET = 0x1000;
|
||||
|
||||
public static int JLRF_GETLONG = 0x2000;
|
||||
|
||||
public static int JLOS_HASSTATICINITIALIZER = 0x4000;
|
||||
|
||||
// For rewritten reflection in system classes, these are used:
|
||||
// The member names are used for fields *and* methods
|
||||
static final String jlcgdfs = "__sljlcgdfs";
|
||||
|
||||
static final String jlcgdfsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Field;";
|
||||
|
||||
static final String jlcgdf = "__sljlcgdf";
|
||||
|
||||
static final String jlcgdfDescriptor = "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Field;";
|
||||
|
||||
static final String jlcgf = "__sljlcgf";
|
||||
|
||||
static final String jlcgfDescriptor = "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Field;";
|
||||
|
||||
static final String jlcgdms = "__sljlcgdms";
|
||||
|
||||
static final String jlcgdmsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Method;";
|
||||
|
||||
static final String jlcgdm = "__sljlcgdm";
|
||||
|
||||
static final String jlcgdmDescriptor = "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;";
|
||||
|
||||
static final String jlcgm = "__sljlcgm";
|
||||
|
||||
static final String jlcgmDescriptor = "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;";
|
||||
|
||||
static final String jlcgdc = "__sljlcgdc";
|
||||
|
||||
static final String jlcgdcDescriptor = "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;";
|
||||
|
||||
static final String jlcgc = "__sljlcgc";
|
||||
|
||||
static final String jlcgcDescriptor = "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;";
|
||||
|
||||
static final String jlcgmods = "__sljlcgmods";
|
||||
|
||||
static final String jlcgmodsDescriptor = "(Ljava/lang/Class;)I";
|
||||
|
||||
static final String jlcgms = "__sljlcgms";
|
||||
|
||||
static final String jlcgmsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Method;";
|
||||
|
||||
// TODO migrate those above to this slightly more comprehensible format
|
||||
static final String jlcGetDeclaredConstructorsMember = "__sljlcgdcs";
|
||||
|
||||
static final String jlcGetDeclaredConstructorsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Constructor;";
|
||||
|
||||
static final String jlrmInvokeMember = "__sljlrmi";
|
||||
|
||||
static final String jlrmInvokeDescriptor = "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;";
|
||||
|
||||
static final String jlrfGetMember = "__sljlrfg";
|
||||
|
||||
static final String jlrfGetDescriptor = "(Ljava/lang/reflect/Field;Ljava/lang/Object;)Ljava/lang/Object;";
|
||||
|
||||
static final String jlrfGetLongMember = "__sljlrfgl";
|
||||
|
||||
static final String jlrfGetLongDescriptor = "(Ljava/lang/reflect/Field;Ljava/lang/Object;)J";
|
||||
|
||||
static final String jloObjectStream_hasInitializerMethod = "__sljlos_him";
|
||||
|
||||
static final String methodSuffixSuperDispatcher = "_$superdispatcher$";
|
||||
}
|
||||
|
||||
@@ -1,317 +1,317 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
/**
|
||||
* Captures the information about the reloaded parts of a type that vary each time a new version is loaded.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class CurrentLiveVersion {
|
||||
|
||||
private static Logger log = Logger.getLogger(CurrentLiveVersion.class.getName());
|
||||
|
||||
// Which reloadable type this represents the live version of
|
||||
final ReloadableType reloadableType;
|
||||
|
||||
// Type descriptor for this live version
|
||||
final TypeDescriptor typeDescriptor;
|
||||
|
||||
// 'stamp' (i.e. suffix) for this version
|
||||
final String versionstamp;
|
||||
|
||||
public final IncrementalTypeDescriptor incrementalTypeDescriptor;
|
||||
|
||||
String dispatcherName;
|
||||
|
||||
byte[] dispatcher;
|
||||
|
||||
Class<?> dispatcherClass;
|
||||
|
||||
Object dispatcherInstance;
|
||||
|
||||
String executorName;
|
||||
|
||||
byte[] executor;
|
||||
|
||||
Class<?> executorClass;
|
||||
|
||||
TypeDelta typeDelta;
|
||||
|
||||
private Method staticInitializer;
|
||||
|
||||
private boolean haveLookedForStaticInitializer;
|
||||
|
||||
public boolean staticInitializedNeedsRerunningOnDefine = false;
|
||||
|
||||
public CurrentLiveVersion(ReloadableType reloadableType, String versionstamp, byte[] newbytedata) {
|
||||
if (GlobalConfiguration.logging && log.isLoggable(Level.FINER)) {
|
||||
log.entering("CurrentLiveVersion", "<init>", " new version of " + reloadableType.getName()
|
||||
+ " loaded, version stamp '"
|
||||
+ versionstamp + "'");
|
||||
}
|
||||
this.reloadableType = reloadableType;
|
||||
this.typeDescriptor = reloadableType.getTypeRegistry().getExtractor().extract(newbytedata, true);
|
||||
this.versionstamp = versionstamp;
|
||||
|
||||
if (GlobalConfiguration.assertsMode) {
|
||||
if (!this.typeDescriptor.getName().equals(reloadableType.typedescriptor.getName())) {
|
||||
throw new IllegalStateException("New version has wrong name. Expected "
|
||||
+ reloadableType.typedescriptor.getName()
|
||||
+ " but was " + typeDescriptor.getName());
|
||||
}
|
||||
}
|
||||
|
||||
newbytedata = GlobalConfiguration.callsideRewritingOn ? MethodInvokerRewriter.rewrite(
|
||||
reloadableType.typeRegistry,
|
||||
newbytedata) : newbytedata;
|
||||
|
||||
this.incrementalTypeDescriptor = new IncrementalTypeDescriptor(reloadableType.typedescriptor);
|
||||
this.incrementalTypeDescriptor.setLatestTypeDescriptor(this.typeDescriptor);
|
||||
|
||||
// Executors for interfaces simply hold annotations
|
||||
this.executor = reloadableType.getTypeRegistry().executorBuilder.createFor(reloadableType, versionstamp,
|
||||
typeDescriptor,
|
||||
newbytedata);
|
||||
|
||||
if (GlobalConfiguration.classesToDump != null
|
||||
&& GlobalConfiguration.classesToDump.contains(reloadableType.getSlashedName())) {
|
||||
Utils.dump(Utils.getExecutorName(reloadableType.getName(), versionstamp).replace('.', '/'), this.executor);
|
||||
}
|
||||
if (!typeDescriptor.isInterface()) {
|
||||
this.dispatcherName = Utils.getDispatcherName(reloadableType.getName(), versionstamp);
|
||||
this.executorName = Utils.getExecutorName(reloadableType.getName(), versionstamp);
|
||||
this.dispatcher = DispatcherBuilder.createFor(reloadableType, incrementalTypeDescriptor, versionstamp);
|
||||
}
|
||||
reloadableType.typeRegistry.checkChildClassLoader(reloadableType);
|
||||
define();
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines this version. Called up front but can also be called later if the ChildClassLoader in a type registry is
|
||||
* discarded and recreated.
|
||||
*/
|
||||
public void define() {
|
||||
staticInitializer = null;
|
||||
haveLookedForStaticInitializer = false;
|
||||
if (!typeDescriptor.isInterface()) {
|
||||
try {
|
||||
dispatcherClass = reloadableType.typeRegistry.defineClass(dispatcherName, dispatcher, false);
|
||||
}
|
||||
catch (RuntimeException t) {
|
||||
// TODO check for something strange. something to do with the file detection misbehaving, see the same file attempted to be reloaded twice...
|
||||
if (t.getMessage().indexOf("duplicate class definition") == -1) {
|
||||
throw t;
|
||||
}
|
||||
else {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
executorClass = reloadableType.typeRegistry.defineClass(executorName, executor, false);
|
||||
}
|
||||
catch (RuntimeException t) {
|
||||
// TODO check for something strange. something to do with the file detection misbehaving, see the same file attempted to be reloaded twice...
|
||||
if (t.getMessage().indexOf("duplicate class definition") == -1) {
|
||||
throw t;
|
||||
}
|
||||
else {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (!typeDescriptor.isInterface()) {
|
||||
try {
|
||||
dispatcherInstance = dispatcherClass.newInstance();
|
||||
}
|
||||
catch (InstantiationException e) {
|
||||
throw new RuntimeException("Unable to build dispatcher class instance", e);
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Unable to build dispatcher class instance", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MethodMember getReloadableMethod(String name, String descriptor) {
|
||||
// Look through the methods on the latest loaded version and find the method we want
|
||||
MethodMember[] methods = incrementalTypeDescriptor.getLatestTypeDescriptor().getMethods();
|
||||
for (MethodMember rmethod : methods) {
|
||||
if (rmethod.getName().equals(name)) {
|
||||
if (descriptor.equals(rmethod.getDescriptor())) {
|
||||
return rmethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO should be caching the result in the MethodMember objects for speed
|
||||
public Method getExecutorMethod(MethodMember methodMember) {
|
||||
String executorDescriptor;
|
||||
String name;
|
||||
|
||||
//What to search for:
|
||||
if (methodMember.isConstructor()) {
|
||||
name = Constants.mInitializerName;
|
||||
}
|
||||
else {
|
||||
name = methodMember.getName();
|
||||
}
|
||||
executorDescriptor = getExecutorDescriptor(methodMember);
|
||||
|
||||
//Search for it:
|
||||
if (executorClass != null) {
|
||||
Method[] executorMethods = executorClass.getDeclaredMethods();
|
||||
for (Method executor : executorMethods) {
|
||||
if (executor.getName().equals(name) && Type.getMethodDescriptor(executor).equals(executorDescriptor)) {
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getExecutorDescriptor(MethodMember methodMember) {
|
||||
Type[] params = Type.getArgumentTypes(methodMember.getDescriptor());
|
||||
Type[] newParametersArray = params;
|
||||
if (!methodMember.isStatic()) {
|
||||
newParametersArray = new Type[params.length + 1];
|
||||
System.arraycopy(params, 0, newParametersArray, 1, params.length);
|
||||
newParametersArray[0] = Type.getType(reloadableType.getClazz());
|
||||
}
|
||||
String executorDescriptor = Type.getMethodDescriptor(Type.getReturnType(methodMember.getDescriptor()),
|
||||
newParametersArray);
|
||||
return executorDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CurrentLiveVersion [reloadableType=" + reloadableType + ", typeDescriptor=" + typeDescriptor
|
||||
+ ", versionstamp="
|
||||
+ versionstamp + ", dispatcherName=" + dispatcherName + ", executorName=" + executorName + "]";
|
||||
}
|
||||
|
||||
public Class<?> getExecutorClass() {
|
||||
return executorClass;
|
||||
}
|
||||
|
||||
public String getVersionStamp() {
|
||||
return versionstamp;
|
||||
}
|
||||
|
||||
public Field getExecutorField(String name) throws SecurityException, NoSuchFieldException {
|
||||
return executorClass.getDeclaredField(name);
|
||||
}
|
||||
|
||||
public TypeDelta getTypeDelta() {
|
||||
return typeDelta;
|
||||
}
|
||||
|
||||
public void setTypeDelta(TypeDelta td) {
|
||||
typeDelta = td;
|
||||
}
|
||||
|
||||
public boolean hasClinit() {
|
||||
return typeDescriptor.hasClinit();
|
||||
}
|
||||
|
||||
public boolean hasConstructorChanged(String descriptor) {
|
||||
MethodMember mm = typeDescriptor.getConstructor(descriptor);
|
||||
return hasConstructorChanged(mm);
|
||||
}
|
||||
|
||||
public boolean hasConstructorChanged(MethodMember mm) {
|
||||
if (mm == null) {
|
||||
return true;
|
||||
}
|
||||
// need to look at the delta
|
||||
if (typeDelta.haveMethodsChangedOrBeenAddedOrRemoved()) {
|
||||
if (typeDelta.haveMethodsChanged()) {
|
||||
MethodDelta md = typeDelta.changedMethods.get(mm.name + mm.descriptor);
|
||||
if (md != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (typeDelta.haveMethodsBeenAdded()) {
|
||||
MethodNode mn = typeDelta.brandNewMethods.get(mm.name + mm.descriptor);
|
||||
if (mn != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (typeDelta.haveMethodsBeenDeleted()) {
|
||||
MethodNode mn = typeDelta.lostMethods.get(mm.name + mm.descriptor);
|
||||
if (mn != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO can we speed this up?
|
||||
public boolean hasConstructorChanged(int ctorId) {
|
||||
// need to find the constructor that id is for
|
||||
MethodMember mm = typeDescriptor.getConstructor(ctorId);
|
||||
return hasConstructorChanged(mm);
|
||||
}
|
||||
|
||||
public void clearClassloaderLinks() {
|
||||
this.executorClass = null;
|
||||
this.dispatcherClass = null;
|
||||
}
|
||||
|
||||
public void reloadMostRecentDispatcherAndExecutor() {
|
||||
define();
|
||||
}
|
||||
|
||||
public Object getDispatcherInstance() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public void runStaticInitializer() {
|
||||
if (!haveLookedForStaticInitializer) {
|
||||
try {
|
||||
staticInitializer = this.getExecutorClass().getDeclaredMethod(Constants.mStaticInitializerName);
|
||||
}
|
||||
catch (NoSuchMethodException e) {
|
||||
// some types don't have a static initializer, that is OK
|
||||
}
|
||||
haveLookedForStaticInitializer = true;
|
||||
}
|
||||
if (staticInitializer != null) {
|
||||
try {
|
||||
staticInitializer.invoke(null);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.severe("Unexpected exception whilst trying to call the static initializer on "
|
||||
+ this.reloadableType.getName());
|
||||
e.printStackTrace(); // TODO remove when happy
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
/**
|
||||
* Captures the information about the reloaded parts of a type that vary each time a new version is loaded.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class CurrentLiveVersion {
|
||||
|
||||
private static Logger log = Logger.getLogger(CurrentLiveVersion.class.getName());
|
||||
|
||||
// Which reloadable type this represents the live version of
|
||||
final ReloadableType reloadableType;
|
||||
|
||||
// Type descriptor for this live version
|
||||
final TypeDescriptor typeDescriptor;
|
||||
|
||||
// 'stamp' (i.e. suffix) for this version
|
||||
final String versionstamp;
|
||||
|
||||
public final IncrementalTypeDescriptor incrementalTypeDescriptor;
|
||||
|
||||
String dispatcherName;
|
||||
|
||||
byte[] dispatcher;
|
||||
|
||||
Class<?> dispatcherClass;
|
||||
|
||||
Object dispatcherInstance;
|
||||
|
||||
String executorName;
|
||||
|
||||
byte[] executor;
|
||||
|
||||
Class<?> executorClass;
|
||||
|
||||
TypeDelta typeDelta;
|
||||
|
||||
private Method staticInitializer;
|
||||
|
||||
private boolean haveLookedForStaticInitializer;
|
||||
|
||||
public boolean staticInitializedNeedsRerunningOnDefine = false;
|
||||
|
||||
public CurrentLiveVersion(ReloadableType reloadableType, String versionstamp, byte[] newbytedata) {
|
||||
if (GlobalConfiguration.logging && log.isLoggable(Level.FINER)) {
|
||||
log.entering("CurrentLiveVersion", "<init>", " new version of " + reloadableType.getName()
|
||||
+ " loaded, version stamp '"
|
||||
+ versionstamp + "'");
|
||||
}
|
||||
this.reloadableType = reloadableType;
|
||||
this.typeDescriptor = reloadableType.getTypeRegistry().getExtractor().extract(newbytedata, true);
|
||||
this.versionstamp = versionstamp;
|
||||
|
||||
if (GlobalConfiguration.assertsMode) {
|
||||
if (!this.typeDescriptor.getName().equals(reloadableType.typedescriptor.getName())) {
|
||||
throw new IllegalStateException("New version has wrong name. Expected "
|
||||
+ reloadableType.typedescriptor.getName()
|
||||
+ " but was " + typeDescriptor.getName());
|
||||
}
|
||||
}
|
||||
|
||||
newbytedata = GlobalConfiguration.callsideRewritingOn ? MethodInvokerRewriter.rewrite(
|
||||
reloadableType.typeRegistry,
|
||||
newbytedata) : newbytedata;
|
||||
|
||||
this.incrementalTypeDescriptor = new IncrementalTypeDescriptor(reloadableType.typedescriptor);
|
||||
this.incrementalTypeDescriptor.setLatestTypeDescriptor(this.typeDescriptor);
|
||||
|
||||
// Executors for interfaces simply hold annotations
|
||||
this.executor = reloadableType.getTypeRegistry().executorBuilder.createFor(reloadableType, versionstamp,
|
||||
typeDescriptor,
|
||||
newbytedata);
|
||||
|
||||
if (GlobalConfiguration.classesToDump != null
|
||||
&& GlobalConfiguration.classesToDump.contains(reloadableType.getSlashedName())) {
|
||||
Utils.dump(Utils.getExecutorName(reloadableType.getName(), versionstamp).replace('.', '/'), this.executor);
|
||||
}
|
||||
if (!typeDescriptor.isInterface()) {
|
||||
this.dispatcherName = Utils.getDispatcherName(reloadableType.getName(), versionstamp);
|
||||
this.executorName = Utils.getExecutorName(reloadableType.getName(), versionstamp);
|
||||
this.dispatcher = DispatcherBuilder.createFor(reloadableType, incrementalTypeDescriptor, versionstamp);
|
||||
}
|
||||
reloadableType.typeRegistry.checkChildClassLoader(reloadableType);
|
||||
define();
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines this version. Called up front but can also be called later if the ChildClassLoader in a type registry is
|
||||
* discarded and recreated.
|
||||
*/
|
||||
public void define() {
|
||||
staticInitializer = null;
|
||||
haveLookedForStaticInitializer = false;
|
||||
if (!typeDescriptor.isInterface()) {
|
||||
try {
|
||||
dispatcherClass = reloadableType.typeRegistry.defineClass(dispatcherName, dispatcher, false);
|
||||
}
|
||||
catch (RuntimeException t) {
|
||||
// TODO check for something strange. something to do with the file detection misbehaving, see the same file attempted to be reloaded twice...
|
||||
if (t.getMessage().indexOf("duplicate class definition") == -1) {
|
||||
throw t;
|
||||
}
|
||||
else {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
executorClass = reloadableType.typeRegistry.defineClass(executorName, executor, false);
|
||||
}
|
||||
catch (RuntimeException t) {
|
||||
// TODO check for something strange. something to do with the file detection misbehaving, see the same file attempted to be reloaded twice...
|
||||
if (t.getMessage().indexOf("duplicate class definition") == -1) {
|
||||
throw t;
|
||||
}
|
||||
else {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (!typeDescriptor.isInterface()) {
|
||||
try {
|
||||
dispatcherInstance = dispatcherClass.newInstance();
|
||||
}
|
||||
catch (InstantiationException e) {
|
||||
throw new RuntimeException("Unable to build dispatcher class instance", e);
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Unable to build dispatcher class instance", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MethodMember getReloadableMethod(String name, String descriptor) {
|
||||
// Look through the methods on the latest loaded version and find the method we want
|
||||
MethodMember[] methods = incrementalTypeDescriptor.getLatestTypeDescriptor().getMethods();
|
||||
for (MethodMember rmethod : methods) {
|
||||
if (rmethod.getName().equals(name)) {
|
||||
if (descriptor.equals(rmethod.getDescriptor())) {
|
||||
return rmethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO should be caching the result in the MethodMember objects for speed
|
||||
public Method getExecutorMethod(MethodMember methodMember) {
|
||||
String executorDescriptor;
|
||||
String name;
|
||||
|
||||
//What to search for:
|
||||
if (methodMember.isConstructor()) {
|
||||
name = Constants.mInitializerName;
|
||||
}
|
||||
else {
|
||||
name = methodMember.getName();
|
||||
}
|
||||
executorDescriptor = getExecutorDescriptor(methodMember);
|
||||
|
||||
//Search for it:
|
||||
if (executorClass != null) {
|
||||
Method[] executorMethods = executorClass.getDeclaredMethods();
|
||||
for (Method executor : executorMethods) {
|
||||
if (executor.getName().equals(name) && Type.getMethodDescriptor(executor).equals(executorDescriptor)) {
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getExecutorDescriptor(MethodMember methodMember) {
|
||||
Type[] params = Type.getArgumentTypes(methodMember.getDescriptor());
|
||||
Type[] newParametersArray = params;
|
||||
if (!methodMember.isStatic()) {
|
||||
newParametersArray = new Type[params.length + 1];
|
||||
System.arraycopy(params, 0, newParametersArray, 1, params.length);
|
||||
newParametersArray[0] = Type.getType(reloadableType.getClazz());
|
||||
}
|
||||
String executorDescriptor = Type.getMethodDescriptor(Type.getReturnType(methodMember.getDescriptor()),
|
||||
newParametersArray);
|
||||
return executorDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CurrentLiveVersion [reloadableType=" + reloadableType + ", typeDescriptor=" + typeDescriptor
|
||||
+ ", versionstamp="
|
||||
+ versionstamp + ", dispatcherName=" + dispatcherName + ", executorName=" + executorName + "]";
|
||||
}
|
||||
|
||||
public Class<?> getExecutorClass() {
|
||||
return executorClass;
|
||||
}
|
||||
|
||||
public String getVersionStamp() {
|
||||
return versionstamp;
|
||||
}
|
||||
|
||||
public Field getExecutorField(String name) throws SecurityException, NoSuchFieldException {
|
||||
return executorClass.getDeclaredField(name);
|
||||
}
|
||||
|
||||
public TypeDelta getTypeDelta() {
|
||||
return typeDelta;
|
||||
}
|
||||
|
||||
public void setTypeDelta(TypeDelta td) {
|
||||
typeDelta = td;
|
||||
}
|
||||
|
||||
public boolean hasClinit() {
|
||||
return typeDescriptor.hasClinit();
|
||||
}
|
||||
|
||||
public boolean hasConstructorChanged(String descriptor) {
|
||||
MethodMember mm = typeDescriptor.getConstructor(descriptor);
|
||||
return hasConstructorChanged(mm);
|
||||
}
|
||||
|
||||
public boolean hasConstructorChanged(MethodMember mm) {
|
||||
if (mm == null) {
|
||||
return true;
|
||||
}
|
||||
// need to look at the delta
|
||||
if (typeDelta.haveMethodsChangedOrBeenAddedOrRemoved()) {
|
||||
if (typeDelta.haveMethodsChanged()) {
|
||||
MethodDelta md = typeDelta.changedMethods.get(mm.name + mm.descriptor);
|
||||
if (md != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (typeDelta.haveMethodsBeenAdded()) {
|
||||
MethodNode mn = typeDelta.brandNewMethods.get(mm.name + mm.descriptor);
|
||||
if (mn != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (typeDelta.haveMethodsBeenDeleted()) {
|
||||
MethodNode mn = typeDelta.lostMethods.get(mm.name + mm.descriptor);
|
||||
if (mn != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO can we speed this up?
|
||||
public boolean hasConstructorChanged(int ctorId) {
|
||||
// need to find the constructor that id is for
|
||||
MethodMember mm = typeDescriptor.getConstructor(ctorId);
|
||||
return hasConstructorChanged(mm);
|
||||
}
|
||||
|
||||
public void clearClassloaderLinks() {
|
||||
this.executorClass = null;
|
||||
this.dispatcherClass = null;
|
||||
}
|
||||
|
||||
public void reloadMostRecentDispatcherAndExecutor() {
|
||||
define();
|
||||
}
|
||||
|
||||
public Object getDispatcherInstance() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public void runStaticInitializer() {
|
||||
if (!haveLookedForStaticInitializer) {
|
||||
try {
|
||||
staticInitializer = this.getExecutorClass().getDeclaredMethod(Constants.mStaticInitializerName);
|
||||
}
|
||||
catch (NoSuchMethodException e) {
|
||||
// some types don't have a static initializer, that is OK
|
||||
}
|
||||
haveLookedForStaticInitializer = true;
|
||||
}
|
||||
if (staticInitializer != null) {
|
||||
try {
|
||||
staticInitializer.invoke(null);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.severe("Unexpected exception whilst trying to call the static initializer on "
|
||||
+ this.reloadableType.getName());
|
||||
e.printStackTrace(); // TODO remove when happy
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,348 +1,348 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.springsource.loaded.Utils.ReturnType;
|
||||
|
||||
|
||||
/**
|
||||
* Builder that creates the dispatcher. The dispatcher is the implementation of the interface extracted for a type which
|
||||
* then delegates to the executor. A new dispatcher (and executor) is built for each class reload.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class DispatcherBuilder {
|
||||
|
||||
/**
|
||||
* Factory method that builds the dispatcher for a specified reloadabletype.
|
||||
*
|
||||
* @param rtype the reloadable type
|
||||
* @param newVersionTypeDescriptor the descriptor of the new version (the executor will be generated according to
|
||||
* this)
|
||||
* @param versionstamp the suffix that should be appended to the generated dispatcher
|
||||
* @return the bytecode for the new dispatcher
|
||||
*/
|
||||
public static byte[] createFor(ReloadableType rtype, IncrementalTypeDescriptor newVersionTypeDescriptor,
|
||||
String versionstamp) {
|
||||
ClassReader fileReader = new ClassReader(rtype.interfaceBytes);
|
||||
DispatcherBuilderVisitor dispatcherVisitor = new DispatcherBuilderVisitor(rtype, newVersionTypeDescriptor,
|
||||
versionstamp);
|
||||
fileReader.accept(dispatcherVisitor, 0);
|
||||
return dispatcherVisitor.getBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whilst visiting the interface, the implementation is created.
|
||||
*/
|
||||
static class DispatcherBuilderVisitor extends ClassVisitor implements Opcodes, Constants {
|
||||
|
||||
private ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
|
||||
private String classname;
|
||||
|
||||
private String executorClassName;
|
||||
|
||||
private String suffix;
|
||||
|
||||
private ReloadableType rtype;
|
||||
|
||||
private IncrementalTypeDescriptor typeDescriptor;
|
||||
|
||||
public DispatcherBuilderVisitor(ReloadableType rtype, IncrementalTypeDescriptor typeDescriptor, String suffix) {
|
||||
super(ASM5);
|
||||
this.classname = rtype.getSlashedName();
|
||||
this.typeDescriptor = typeDescriptor;
|
||||
this.suffix = suffix;
|
||||
this.rtype = rtype;
|
||||
this.executorClassName = Utils.getExecutorName(classname, suffix);
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
public void visit(int version, int flags, String name, String signature, String superclassName,
|
||||
String[] interfaceNames) {
|
||||
String dispatcherName = Utils.getDispatcherName(classname, suffix);
|
||||
cw.visit(version, Opcodes.ACC_PUBLIC, dispatcherName, null, "java/lang/Object",
|
||||
new String[] { Utils.getInterfaceName(classname),
|
||||
"org/springsource/loaded/__DynamicallyDispatchable" });
|
||||
generateDefaultConstructor();
|
||||
}
|
||||
|
||||
private void generateDefaultConstructor() {
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void generateClinitDispatcher() {
|
||||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, mStaticInitializerName, "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitMethodInsn(INVOKESTATIC, executorClassName, mStaticInitializerName, "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String arg0, boolean arg1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitAttribute(Attribute arg0) {
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
}
|
||||
|
||||
public FieldVisitor visitField(int arg0, String arg1, String arg2, String arg3, Object arg4) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitInnerClass(String arg0, String arg1, String arg2, int arg3) {
|
||||
}
|
||||
|
||||
public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
|
||||
String[] exceptions) {
|
||||
if (name.equals(mDynamicDispatchName)) {
|
||||
generateDynamicDispatchMethod(name, descriptor, signature, exceptions);
|
||||
}
|
||||
else if (!name.equals("<init>")) {
|
||||
generateRegularMethod(name, descriptor, signature, exceptions);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the body of the dynamic dispatcher method. This method is responsible for calling all the methods
|
||||
* that are added to a type after the first time it is defined.
|
||||
*/
|
||||
private void generateDynamicDispatchMethod(String name, String descriptor, String signature, String[] exceptions) {
|
||||
final int indexDispatcherInstance = 0;
|
||||
final int indexArgs = 1;
|
||||
final int indexTarget = 2;
|
||||
final int indexNameAndDescriptor = 3;
|
||||
|
||||
// Should be generating the code for each additional method in
|
||||
// the executor (new version) that wasn't in the original.
|
||||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, name, descriptor, signature, exceptions);
|
||||
mv.visitCode();
|
||||
|
||||
// Entries required here for all methods that exist in the new version but didn't exist in the original version
|
||||
// There should be no entries for catchers
|
||||
|
||||
int maxStack = 0;
|
||||
// Basically generate a long if..else sequence for each method
|
||||
List<MethodMember> methods = new ArrayList<MethodMember>(typeDescriptor.getNewOrChangedMethods());
|
||||
|
||||
// these are added because we may be calling through the dynamic dispatcher if calling from an invokeinterface - the invokeinterface
|
||||
// will call __execute on the interface, which is then implemented by the real class - but it may be that the
|
||||
// actual type implementing the interface already implements that method - if the dispatcher doesn't recognize
|
||||
// it then we may go bang
|
||||
|
||||
// System.out.println("Generating __execute in type " + classname);
|
||||
for (MethodMember m : typeDescriptor.getOriginal().getMethods()) {
|
||||
methods.add(m);
|
||||
}
|
||||
|
||||
for (MethodMember method : methods) {
|
||||
if (MethodMember.isCatcher(method) || MethodMember.isSuperDispatcher(method)) { // for reason above, may also need to consider catchers here - what if an interface is changed to add a toString() method, for example
|
||||
continue;
|
||||
// would the implementation for a catcher call the super catcher?
|
||||
}
|
||||
// System.out.println("Generating handler for " + method.name);
|
||||
String nameWithDescriptor = new StringBuilder(method.name).append(method.descriptor).toString();
|
||||
|
||||
// 2. Load the input name+descriptor and compare it with this method:
|
||||
mv.visitVarInsn(ALOAD, 3);
|
||||
mv.visitLdcInsn(nameWithDescriptor);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
|
||||
Label label = new Label();
|
||||
mv.visitJumpInsn(IFEQ, label); // means if false
|
||||
|
||||
// 3. Generate the code that will call the method on the executor:
|
||||
if (!method.isStatic()) {
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 2);
|
||||
mv.visitTypeInsn(CHECKCAST, classname);
|
||||
}
|
||||
String callDescriptor = method.isStatic() ? method.descriptor : Utils.insertExtraParameter(classname,
|
||||
method.descriptor);
|
||||
|
||||
int pcount = Utils.getParameterCount(method.descriptor);
|
||||
if (pcount > maxStack) {
|
||||
pcount = maxStack;
|
||||
}
|
||||
|
||||
// 4. Unpack parameter array to fit the descriptor for that method
|
||||
Utils.generateInstructionsToUnpackArrayAccordingToDescriptor(mv, method.descriptor, 1);
|
||||
|
||||
ReturnType returnType = Utils.getReturnTypeDescriptor(method.descriptor);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, executorClassName, method.name, callDescriptor, false);
|
||||
if (returnType.isVoid()) {
|
||||
mv.visitInsn(ACONST_NULL);
|
||||
}
|
||||
else if (returnType.isPrimitive()) {
|
||||
Utils.insertBoxInsns(mv, returnType.descriptor);
|
||||
}
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
mv.visitLabel(label);
|
||||
}
|
||||
for (MethodMember ctor : typeDescriptor.getLatestTypeDescriptor().getConstructors()) {
|
||||
String nameWithDescriptor = new StringBuilder(ctor.name).append(ctor.descriptor).toString();
|
||||
|
||||
// 2. Load the input name+descriptor and compare it with this method:
|
||||
// if (nameAndDescriptor.equals(xxx)) {
|
||||
mv.visitVarInsn(ALOAD, indexNameAndDescriptor);
|
||||
mv.visitLdcInsn(nameWithDescriptor);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
|
||||
Label label = new Label();
|
||||
mv.visitJumpInsn(IFEQ, label); // means if false
|
||||
|
||||
// 3. Generate the code that will call the method on the executor:
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 2);
|
||||
mv.visitTypeInsn(CHECKCAST, classname);
|
||||
String callDescriptor = Utils.insertExtraParameter(classname, ctor.descriptor);
|
||||
|
||||
int pcount = Utils.getParameterCount(ctor.descriptor);
|
||||
if (pcount > maxStack) {
|
||||
pcount = maxStack;
|
||||
}
|
||||
|
||||
// 4. Unpack parameter array to fit the descriptor for that method
|
||||
Utils.generateInstructionsToUnpackArrayAccordingToDescriptor(mv, ctor.descriptor, 1);
|
||||
|
||||
// ReturnType returnType = Utils.getReturnTypeDescriptor(method.descriptor);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, executorClassName, "___init___", callDescriptor, false);
|
||||
// if (returnType.isVoid()) {
|
||||
mv.visitInsn(ACONST_NULL);
|
||||
// } else if (returnType.isPrimitive()) {
|
||||
// Utils.insertBoxInsns(mv, returnType.descriptor);
|
||||
// }
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
mv.visitLabel(label);
|
||||
}
|
||||
|
||||
// 5. Throw exception as dynamic dispatcher has been called for something it shouldn't have
|
||||
|
||||
// At this point we failed to find it as a method we can dispatch to our executor, so we want
|
||||
// to pass it 'up' to our supertype. We need to get the dispatcher for our superclass
|
||||
// and then call the __execute() on it, assuming that it will be able to handle this request.
|
||||
|
||||
// alternative 1: use the dispatcher for the superclass
|
||||
|
||||
// Determine the supertype
|
||||
String slashedSupertypeName = rtype.getTypeDescriptor().getSupertypeName();
|
||||
|
||||
// getDispatcher will give us the dispatcher for the supertype
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, slashedSupertypeName, fReloadableTypeFieldName, lReloadableType);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, tReloadableType, "getDispatcher",
|
||||
"()Lorg/springsource/loaded/__DynamicallyDispatchable;", false);
|
||||
|
||||
// alternative 2: find the right dispatcher - i.e. who in the super hierarchy provides that nameAndDescriptor
|
||||
|
||||
// now invoke the dynamic dispatch call on that dispatcher
|
||||
mv.visitVarInsn(ALOAD, indexArgs);
|
||||
mv.visitVarInsn(ALOAD, indexTarget);
|
||||
mv.visitVarInsn(ALOAD, indexNameAndDescriptor);
|
||||
mv.visitMethodInsn(INVOKEINTERFACE, tDynamicallyDispatchable, mDynamicDispatchName,
|
||||
mDynamicDispatchDescriptor, false);
|
||||
mv.visitInsn(ARETURN);
|
||||
|
||||
// mv.visitTypeInsn(NEW, "java/lang/IllegalStateException");
|
||||
// mv.visitInsn(DUP);
|
||||
// mv.visitVarInsn(ALOAD, 3);
|
||||
// mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalStateException", "<init>", "(Ljava/lang/String;)V");
|
||||
// mv.visitInsn(ATHROW);
|
||||
mv.visitMaxs(maxStack, 6);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to generate the implementation of a normal method on the interface - a normal method is one that did
|
||||
* exist when the type was first defined. Might be a catcher.
|
||||
*/
|
||||
private void generateRegularMethod(String name, String descriptor, String signature, String[] exceptions) {
|
||||
// The original descriptor is how it was defined on the original type and how it is defined in the executor class.
|
||||
// The original descriptor is this descriptor with the first parameter trimmed off.
|
||||
boolean isClinit = name.equals("___clinit___");
|
||||
String originalDescriptor = isClinit ? descriptor : Utils.stripFirstParameter(descriptor);
|
||||
MethodMember method = null;
|
||||
|
||||
// Detect if the name has been modified for clash avoidance reasons
|
||||
if (name.equals("___init___")) {
|
||||
// it is a ctor
|
||||
method = rtype.getConstructor(originalDescriptor);
|
||||
}
|
||||
else {
|
||||
if (isClinit) {
|
||||
generateClinitDispatcher();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// TODO need a better solution that these __
|
||||
if (name.startsWith("__") && !name.equals("__$swapInit")) { // __$swapInit is the groovy reset method
|
||||
// clash avoidance name
|
||||
method = rtype.getMethod(name.substring(2), originalDescriptor);
|
||||
}
|
||||
else {
|
||||
method = rtype.getMethod(name, originalDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean isStatic = method.isStatic();
|
||||
|
||||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, name, descriptor, signature,
|
||||
exceptions);
|
||||
mv.visitCode();
|
||||
// The input descriptor will include the extra initial parameter (the instance, or null for static methods)
|
||||
ReturnType returnTypeDescriptor = Utils.getReturnTypeDescriptor(descriptor);
|
||||
// For a static method the first parameter can be ignored
|
||||
int params = Utils.getParameterCount(descriptor);
|
||||
String callDescriptor = isStatic ? originalDescriptor : descriptor;
|
||||
Utils.createLoadsBasedOnDescriptor(mv, callDescriptor, isStatic ? 2 : 1);
|
||||
mv.visitMethodInsn(INVOKESTATIC, executorClassName, name, callDescriptor, false);
|
||||
Utils.addCorrectReturnInstruction(mv, returnTypeDescriptor, false);
|
||||
mv.visitMaxs(params, params + 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
public void visitOuterClass(String arg0, String arg1, String arg2) {
|
||||
}
|
||||
|
||||
public void visitSource(String arg0, String arg1) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.springsource.loaded.Utils.ReturnType;
|
||||
|
||||
|
||||
/**
|
||||
* Builder that creates the dispatcher. The dispatcher is the implementation of the interface extracted for a type which
|
||||
* then delegates to the executor. A new dispatcher (and executor) is built for each class reload.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class DispatcherBuilder {
|
||||
|
||||
/**
|
||||
* Factory method that builds the dispatcher for a specified reloadabletype.
|
||||
*
|
||||
* @param rtype the reloadable type
|
||||
* @param newVersionTypeDescriptor the descriptor of the new version (the executor will be generated according to
|
||||
* this)
|
||||
* @param versionstamp the suffix that should be appended to the generated dispatcher
|
||||
* @return the bytecode for the new dispatcher
|
||||
*/
|
||||
public static byte[] createFor(ReloadableType rtype, IncrementalTypeDescriptor newVersionTypeDescriptor,
|
||||
String versionstamp) {
|
||||
ClassReader fileReader = new ClassReader(rtype.interfaceBytes);
|
||||
DispatcherBuilderVisitor dispatcherVisitor = new DispatcherBuilderVisitor(rtype, newVersionTypeDescriptor,
|
||||
versionstamp);
|
||||
fileReader.accept(dispatcherVisitor, 0);
|
||||
return dispatcherVisitor.getBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whilst visiting the interface, the implementation is created.
|
||||
*/
|
||||
static class DispatcherBuilderVisitor extends ClassVisitor implements Opcodes, Constants {
|
||||
|
||||
private ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
|
||||
private String classname;
|
||||
|
||||
private String executorClassName;
|
||||
|
||||
private String suffix;
|
||||
|
||||
private ReloadableType rtype;
|
||||
|
||||
private IncrementalTypeDescriptor typeDescriptor;
|
||||
|
||||
public DispatcherBuilderVisitor(ReloadableType rtype, IncrementalTypeDescriptor typeDescriptor, String suffix) {
|
||||
super(ASM5);
|
||||
this.classname = rtype.getSlashedName();
|
||||
this.typeDescriptor = typeDescriptor;
|
||||
this.suffix = suffix;
|
||||
this.rtype = rtype;
|
||||
this.executorClassName = Utils.getExecutorName(classname, suffix);
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
public void visit(int version, int flags, String name, String signature, String superclassName,
|
||||
String[] interfaceNames) {
|
||||
String dispatcherName = Utils.getDispatcherName(classname, suffix);
|
||||
cw.visit(version, Opcodes.ACC_PUBLIC, dispatcherName, null, "java/lang/Object",
|
||||
new String[] { Utils.getInterfaceName(classname),
|
||||
"org/springsource/loaded/__DynamicallyDispatchable" });
|
||||
generateDefaultConstructor();
|
||||
}
|
||||
|
||||
private void generateDefaultConstructor() {
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void generateClinitDispatcher() {
|
||||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, mStaticInitializerName, "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitMethodInsn(INVOKESTATIC, executorClassName, mStaticInitializerName, "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String arg0, boolean arg1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitAttribute(Attribute arg0) {
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
}
|
||||
|
||||
public FieldVisitor visitField(int arg0, String arg1, String arg2, String arg3, Object arg4) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitInnerClass(String arg0, String arg1, String arg2, int arg3) {
|
||||
}
|
||||
|
||||
public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
|
||||
String[] exceptions) {
|
||||
if (name.equals(mDynamicDispatchName)) {
|
||||
generateDynamicDispatchMethod(name, descriptor, signature, exceptions);
|
||||
}
|
||||
else if (!name.equals("<init>")) {
|
||||
generateRegularMethod(name, descriptor, signature, exceptions);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the body of the dynamic dispatcher method. This method is responsible for calling all the methods
|
||||
* that are added to a type after the first time it is defined.
|
||||
*/
|
||||
private void generateDynamicDispatchMethod(String name, String descriptor, String signature, String[] exceptions) {
|
||||
final int indexDispatcherInstance = 0;
|
||||
final int indexArgs = 1;
|
||||
final int indexTarget = 2;
|
||||
final int indexNameAndDescriptor = 3;
|
||||
|
||||
// Should be generating the code for each additional method in
|
||||
// the executor (new version) that wasn't in the original.
|
||||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, name, descriptor, signature, exceptions);
|
||||
mv.visitCode();
|
||||
|
||||
// Entries required here for all methods that exist in the new version but didn't exist in the original version
|
||||
// There should be no entries for catchers
|
||||
|
||||
int maxStack = 0;
|
||||
// Basically generate a long if..else sequence for each method
|
||||
List<MethodMember> methods = new ArrayList<MethodMember>(typeDescriptor.getNewOrChangedMethods());
|
||||
|
||||
// these are added because we may be calling through the dynamic dispatcher if calling from an invokeinterface - the invokeinterface
|
||||
// will call __execute on the interface, which is then implemented by the real class - but it may be that the
|
||||
// actual type implementing the interface already implements that method - if the dispatcher doesn't recognize
|
||||
// it then we may go bang
|
||||
|
||||
// System.out.println("Generating __execute in type " + classname);
|
||||
for (MethodMember m : typeDescriptor.getOriginal().getMethods()) {
|
||||
methods.add(m);
|
||||
}
|
||||
|
||||
for (MethodMember method : methods) {
|
||||
if (MethodMember.isCatcher(method) || MethodMember.isSuperDispatcher(method)) { // for reason above, may also need to consider catchers here - what if an interface is changed to add a toString() method, for example
|
||||
continue;
|
||||
// would the implementation for a catcher call the super catcher?
|
||||
}
|
||||
// System.out.println("Generating handler for " + method.name);
|
||||
String nameWithDescriptor = new StringBuilder(method.name).append(method.descriptor).toString();
|
||||
|
||||
// 2. Load the input name+descriptor and compare it with this method:
|
||||
mv.visitVarInsn(ALOAD, 3);
|
||||
mv.visitLdcInsn(nameWithDescriptor);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
|
||||
Label label = new Label();
|
||||
mv.visitJumpInsn(IFEQ, label); // means if false
|
||||
|
||||
// 3. Generate the code that will call the method on the executor:
|
||||
if (!method.isStatic()) {
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 2);
|
||||
mv.visitTypeInsn(CHECKCAST, classname);
|
||||
}
|
||||
String callDescriptor = method.isStatic() ? method.descriptor : Utils.insertExtraParameter(classname,
|
||||
method.descriptor);
|
||||
|
||||
int pcount = Utils.getParameterCount(method.descriptor);
|
||||
if (pcount > maxStack) {
|
||||
pcount = maxStack;
|
||||
}
|
||||
|
||||
// 4. Unpack parameter array to fit the descriptor for that method
|
||||
Utils.generateInstructionsToUnpackArrayAccordingToDescriptor(mv, method.descriptor, 1);
|
||||
|
||||
ReturnType returnType = Utils.getReturnTypeDescriptor(method.descriptor);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, executorClassName, method.name, callDescriptor, false);
|
||||
if (returnType.isVoid()) {
|
||||
mv.visitInsn(ACONST_NULL);
|
||||
}
|
||||
else if (returnType.isPrimitive()) {
|
||||
Utils.insertBoxInsns(mv, returnType.descriptor);
|
||||
}
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
mv.visitLabel(label);
|
||||
}
|
||||
for (MethodMember ctor : typeDescriptor.getLatestTypeDescriptor().getConstructors()) {
|
||||
String nameWithDescriptor = new StringBuilder(ctor.name).append(ctor.descriptor).toString();
|
||||
|
||||
// 2. Load the input name+descriptor and compare it with this method:
|
||||
// if (nameAndDescriptor.equals(xxx)) {
|
||||
mv.visitVarInsn(ALOAD, indexNameAndDescriptor);
|
||||
mv.visitLdcInsn(nameWithDescriptor);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
|
||||
Label label = new Label();
|
||||
mv.visitJumpInsn(IFEQ, label); // means if false
|
||||
|
||||
// 3. Generate the code that will call the method on the executor:
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 2);
|
||||
mv.visitTypeInsn(CHECKCAST, classname);
|
||||
String callDescriptor = Utils.insertExtraParameter(classname, ctor.descriptor);
|
||||
|
||||
int pcount = Utils.getParameterCount(ctor.descriptor);
|
||||
if (pcount > maxStack) {
|
||||
pcount = maxStack;
|
||||
}
|
||||
|
||||
// 4. Unpack parameter array to fit the descriptor for that method
|
||||
Utils.generateInstructionsToUnpackArrayAccordingToDescriptor(mv, ctor.descriptor, 1);
|
||||
|
||||
// ReturnType returnType = Utils.getReturnTypeDescriptor(method.descriptor);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, executorClassName, "___init___", callDescriptor, false);
|
||||
// if (returnType.isVoid()) {
|
||||
mv.visitInsn(ACONST_NULL);
|
||||
// } else if (returnType.isPrimitive()) {
|
||||
// Utils.insertBoxInsns(mv, returnType.descriptor);
|
||||
// }
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
mv.visitLabel(label);
|
||||
}
|
||||
|
||||
// 5. Throw exception as dynamic dispatcher has been called for something it shouldn't have
|
||||
|
||||
// At this point we failed to find it as a method we can dispatch to our executor, so we want
|
||||
// to pass it 'up' to our supertype. We need to get the dispatcher for our superclass
|
||||
// and then call the __execute() on it, assuming that it will be able to handle this request.
|
||||
|
||||
// alternative 1: use the dispatcher for the superclass
|
||||
|
||||
// Determine the supertype
|
||||
String slashedSupertypeName = rtype.getTypeDescriptor().getSupertypeName();
|
||||
|
||||
// getDispatcher will give us the dispatcher for the supertype
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, slashedSupertypeName, fReloadableTypeFieldName, lReloadableType);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, tReloadableType, "getDispatcher",
|
||||
"()Lorg/springsource/loaded/__DynamicallyDispatchable;", false);
|
||||
|
||||
// alternative 2: find the right dispatcher - i.e. who in the super hierarchy provides that nameAndDescriptor
|
||||
|
||||
// now invoke the dynamic dispatch call on that dispatcher
|
||||
mv.visitVarInsn(ALOAD, indexArgs);
|
||||
mv.visitVarInsn(ALOAD, indexTarget);
|
||||
mv.visitVarInsn(ALOAD, indexNameAndDescriptor);
|
||||
mv.visitMethodInsn(INVOKEINTERFACE, tDynamicallyDispatchable, mDynamicDispatchName,
|
||||
mDynamicDispatchDescriptor, false);
|
||||
mv.visitInsn(ARETURN);
|
||||
|
||||
// mv.visitTypeInsn(NEW, "java/lang/IllegalStateException");
|
||||
// mv.visitInsn(DUP);
|
||||
// mv.visitVarInsn(ALOAD, 3);
|
||||
// mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalStateException", "<init>", "(Ljava/lang/String;)V");
|
||||
// mv.visitInsn(ATHROW);
|
||||
mv.visitMaxs(maxStack, 6);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to generate the implementation of a normal method on the interface - a normal method is one that did
|
||||
* exist when the type was first defined. Might be a catcher.
|
||||
*/
|
||||
private void generateRegularMethod(String name, String descriptor, String signature, String[] exceptions) {
|
||||
// The original descriptor is how it was defined on the original type and how it is defined in the executor class.
|
||||
// The original descriptor is this descriptor with the first parameter trimmed off.
|
||||
boolean isClinit = name.equals("___clinit___");
|
||||
String originalDescriptor = isClinit ? descriptor : Utils.stripFirstParameter(descriptor);
|
||||
MethodMember method = null;
|
||||
|
||||
// Detect if the name has been modified for clash avoidance reasons
|
||||
if (name.equals("___init___")) {
|
||||
// it is a ctor
|
||||
method = rtype.getConstructor(originalDescriptor);
|
||||
}
|
||||
else {
|
||||
if (isClinit) {
|
||||
generateClinitDispatcher();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// TODO need a better solution that these __
|
||||
if (name.startsWith("__") && !name.equals("__$swapInit")) { // __$swapInit is the groovy reset method
|
||||
// clash avoidance name
|
||||
method = rtype.getMethod(name.substring(2), originalDescriptor);
|
||||
}
|
||||
else {
|
||||
method = rtype.getMethod(name, originalDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean isStatic = method.isStatic();
|
||||
|
||||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, name, descriptor, signature,
|
||||
exceptions);
|
||||
mv.visitCode();
|
||||
// The input descriptor will include the extra initial parameter (the instance, or null for static methods)
|
||||
ReturnType returnTypeDescriptor = Utils.getReturnTypeDescriptor(descriptor);
|
||||
// For a static method the first parameter can be ignored
|
||||
int params = Utils.getParameterCount(descriptor);
|
||||
String callDescriptor = isStatic ? originalDescriptor : descriptor;
|
||||
Utils.createLoadsBasedOnDescriptor(mv, callDescriptor, isStatic ? 2 : 1);
|
||||
mv.visitMethodInsn(INVOKESTATIC, executorClassName, name, callDescriptor, false);
|
||||
Utils.addCorrectReturnInstruction(mv, returnTypeDescriptor, false);
|
||||
mv.visitMaxs(params, params + 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
public void visitOuterClass(String arg0, String arg1, String arg2) {
|
||||
}
|
||||
|
||||
public void visitSource(String arg0, String arg1) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,44 +1,44 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Represents an exact type pattern.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class ExactTypePattern extends TypePattern {
|
||||
|
||||
private String pattern;
|
||||
|
||||
/**
|
||||
* @param pattern type pattern of the form com.foo.Bar
|
||||
*/
|
||||
public ExactTypePattern(String pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
protected boolean internalMatches(String input) {
|
||||
boolean b = input.equals(pattern);
|
||||
return b;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "text:" + pattern;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Represents an exact type pattern.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class ExactTypePattern extends TypePattern {
|
||||
|
||||
private String pattern;
|
||||
|
||||
/**
|
||||
* @param pattern type pattern of the form com.foo.Bar
|
||||
*/
|
||||
public ExactTypePattern(String pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
protected boolean internalMatches(String input) {
|
||||
boolean b = input.equals(pattern);
|
||||
return b;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "text:" + pattern;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Call back interface for the FileSystemWatcher.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public interface FileChangeListener {
|
||||
|
||||
void fileChanged(File file);
|
||||
|
||||
void register(ReloadableType rtype, File file);
|
||||
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Call back interface for the FileSystemWatcher.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public interface FileChangeListener {
|
||||
|
||||
void fileChanged(File file);
|
||||
|
||||
void register(ReloadableType rtype, File file);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,188 +1,188 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
/**
|
||||
* Extract an interface for a type. The interface embodies the shape of the type as originally loaded. The key
|
||||
* difference with methods in the interface is that they contain an extra (leading) parameter that is the type of the
|
||||
* original loaded class.<br>
|
||||
* For example:<br>
|
||||
*
|
||||
* <pre>
|
||||
* <tt>
|
||||
* class Foo {
|
||||
* public String foo(int i) {}
|
||||
* }
|
||||
* </tt>
|
||||
* </pre>
|
||||
*
|
||||
* will cause creation of an interface method:
|
||||
*
|
||||
* <pre>
|
||||
* <tt>
|
||||
* String foo(Foo instance, int i) {}
|
||||
* </tt>
|
||||
* </pre>
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class InterfaceExtractor {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private TypeRegistry registry;
|
||||
|
||||
public InterfaceExtractor(TypeRegistry registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the fixed interface for a class and a type descriptor with more details on the methods.
|
||||
*
|
||||
* @param classbytes bytes for the class which is going through interface extraction
|
||||
* @param registry type registry related to the classloader for this class
|
||||
* @param typeDescriptor previously extracted type descriptor for the class
|
||||
* @return class bytes for extracted interface
|
||||
*/
|
||||
public static byte[] extract(byte[] classbytes, TypeRegistry registry, TypeDescriptor typeDescriptor) {
|
||||
return new InterfaceExtractor(registry).extract(classbytes, typeDescriptor);
|
||||
}
|
||||
|
||||
public byte[] extract(byte[] classbytes, TypeDescriptor typeDescriptor) {
|
||||
ClassReader fileReader = new ClassReader(classbytes);
|
||||
ExtractorVisitor extractorVisitor = new ExtractorVisitor(typeDescriptor);
|
||||
fileReader.accept(extractorVisitor, 0);
|
||||
return extractorVisitor.getBytes();
|
||||
}
|
||||
|
||||
class ExtractorVisitor extends ClassVisitor implements Constants {
|
||||
|
||||
private TypeDescriptor typeDescriptor;
|
||||
|
||||
private ClassWriter interfaceWriter = new ClassWriter(0);
|
||||
|
||||
private String slashedtypename;
|
||||
|
||||
public ExtractorVisitor(TypeDescriptor typeDescriptor) {
|
||||
super(ASM5);
|
||||
this.typeDescriptor = typeDescriptor;
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return interfaceWriter.toByteArray();
|
||||
}
|
||||
|
||||
public void visit(int version, int flags, String name, String signature, String superclassName,
|
||||
String[] interfaceNames) {
|
||||
// Create interface "public interface [typename]__I {"
|
||||
interfaceWriter.visit(version, ACC_PUBLIC_INTERFACE, Utils.getInterfaceName(name), null,
|
||||
"java/lang/Object", null);
|
||||
this.slashedtypename = name;
|
||||
}
|
||||
|
||||
public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
|
||||
String[] exceptions) {
|
||||
// TODO should we special case statics (and not have them require an extra leading param)?
|
||||
if (isClinitOrInit(name)) {
|
||||
if (name.charAt(1) != 'c') { // avoid <clinit>
|
||||
// It is a constructor
|
||||
String newDescriptor = createDescriptorWithPrefixedParameter(descriptor);
|
||||
// Need a modified name
|
||||
name = "___init___";
|
||||
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, name, newDescriptor, signature, exceptions);
|
||||
}
|
||||
}
|
||||
else {
|
||||
String newDescriptor = createDescriptorWithPrefixedParameter(descriptor);
|
||||
// generic signature is erased
|
||||
MethodMember method = typeDescriptor.getByDescriptor(name, descriptor);
|
||||
if (MethodMember.isClash(method)) {
|
||||
name = "__" + name;
|
||||
}
|
||||
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, name, newDescriptor, null, exceptions);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
public void visitOuterClass(String owner, String name, String desc) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
public void visitSource(String source, String debug) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
public void visitAttribute(Attribute attr) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
// Must add a method on the interface for the dynamic invocation method
|
||||
String descriptor = mDynamicDispatchDescriptor;
|
||||
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, mDynamicDispatchName, descriptor, null, null);
|
||||
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, mStaticInitializerName, "()V", null, null);
|
||||
// Go through catchers on the type descriptor and add the methods to the interface
|
||||
for (MethodMember method : typeDescriptor.getMethods()) {
|
||||
if (!MethodMember.isCatcher(method)) {
|
||||
continue;
|
||||
}
|
||||
descriptor = createDescriptorWithPrefixedParameter(method.getDescriptor());
|
||||
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, method.getName(), descriptor, null,
|
||||
method.getExceptions());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the descriptor to include a leading parameter of the type of the class being visited. For example: if
|
||||
* visiting type "com.Bar" and hit method "(Ljava/lang/String;)V" then this method will return
|
||||
* "(Lcom/Bar;Ljava/lang/String;)V"
|
||||
*
|
||||
* @return new descriptor with extra leading parameter
|
||||
*/
|
||||
private String createDescriptorWithPrefixedParameter(String descriptor) {
|
||||
StringBuilder newDescriptor = new StringBuilder();
|
||||
newDescriptor.append("(L").append(slashedtypename).append(";");
|
||||
newDescriptor.append(descriptor, 1, descriptor.length());
|
||||
return newDescriptor.toString();
|
||||
}
|
||||
|
||||
private boolean isClinitOrInit(String name) {
|
||||
return name.charAt(0) == '<';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
/**
|
||||
* Extract an interface for a type. The interface embodies the shape of the type as originally loaded. The key
|
||||
* difference with methods in the interface is that they contain an extra (leading) parameter that is the type of the
|
||||
* original loaded class.<br>
|
||||
* For example:<br>
|
||||
*
|
||||
* <pre>
|
||||
* <tt>
|
||||
* class Foo {
|
||||
* public String foo(int i) {}
|
||||
* }
|
||||
* </tt>
|
||||
* </pre>
|
||||
*
|
||||
* will cause creation of an interface method:
|
||||
*
|
||||
* <pre>
|
||||
* <tt>
|
||||
* String foo(Foo instance, int i) {}
|
||||
* </tt>
|
||||
* </pre>
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class InterfaceExtractor {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private TypeRegistry registry;
|
||||
|
||||
public InterfaceExtractor(TypeRegistry registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the fixed interface for a class and a type descriptor with more details on the methods.
|
||||
*
|
||||
* @param classbytes bytes for the class which is going through interface extraction
|
||||
* @param registry type registry related to the classloader for this class
|
||||
* @param typeDescriptor previously extracted type descriptor for the class
|
||||
* @return class bytes for extracted interface
|
||||
*/
|
||||
public static byte[] extract(byte[] classbytes, TypeRegistry registry, TypeDescriptor typeDescriptor) {
|
||||
return new InterfaceExtractor(registry).extract(classbytes, typeDescriptor);
|
||||
}
|
||||
|
||||
public byte[] extract(byte[] classbytes, TypeDescriptor typeDescriptor) {
|
||||
ClassReader fileReader = new ClassReader(classbytes);
|
||||
ExtractorVisitor extractorVisitor = new ExtractorVisitor(typeDescriptor);
|
||||
fileReader.accept(extractorVisitor, 0);
|
||||
return extractorVisitor.getBytes();
|
||||
}
|
||||
|
||||
class ExtractorVisitor extends ClassVisitor implements Constants {
|
||||
|
||||
private TypeDescriptor typeDescriptor;
|
||||
|
||||
private ClassWriter interfaceWriter = new ClassWriter(0);
|
||||
|
||||
private String slashedtypename;
|
||||
|
||||
public ExtractorVisitor(TypeDescriptor typeDescriptor) {
|
||||
super(ASM5);
|
||||
this.typeDescriptor = typeDescriptor;
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return interfaceWriter.toByteArray();
|
||||
}
|
||||
|
||||
public void visit(int version, int flags, String name, String signature, String superclassName,
|
||||
String[] interfaceNames) {
|
||||
// Create interface "public interface [typename]__I {"
|
||||
interfaceWriter.visit(version, ACC_PUBLIC_INTERFACE, Utils.getInterfaceName(name), null,
|
||||
"java/lang/Object", null);
|
||||
this.slashedtypename = name;
|
||||
}
|
||||
|
||||
public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
|
||||
String[] exceptions) {
|
||||
// TODO should we special case statics (and not have them require an extra leading param)?
|
||||
if (isClinitOrInit(name)) {
|
||||
if (name.charAt(1) != 'c') { // avoid <clinit>
|
||||
// It is a constructor
|
||||
String newDescriptor = createDescriptorWithPrefixedParameter(descriptor);
|
||||
// Need a modified name
|
||||
name = "___init___";
|
||||
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, name, newDescriptor, signature, exceptions);
|
||||
}
|
||||
}
|
||||
else {
|
||||
String newDescriptor = createDescriptorWithPrefixedParameter(descriptor);
|
||||
// generic signature is erased
|
||||
MethodMember method = typeDescriptor.getByDescriptor(name, descriptor);
|
||||
if (MethodMember.isClash(method)) {
|
||||
name = "__" + name;
|
||||
}
|
||||
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, name, newDescriptor, null, exceptions);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
public void visitOuterClass(String owner, String name, String desc) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
public void visitSource(String source, String debug) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
public void visitAttribute(Attribute attr) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
// Must add a method on the interface for the dynamic invocation method
|
||||
String descriptor = mDynamicDispatchDescriptor;
|
||||
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, mDynamicDispatchName, descriptor, null, null);
|
||||
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, mStaticInitializerName, "()V", null, null);
|
||||
// Go through catchers on the type descriptor and add the methods to the interface
|
||||
for (MethodMember method : typeDescriptor.getMethods()) {
|
||||
if (!MethodMember.isCatcher(method)) {
|
||||
continue;
|
||||
}
|
||||
descriptor = createDescriptorWithPrefixedParameter(method.getDescriptor());
|
||||
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, method.getName(), descriptor, null,
|
||||
method.getExceptions());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the descriptor to include a leading parameter of the type of the class being visited. For example: if
|
||||
* visiting type "com.Bar" and hit method "(Ljava/lang/String;)V" then this method will return
|
||||
* "(Lcom/Bar;Ljava/lang/String;)V"
|
||||
*
|
||||
* @return new descriptor with extra leading parameter
|
||||
*/
|
||||
private String createDescriptorWithPrefixedParameter(String descriptor) {
|
||||
StringBuilder newDescriptor = new StringBuilder();
|
||||
newDescriptor.append("(L").append(slashedtypename).append(";");
|
||||
newDescriptor.append(descriptor, 1, descriptor.length());
|
||||
return newDescriptor.toString();
|
||||
}
|
||||
|
||||
private boolean isClinitOrInit(String name) {
|
||||
return name.charAt(0) == '<';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Represents a double dotted type pattern. For example: com.foo.bar..* - this has the same meaning as in AspectJ.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class PrefixTypePattern extends TypePattern {
|
||||
|
||||
private String pattern;
|
||||
|
||||
/**
|
||||
* @param pattern the prefix pattern of the form 'com.foo.bar..*'
|
||||
*/
|
||||
public PrefixTypePattern(String pattern) {
|
||||
this.pattern = pattern.substring(0, pattern.length() - 2); // chop off the '.*'
|
||||
}
|
||||
|
||||
protected boolean internalMatches(String input) {
|
||||
boolean b = input.startsWith(pattern);
|
||||
return b;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "text:" + pattern + ".*";
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Represents a double dotted type pattern. For example: com.foo.bar..* - this has the same meaning as in AspectJ.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class PrefixTypePattern extends TypePattern {
|
||||
|
||||
private String pattern;
|
||||
|
||||
/**
|
||||
* @param pattern the prefix pattern of the form 'com.foo.bar..*'
|
||||
*/
|
||||
public PrefixTypePattern(String pattern) {
|
||||
this.pattern = pattern.substring(0, pattern.length() - 2); // chop off the '.*'
|
||||
}
|
||||
|
||||
protected boolean internalMatches(String input) {
|
||||
boolean b = input.startsWith(pattern);
|
||||
return b;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "text:" + pattern + ".*";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ReloadException extends RuntimeException {
|
||||
|
||||
public ReloadException(String message, Exception cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ReloadException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ReloadException extends RuntimeException {
|
||||
|
||||
public ReloadException(String message, Exception cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ReloadException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,357 +1,357 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Encapsulates the information about a type relevant to reloading. The TypeDescriptor for a type is sometimes extracted
|
||||
* whilst performing some other operation (eg. {@link InterfaceExtractor}) but can also be retrieved directly using
|
||||
* {@link TypeDescriptorExtractor}.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class TypeDescriptor implements Constants {
|
||||
|
||||
private final int modifiers;
|
||||
|
||||
final String typename; // slashed
|
||||
|
||||
final String supertypeName; // slashed
|
||||
|
||||
final String[] superinterfaceNames; // slashed // empty array if there are none
|
||||
|
||||
private final MethodMember[] constructors; // empty array if there are none (but this doesn't ever happen!)
|
||||
|
||||
private final MethodMember[] methods; // empty array if there are none
|
||||
|
||||
private final MethodMember[] nonprivateMethods; // empty array if there are none
|
||||
|
||||
private final FieldMember[] fields; // empty array if there are none
|
||||
|
||||
private final FieldMember[] fieldsRequiringAccessors; // empty array if there are none
|
||||
|
||||
private List<String> finalInHierarchy; // nameAndDescriptor strings for methods final in the hierarchy (e.g. ordinal()I for an enum)
|
||||
|
||||
private final TypeRegistry registry;
|
||||
|
||||
private final boolean isReloadable;
|
||||
|
||||
private final boolean hasClinit;
|
||||
|
||||
private final static int IS_GROOVY_TYPE = 0x0001;
|
||||
|
||||
private int bits = 0x0000;
|
||||
|
||||
private ReloadableType reloadableType;
|
||||
|
||||
private int nextId = 0;
|
||||
|
||||
public TypeDescriptor(String slashedTypeName, String supertypeName, String[] superinterfaceNames, int modifiers,
|
||||
List<? extends MethodMember> constructors, List<MethodMember> methods, List<? extends FieldMember> fields,
|
||||
List<? extends FieldMember> fieldsRequiringAccessors, boolean isReloadable, TypeRegistry registry,
|
||||
boolean hasClinit,
|
||||
List<String> finalInHierarchy) {
|
||||
this.typename = slashedTypeName;
|
||||
this.supertypeName = supertypeName;
|
||||
this.superinterfaceNames = (superinterfaceNames == null ? NO_STRINGS : superinterfaceNames);
|
||||
this.finalInHierarchy = finalInHierarchy;
|
||||
this.modifiers = modifiers;
|
||||
this.fields = fields.size() == 0 ? FieldMember.NONE : fields.toArray(new FieldMember[fields.size()]);
|
||||
this.fieldsRequiringAccessors = fieldsRequiringAccessors.size() == 0 ? FieldMember.NONE
|
||||
: fieldsRequiringAccessors
|
||||
.toArray(new FieldMember[fieldsRequiringAccessors.size()]);
|
||||
this.constructors = constructors.size() == 0 ? MethodMember.NONE
|
||||
: constructors.toArray(new MethodMember[constructors
|
||||
.size()]);
|
||||
this.methods = methods.size() == 0 ? MethodMember.NONE : methods.toArray(new MethodMember[methods.size()]);
|
||||
this.nonprivateMethods = filterNonPrivateMethods(this.methods);
|
||||
this.isReloadable = isReloadable;
|
||||
this.registry = registry;
|
||||
this.hasClinit = hasClinit;
|
||||
allocateIds();
|
||||
}
|
||||
|
||||
private static MethodMember[] filterNonPrivateMethods(MethodMember[] allMethods) {
|
||||
List<MethodMember> result = null;
|
||||
for (MethodMember method : allMethods) {
|
||||
if (!method.isPrivate()) {
|
||||
if (result == null) {
|
||||
result = new ArrayList<MethodMember>();
|
||||
}
|
||||
result.add(method);
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
return MethodMember.NONE;
|
||||
}
|
||||
else {
|
||||
return result.toArray(new MethodMember[result.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
private void allocateIds() {
|
||||
// Give the methods awareness of their index
|
||||
for (MethodMember method : methods) {
|
||||
method.setId(nextId++);
|
||||
}
|
||||
}
|
||||
|
||||
public MethodMember[] getMethods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
public MethodMember[] getConstructors() {
|
||||
return constructors;
|
||||
}
|
||||
|
||||
public FieldMember[] getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
public FieldMember[] getFieldsRequiringAccessors() {
|
||||
return fieldsRequiringAccessors;
|
||||
}
|
||||
|
||||
public int getModifiers() {
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the (slashed) type name
|
||||
*/
|
||||
public String getName() {
|
||||
return typename;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the (slashed) supertype name
|
||||
*/
|
||||
public String getSupertypeName() {
|
||||
return supertypeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of (slashed) superinterface names (or an empty array if none)
|
||||
*/
|
||||
public String[] getSuperinterfacesName() {
|
||||
return superinterfaceNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this descriptor defines the specified method. A strict check on all aspects of the method -
|
||||
* names/exceptions/flags, etc.
|
||||
*
|
||||
* @param method the method to check the existence of in this type descriptor
|
||||
* @return true if this descriptor defines the specified method
|
||||
*/
|
||||
public boolean defines(MethodMember method) {
|
||||
for (MethodMember existingMethod : methods) {
|
||||
// make sure it *really* defines it (i.e. it is not a catcher)
|
||||
if (!MethodMember.isCatcher(existingMethod) && existingMethod.equals(method)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this descriptor defines a method with the specified name and descriptor. Return the method if it is
|
||||
* found. Modifiers, generic signature and exceptions are ignored in this search.
|
||||
*
|
||||
* @param name the member name
|
||||
* @param descriptor the member descriptor (e.g. (Ljava/lang/String;)I)
|
||||
* @return the MethodMember if there is one
|
||||
*/
|
||||
public MethodMember getByDescriptor(String name, String descriptor) {
|
||||
for (MethodMember existingMethod : methods) {
|
||||
if (existingMethod.getName().equals(name) && existingMethod.getDescriptor().equals(descriptor)) {
|
||||
return existingMethod;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public MethodMember getByNameAndDescriptor(String nameAndDescriptor) {
|
||||
for (MethodMember existingMethod : methods) {
|
||||
if (nameAndDescriptor.startsWith(existingMethod.getName())
|
||||
&& nameAndDescriptor.endsWith(existingMethod.getDescriptor())) {
|
||||
return existingMethod;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this type descriptor has been created for a reloadable type
|
||||
*/
|
||||
public boolean isReloadable() {
|
||||
return isReloadable;
|
||||
}
|
||||
|
||||
public MethodMember getMethod(int methodId) {
|
||||
// Should never be an AIOOBE if the woven code is behaving
|
||||
return methods[methodId];
|
||||
}
|
||||
|
||||
public MethodMember getConstructor(int ctorId) {
|
||||
// Should never be an AIOOBE if the woven code is behaving
|
||||
return constructors[ctorId];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the type is an interface
|
||||
*/
|
||||
public boolean isInterface() {
|
||||
return (modifiers & ACC_INTERFACE) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the type is an annotation
|
||||
*/
|
||||
public boolean isAnnotation() {
|
||||
return (modifiers & ACC_ANNOTATION) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the type is an enum
|
||||
*/
|
||||
public boolean isEnum() {
|
||||
return (modifiers & ACC_ENUM) != 0;
|
||||
}
|
||||
|
||||
public boolean definesNonPrivate(String nameAndDescriptor) {
|
||||
for (MethodMember existingMethod : nonprivateMethods) {
|
||||
if (existingMethod.nameAndDescriptor.equals(nameAndDescriptor)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isFinalInHierarchy(String nad) {
|
||||
return finalInHierarchy.contains(nad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a field on this type descriptor - do not try supertypes. This lookup does not differentiate between
|
||||
* static/instance fields.
|
||||
*
|
||||
* @param name the name of the field
|
||||
* @return a FieldMember if the field is found, otherwise null
|
||||
*/
|
||||
public FieldMember getField(String name) {
|
||||
for (FieldMember field : fields) {
|
||||
if (field.getName().equals(name)) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ReloadableType getReloadableType() {
|
||||
if (!isReloadable) {
|
||||
return null;
|
||||
}
|
||||
if (reloadableType == null) {
|
||||
reloadableType = registry.getReloadableType(this.typename);
|
||||
if (reloadableType == null) {
|
||||
throw new IllegalStateException("There is no ReloadableType instance for " + typename);
|
||||
}
|
||||
}
|
||||
return reloadableType;
|
||||
}
|
||||
|
||||
public TypeRegistry getTypeRegistry() {
|
||||
return registry;
|
||||
}
|
||||
|
||||
// could be worth caching if used for more than error messages...
|
||||
public String getDottedName() {
|
||||
return getName().replace('/', '.');
|
||||
}
|
||||
|
||||
public MethodMember getConstructor(String desc) {
|
||||
for (MethodMember ctor : constructors) {
|
||||
String d = ctor.getDescriptor();
|
||||
if (d.equals(desc)) {
|
||||
return ctor;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isGroovyType() {
|
||||
return (bits & IS_GROOVY_TYPE) != 0;
|
||||
}
|
||||
|
||||
public void setIsGroovyType(boolean b) {
|
||||
bits |= IS_GROOVY_TYPE;
|
||||
}
|
||||
|
||||
public boolean hasClinit() {
|
||||
return hasClinit;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append("TypeDescriptor: name=" + typename + " superclass=" + supertypeName + " superinterfaces="
|
||||
+ interfacesToString());
|
||||
s.append(" flags=0x" + Integer.toHexString(modifiers).toUpperCase()).append("\n");
|
||||
s.append("Fields: #" + fields.length + "\n" + fieldsToString());
|
||||
s.append("Constructors:#" + constructors.length + "\n" + methodsToString(constructors));
|
||||
s.append("Methods:#" + methods.length + "\n" + methodsToString(methods));
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
private String fieldsToString() {
|
||||
StringBuilder s = new StringBuilder();
|
||||
int count = 0;
|
||||
for (FieldMember field : fields) {
|
||||
s.append(" field #" + Utils.toPaddedNumber((count++), 3)).append(' ').append(field.toString()).append('\n');
|
||||
}
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
private String interfacesToString() {
|
||||
if (superinterfaceNames == null) {
|
||||
return "";
|
||||
}
|
||||
else {
|
||||
StringBuilder s = new StringBuilder();
|
||||
for (String superinterfaceName : superinterfaceNames) {
|
||||
s.append(superinterfaceName);
|
||||
s.append(" ");
|
||||
}
|
||||
return s.toString().trim();
|
||||
}
|
||||
}
|
||||
|
||||
public String methodsToString(MethodMember[] methods) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
int count = 0;
|
||||
for (MethodMember method : methods) {
|
||||
s.append(" method #" + Utils.toPaddedNumber((count++), 3)).append(' ').append(method.toString()).append(
|
||||
" ")
|
||||
.append(method.bitsToString()).append('\n');
|
||||
}
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Encapsulates the information about a type relevant to reloading. The TypeDescriptor for a type is sometimes extracted
|
||||
* whilst performing some other operation (eg. {@link InterfaceExtractor}) but can also be retrieved directly using
|
||||
* {@link TypeDescriptorExtractor}.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class TypeDescriptor implements Constants {
|
||||
|
||||
private final int modifiers;
|
||||
|
||||
final String typename; // slashed
|
||||
|
||||
final String supertypeName; // slashed
|
||||
|
||||
final String[] superinterfaceNames; // slashed // empty array if there are none
|
||||
|
||||
private final MethodMember[] constructors; // empty array if there are none (but this doesn't ever happen!)
|
||||
|
||||
private final MethodMember[] methods; // empty array if there are none
|
||||
|
||||
private final MethodMember[] nonprivateMethods; // empty array if there are none
|
||||
|
||||
private final FieldMember[] fields; // empty array if there are none
|
||||
|
||||
private final FieldMember[] fieldsRequiringAccessors; // empty array if there are none
|
||||
|
||||
private List<String> finalInHierarchy; // nameAndDescriptor strings for methods final in the hierarchy (e.g. ordinal()I for an enum)
|
||||
|
||||
private final TypeRegistry registry;
|
||||
|
||||
private final boolean isReloadable;
|
||||
|
||||
private final boolean hasClinit;
|
||||
|
||||
private final static int IS_GROOVY_TYPE = 0x0001;
|
||||
|
||||
private int bits = 0x0000;
|
||||
|
||||
private ReloadableType reloadableType;
|
||||
|
||||
private int nextId = 0;
|
||||
|
||||
public TypeDescriptor(String slashedTypeName, String supertypeName, String[] superinterfaceNames, int modifiers,
|
||||
List<? extends MethodMember> constructors, List<MethodMember> methods, List<? extends FieldMember> fields,
|
||||
List<? extends FieldMember> fieldsRequiringAccessors, boolean isReloadable, TypeRegistry registry,
|
||||
boolean hasClinit,
|
||||
List<String> finalInHierarchy) {
|
||||
this.typename = slashedTypeName;
|
||||
this.supertypeName = supertypeName;
|
||||
this.superinterfaceNames = (superinterfaceNames == null ? NO_STRINGS : superinterfaceNames);
|
||||
this.finalInHierarchy = finalInHierarchy;
|
||||
this.modifiers = modifiers;
|
||||
this.fields = fields.size() == 0 ? FieldMember.NONE : fields.toArray(new FieldMember[fields.size()]);
|
||||
this.fieldsRequiringAccessors = fieldsRequiringAccessors.size() == 0 ? FieldMember.NONE
|
||||
: fieldsRequiringAccessors
|
||||
.toArray(new FieldMember[fieldsRequiringAccessors.size()]);
|
||||
this.constructors = constructors.size() == 0 ? MethodMember.NONE
|
||||
: constructors.toArray(new MethodMember[constructors
|
||||
.size()]);
|
||||
this.methods = methods.size() == 0 ? MethodMember.NONE : methods.toArray(new MethodMember[methods.size()]);
|
||||
this.nonprivateMethods = filterNonPrivateMethods(this.methods);
|
||||
this.isReloadable = isReloadable;
|
||||
this.registry = registry;
|
||||
this.hasClinit = hasClinit;
|
||||
allocateIds();
|
||||
}
|
||||
|
||||
private static MethodMember[] filterNonPrivateMethods(MethodMember[] allMethods) {
|
||||
List<MethodMember> result = null;
|
||||
for (MethodMember method : allMethods) {
|
||||
if (!method.isPrivate()) {
|
||||
if (result == null) {
|
||||
result = new ArrayList<MethodMember>();
|
||||
}
|
||||
result.add(method);
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
return MethodMember.NONE;
|
||||
}
|
||||
else {
|
||||
return result.toArray(new MethodMember[result.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
private void allocateIds() {
|
||||
// Give the methods awareness of their index
|
||||
for (MethodMember method : methods) {
|
||||
method.setId(nextId++);
|
||||
}
|
||||
}
|
||||
|
||||
public MethodMember[] getMethods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
public MethodMember[] getConstructors() {
|
||||
return constructors;
|
||||
}
|
||||
|
||||
public FieldMember[] getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
public FieldMember[] getFieldsRequiringAccessors() {
|
||||
return fieldsRequiringAccessors;
|
||||
}
|
||||
|
||||
public int getModifiers() {
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the (slashed) type name
|
||||
*/
|
||||
public String getName() {
|
||||
return typename;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the (slashed) supertype name
|
||||
*/
|
||||
public String getSupertypeName() {
|
||||
return supertypeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of (slashed) superinterface names (or an empty array if none)
|
||||
*/
|
||||
public String[] getSuperinterfacesName() {
|
||||
return superinterfaceNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this descriptor defines the specified method. A strict check on all aspects of the method -
|
||||
* names/exceptions/flags, etc.
|
||||
*
|
||||
* @param method the method to check the existence of in this type descriptor
|
||||
* @return true if this descriptor defines the specified method
|
||||
*/
|
||||
public boolean defines(MethodMember method) {
|
||||
for (MethodMember existingMethod : methods) {
|
||||
// make sure it *really* defines it (i.e. it is not a catcher)
|
||||
if (!MethodMember.isCatcher(existingMethod) && existingMethod.equals(method)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this descriptor defines a method with the specified name and descriptor. Return the method if it is
|
||||
* found. Modifiers, generic signature and exceptions are ignored in this search.
|
||||
*
|
||||
* @param name the member name
|
||||
* @param descriptor the member descriptor (e.g. (Ljava/lang/String;)I)
|
||||
* @return the MethodMember if there is one
|
||||
*/
|
||||
public MethodMember getByDescriptor(String name, String descriptor) {
|
||||
for (MethodMember existingMethod : methods) {
|
||||
if (existingMethod.getName().equals(name) && existingMethod.getDescriptor().equals(descriptor)) {
|
||||
return existingMethod;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public MethodMember getByNameAndDescriptor(String nameAndDescriptor) {
|
||||
for (MethodMember existingMethod : methods) {
|
||||
if (nameAndDescriptor.startsWith(existingMethod.getName())
|
||||
&& nameAndDescriptor.endsWith(existingMethod.getDescriptor())) {
|
||||
return existingMethod;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this type descriptor has been created for a reloadable type
|
||||
*/
|
||||
public boolean isReloadable() {
|
||||
return isReloadable;
|
||||
}
|
||||
|
||||
public MethodMember getMethod(int methodId) {
|
||||
// Should never be an AIOOBE if the woven code is behaving
|
||||
return methods[methodId];
|
||||
}
|
||||
|
||||
public MethodMember getConstructor(int ctorId) {
|
||||
// Should never be an AIOOBE if the woven code is behaving
|
||||
return constructors[ctorId];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the type is an interface
|
||||
*/
|
||||
public boolean isInterface() {
|
||||
return (modifiers & ACC_INTERFACE) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the type is an annotation
|
||||
*/
|
||||
public boolean isAnnotation() {
|
||||
return (modifiers & ACC_ANNOTATION) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the type is an enum
|
||||
*/
|
||||
public boolean isEnum() {
|
||||
return (modifiers & ACC_ENUM) != 0;
|
||||
}
|
||||
|
||||
public boolean definesNonPrivate(String nameAndDescriptor) {
|
||||
for (MethodMember existingMethod : nonprivateMethods) {
|
||||
if (existingMethod.nameAndDescriptor.equals(nameAndDescriptor)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isFinalInHierarchy(String nad) {
|
||||
return finalInHierarchy.contains(nad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a field on this type descriptor - do not try supertypes. This lookup does not differentiate between
|
||||
* static/instance fields.
|
||||
*
|
||||
* @param name the name of the field
|
||||
* @return a FieldMember if the field is found, otherwise null
|
||||
*/
|
||||
public FieldMember getField(String name) {
|
||||
for (FieldMember field : fields) {
|
||||
if (field.getName().equals(name)) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ReloadableType getReloadableType() {
|
||||
if (!isReloadable) {
|
||||
return null;
|
||||
}
|
||||
if (reloadableType == null) {
|
||||
reloadableType = registry.getReloadableType(this.typename);
|
||||
if (reloadableType == null) {
|
||||
throw new IllegalStateException("There is no ReloadableType instance for " + typename);
|
||||
}
|
||||
}
|
||||
return reloadableType;
|
||||
}
|
||||
|
||||
public TypeRegistry getTypeRegistry() {
|
||||
return registry;
|
||||
}
|
||||
|
||||
// could be worth caching if used for more than error messages...
|
||||
public String getDottedName() {
|
||||
return getName().replace('/', '.');
|
||||
}
|
||||
|
||||
public MethodMember getConstructor(String desc) {
|
||||
for (MethodMember ctor : constructors) {
|
||||
String d = ctor.getDescriptor();
|
||||
if (d.equals(desc)) {
|
||||
return ctor;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isGroovyType() {
|
||||
return (bits & IS_GROOVY_TYPE) != 0;
|
||||
}
|
||||
|
||||
public void setIsGroovyType(boolean b) {
|
||||
bits |= IS_GROOVY_TYPE;
|
||||
}
|
||||
|
||||
public boolean hasClinit() {
|
||||
return hasClinit;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append("TypeDescriptor: name=" + typename + " superclass=" + supertypeName + " superinterfaces="
|
||||
+ interfacesToString());
|
||||
s.append(" flags=0x" + Integer.toHexString(modifiers).toUpperCase()).append("\n");
|
||||
s.append("Fields: #" + fields.length + "\n" + fieldsToString());
|
||||
s.append("Constructors:#" + constructors.length + "\n" + methodsToString(constructors));
|
||||
s.append("Methods:#" + methods.length + "\n" + methodsToString(methods));
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
private String fieldsToString() {
|
||||
StringBuilder s = new StringBuilder();
|
||||
int count = 0;
|
||||
for (FieldMember field : fields) {
|
||||
s.append(" field #" + Utils.toPaddedNumber((count++), 3)).append(' ').append(field.toString()).append('\n');
|
||||
}
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
private String interfacesToString() {
|
||||
if (superinterfaceNames == null) {
|
||||
return "";
|
||||
}
|
||||
else {
|
||||
StringBuilder s = new StringBuilder();
|
||||
for (String superinterfaceName : superinterfaceNames) {
|
||||
s.append(superinterfaceName);
|
||||
s.append(" ");
|
||||
}
|
||||
return s.toString().trim();
|
||||
}
|
||||
}
|
||||
|
||||
public String methodsToString(MethodMember[] methods) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
int count = 0;
|
||||
for (MethodMember method : methods) {
|
||||
s.append(" method #" + Utils.toPaddedNumber((count++), 3)).append(' ').append(method.toString()).append(
|
||||
" ")
|
||||
.append(method.bitsToString()).append('\n');
|
||||
}
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,389 +1,389 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
/**
|
||||
* A type descriptor describes the type, methods, fields, etc - two type descriptors are comparable to discover what has
|
||||
* changed between versions.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class TypeDescriptorExtractor {
|
||||
|
||||
private final static boolean DEBUG_TYPE_DESCRIPTOR_EXTRACTOR = false;
|
||||
|
||||
private TypeRegistry registry;
|
||||
|
||||
public TypeDescriptorExtractor(TypeRegistry registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
public TypeDescriptor extract(byte[] bytes, boolean isReloadableType) {
|
||||
ClassReader fileReader = new ClassReader(bytes);
|
||||
ExtractionVisitor extractionVisitor = new ExtractionVisitor(isReloadableType);
|
||||
fileReader.accept(extractionVisitor, 0);
|
||||
return extractionVisitor.getTypeDescriptor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit a class and accumulate sufficient information to build a TypeDescriptor.
|
||||
*/
|
||||
class ExtractionVisitor extends ClassVisitor implements Opcodes {
|
||||
|
||||
private boolean isReloadableType;
|
||||
|
||||
private int flags;
|
||||
|
||||
private String typename;
|
||||
|
||||
private String superclassName;
|
||||
|
||||
private String[] interfaceNames;
|
||||
|
||||
private boolean isGroovy = false;
|
||||
|
||||
private boolean isEnum = false;
|
||||
|
||||
private boolean hasClinit = false;
|
||||
|
||||
// TODO [perf - reduce garbage] make these collections lazily initialize
|
||||
private List<MethodMember> constructors = new ArrayList<MethodMember>();
|
||||
|
||||
private List<MethodMember> methods = new ArrayList<MethodMember>();
|
||||
|
||||
private List<FieldMember> fieldsRequiringAccessors = new ArrayList<FieldMember>();
|
||||
|
||||
private List<FieldMember> fields = new ArrayList<FieldMember>();
|
||||
|
||||
private List<String> finalInHierarchy = new ArrayList<String>();
|
||||
|
||||
public ExtractionVisitor(boolean isReloadableType) {
|
||||
super(ASM5);
|
||||
this.isReloadableType = isReloadableType;
|
||||
}
|
||||
|
||||
public TypeDescriptor getTypeDescriptor() {
|
||||
if (isReloadableType) {
|
||||
computeCatchersAndSuperdispatchers();
|
||||
}
|
||||
computeFieldsRequiringAccessors();
|
||||
computeClashes();
|
||||
TypeDescriptor td = new TypeDescriptor(typename, superclassName, interfaceNames, flags, constructors,
|
||||
methods, fields,
|
||||
fieldsRequiringAccessors, isReloadableType, registry, hasClinit, finalInHierarchy);
|
||||
if (isGroovy) {
|
||||
td.setIsGroovyType(true);
|
||||
}
|
||||
return td;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if there are clashes. A clash is where a static method takes the reloadable type as its first
|
||||
* parameter but in all other ways is the same as an existing instance method. For example this instance method
|
||||
* A.foo(String) clashes with this static method A.foo(A, String). 'clashing' means the executor will have to do
|
||||
* something to avoid a duplicate method problem and we'll have to differentiate between the two.
|
||||
*/
|
||||
private void computeClashes() {
|
||||
String clashDescriptorPrefix = "(L" + typename + ";";
|
||||
for (MethodMember member : methods) {
|
||||
if (member.isStatic()) {
|
||||
String desc = member.descriptor;
|
||||
if (desc.startsWith(clashDescriptorPrefix)) {
|
||||
// might be a clash, need to check the instance methods
|
||||
for (MethodMember member2 : methods) {
|
||||
if (member2.name.equals(member.name)) {
|
||||
// really might be a clash
|
||||
String instanceParams = member2.descriptor;
|
||||
instanceParams = instanceParams.substring(1, instanceParams.indexOf(')') + 1);
|
||||
String staticParams = desc.substring(clashDescriptorPrefix.length(),
|
||||
desc.indexOf(')') + 1);
|
||||
if (instanceParams.equals(staticParams)) {
|
||||
// CLASH
|
||||
member.bits |= MethodMember.BIT_CLASH;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TypeDescriptor getTypeDescriptorFor(String slashedname) {
|
||||
return registry.getDescriptorFor(slashedname);
|
||||
}
|
||||
|
||||
// TODO [refactor] extract the type registry relationship code into a central helper class
|
||||
private TypeDescriptor findTypeDescriptor(TypeRegistry registry, String typename) {
|
||||
// follow the pattern for a classloader: recurse up trying to find it, then recurse down trying to load it
|
||||
TypeRegistry regToTry = registry;
|
||||
TypeDescriptor td = regToTry.getDescriptorForReloadableType(typename);
|
||||
while (td == null) {
|
||||
regToTry = regToTry.getParentRegistry();
|
||||
if (regToTry == null) {
|
||||
break;
|
||||
}
|
||||
td = regToTry.getDescriptorForReloadableType(typename);
|
||||
}
|
||||
if (td == null) {
|
||||
td = getTypeDescriptorFor(typename);
|
||||
}
|
||||
return td;
|
||||
}
|
||||
|
||||
/**
|
||||
* Algorithm: Go up the superclass hierarchy for a type and determine what should be caught in this type (see
|
||||
* 'catchers' in notes.md). Methods that are private, static or final do *not* get a catcher. This method also
|
||||
* computes superdispatchers - see 'superdispatchers' in notes.md
|
||||
*
|
||||
*/
|
||||
private void walkHierarchyForCatchersAndSuperDispatchers(String superclass, List<String> superDispatchers,
|
||||
List<String> finalInHierarchy) {
|
||||
TypeDescriptor supertypeDescriptor = superclass == null ? null : findTypeDescriptor(registry, superclass);
|
||||
if (DEBUG_TYPE_DESCRIPTOR_EXTRACTOR) {
|
||||
System.out.println("Computing catchers on " + this.typename + " from superclass " + superclass);
|
||||
}
|
||||
boolean isReloadable = supertypeDescriptor.isReloadable();
|
||||
for (MethodMember method : supertypeDescriptor.getMethods()) {
|
||||
if (shouldCreateSuperDispatcherFor(method) && !superDispatchers.contains(method.nameAndDescriptor)) {
|
||||
// need a public super dispatcher - so that we can reach that super method
|
||||
// from a reloaded instance of this type
|
||||
MethodMember superdispatcher = method.superDispatcherFor();
|
||||
methods.add(superdispatcher);
|
||||
superDispatchers.add(method.nameAndDescriptor);
|
||||
}
|
||||
if (shouldCatchMethod(method) && !finalInHierarchy.contains(method.getNameAndDescriptor())) {
|
||||
// don't need the catcher if method is already defined since when the existing method is rewritten
|
||||
// it will be kind of morphed into a catcher
|
||||
// TODO what about a private method that is overridden by a static method (same name/descriptor but not
|
||||
// an overrides relationship)
|
||||
if (!isReloadable && Modifier.isFinal(method.getModifiers())) {
|
||||
// Do not create a catcher, the supertype is not reloadable and so an implementation cannot be
|
||||
// added lower in the type hierarchy
|
||||
finalInHierarchy.add(method.getNameAndDescriptor());
|
||||
continue;
|
||||
}
|
||||
MethodMember found = null;
|
||||
for (MethodMember existingMethod : methods) {
|
||||
if (existingMethod.equalsApartFromModifiers(method)) {
|
||||
found = existingMethod;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found != null) {
|
||||
continue;
|
||||
}
|
||||
MethodMember catcherCopy = method.catcherCopyOf();
|
||||
if (DEBUG_TYPE_DESCRIPTOR_EXTRACTOR) {
|
||||
System.out.println("Adding catcher for " + method.nameAndDescriptor);
|
||||
}
|
||||
methods.add(catcherCopy);
|
||||
}
|
||||
else {
|
||||
if (method.isFinal()) {
|
||||
finalInHierarchy.add(method.getNameAndDescriptor());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (supertypeDescriptor.supertypeName != null) {
|
||||
walkHierarchyForCatchersAndSuperDispatchers(supertypeDescriptor.supertypeName, superDispatchers,
|
||||
finalInHierarchy);
|
||||
}
|
||||
if (Modifier.isAbstract(this.flags) && !this.isEnum/* && !Modifier.isInterface(this.flags)*/) {
|
||||
// abstract class may be missing methods that it can implement from the interfaces
|
||||
for (String interfaceName : supertypeDescriptor.superinterfaceNames) {
|
||||
addCatchersForNonImplementedMethodsFrom(interfaceName, finalInHierarchy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute and add the catch methods and super dispatch methods that apply to this type.
|
||||
*/
|
||||
private void computeCatchersAndSuperdispatchers() {
|
||||
if (Modifier.isInterface(this.flags)) { // Don't need catchers in interfaces
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO [review design] review the need to create catchers for methods where the supertype is reloadable.
|
||||
// Can we just add them to the topmost reloadable type?
|
||||
List<String> doNotCatch = new ArrayList<String>();
|
||||
walkHierarchyForCatchersAndSuperDispatchers(superclassName, new ArrayList<String>(), doNotCatch);
|
||||
|
||||
// ought to look in interfaces if we are an abstract class
|
||||
if (Modifier.isAbstract(this.flags) && !this.isEnum/* && !Modifier.isInterface(this.flags)*/) {
|
||||
// abstract class may be missing methods that it can implement from the interfaces
|
||||
for (String interfaceName : interfaceNames) {
|
||||
addCatchersForNonImplementedMethodsFrom(interfaceName, doNotCatch);
|
||||
}
|
||||
}
|
||||
if (DEBUG_TYPE_DESCRIPTOR_EXTRACTOR) {
|
||||
System.out.println("For " + this.typename + " setting finalsInHierarchy to " + doNotCatch);
|
||||
}
|
||||
finalInHierarchy.addAll(doNotCatch);
|
||||
}
|
||||
|
||||
// TODO should clone and finalize be in here?
|
||||
private boolean shouldCreateSuperDispatcherFor(MethodMember method) {
|
||||
return method.isProtected() && !(
|
||||
(method.getName().equals("finalize") && method.getDescriptor().equals("()V")) ||
|
||||
(method.getName().equals("clone") && method.getDescriptor().equals("()Ljava/lang/Object;")));
|
||||
}
|
||||
|
||||
private void addCatchersForNonImplementedMethodsFrom(String interfacename, List<String> finalInNonReloadableType) {
|
||||
TypeDescriptor interfaceDescriptor = findTypeDescriptor(registry, interfacename);
|
||||
for (MethodMember method : interfaceDescriptor.getMethods()) {
|
||||
// If this class doesn't implement this interface method, add it
|
||||
boolean found = false;
|
||||
for (MethodMember existingMethod : methods) {
|
||||
if (existingMethod.equalsApartFromModifiers(method)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found && !finalInNonReloadableType.contains(method.getNameAndDescriptor())) {
|
||||
if (DEBUG_TYPE_DESCRIPTOR_EXTRACTOR) {
|
||||
Log.log("adding catcher for [" + method + "] from [" + interfacename + "] to [" + this.typename
|
||||
+ "]");
|
||||
}
|
||||
methods.add(method.catcherCopyOfWithAbstractRemoved());
|
||||
}
|
||||
}
|
||||
for (String interfaceName : interfaceDescriptor.superinterfaceNames) {
|
||||
addCatchersForNonImplementedMethodsFrom(interfaceName, finalInNonReloadableType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected fields in reloadable parents of a class need an accessor adding to the reloadable type so that the
|
||||
* fields can be reached from the executor.
|
||||
*/
|
||||
private void computeFieldsRequiringAccessors() {
|
||||
String type = superclassName;
|
||||
while (type != null) {
|
||||
TypeDescriptor supertypeDescriptor = findTypeDescriptor(registry, type);
|
||||
if (!supertypeDescriptor.isReloadable()) {
|
||||
for (FieldMember field : supertypeDescriptor.getFields()) {
|
||||
if (field.isProtected()) {
|
||||
boolean found = false;
|
||||
for (FieldMember existingField : fields) {
|
||||
if (existingField.getName().equals(field.getName())) {
|
||||
// no need for accessor... this type defines a field that overrides it
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
fieldsRequiringAccessors.add(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
type = supertypeDescriptor.supertypeName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a method gets a catcher. Deliberately not catching final methods, static methods, private
|
||||
* methods or finalize()V.
|
||||
*
|
||||
* @return true if it should be caught
|
||||
*/
|
||||
private boolean shouldCatchMethod(MethodMember method) {
|
||||
return !(method.isPrivateOrStaticOrFinal()
|
||||
|| method.getName().endsWith(Constants.methodSuffixSuperDispatcher) || (method.getName().equals(
|
||||
"finalize") && method.getDescriptor().equals("()V")));
|
||||
}
|
||||
|
||||
public void visit(int version, int flags, String name, String signature, String superclassName,
|
||||
String[] interfaceNames) {
|
||||
this.flags = flags;
|
||||
this.superclassName = superclassName;
|
||||
this.interfaceNames = interfaceNames;
|
||||
if (superclassName != null && superclassName.equals("java/lang/Enum")) {
|
||||
this.isEnum = true;
|
||||
}
|
||||
this.typename = name;
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String classDesc, boolean isRuntime) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitAttribute(Attribute attribute) {
|
||||
}
|
||||
|
||||
public void visitInnerClass(String name, String outername, String innerName, int access) {
|
||||
if (name.equals(typename)) {
|
||||
this.flags = access;
|
||||
}
|
||||
}
|
||||
|
||||
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
|
||||
fields.add(new FieldMember(typename, access, name, desc, signature));
|
||||
if (name.equals("$callSiteArray")) {
|
||||
isGroovy = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit a method in the class and build an appropriate representation for it to include in the extracted
|
||||
* output.
|
||||
*/
|
||||
public MethodVisitor visitMethod(int flags, String name, String descriptor, String genericSignature,
|
||||
String[] exceptions) {
|
||||
if (name.charAt(0) != '<') {
|
||||
methods.add(new MethodMember(flags, name, descriptor, genericSignature, exceptions));
|
||||
}
|
||||
else {
|
||||
if (name.equals("<init>")) {
|
||||
//Even though constructors are not reloadable at present, we need to add them to type descriptors to know
|
||||
//about their original modifiers (these are promoted to public to allow executors access to them).
|
||||
constructors.add(new MethodMember(flags, name, descriptor, genericSignature, exceptions));
|
||||
}
|
||||
else if (name.equals("<clinit>")) {
|
||||
hasClinit = true;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitOuterClass(String owner, String name, String desc) {
|
||||
}
|
||||
|
||||
public void visitSource(String source, String debug) {
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
/**
|
||||
* A type descriptor describes the type, methods, fields, etc - two type descriptors are comparable to discover what has
|
||||
* changed between versions.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class TypeDescriptorExtractor {
|
||||
|
||||
private final static boolean DEBUG_TYPE_DESCRIPTOR_EXTRACTOR = false;
|
||||
|
||||
private TypeRegistry registry;
|
||||
|
||||
public TypeDescriptorExtractor(TypeRegistry registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
public TypeDescriptor extract(byte[] bytes, boolean isReloadableType) {
|
||||
ClassReader fileReader = new ClassReader(bytes);
|
||||
ExtractionVisitor extractionVisitor = new ExtractionVisitor(isReloadableType);
|
||||
fileReader.accept(extractionVisitor, 0);
|
||||
return extractionVisitor.getTypeDescriptor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit a class and accumulate sufficient information to build a TypeDescriptor.
|
||||
*/
|
||||
class ExtractionVisitor extends ClassVisitor implements Opcodes {
|
||||
|
||||
private boolean isReloadableType;
|
||||
|
||||
private int flags;
|
||||
|
||||
private String typename;
|
||||
|
||||
private String superclassName;
|
||||
|
||||
private String[] interfaceNames;
|
||||
|
||||
private boolean isGroovy = false;
|
||||
|
||||
private boolean isEnum = false;
|
||||
|
||||
private boolean hasClinit = false;
|
||||
|
||||
// TODO [perf - reduce garbage] make these collections lazily initialize
|
||||
private List<MethodMember> constructors = new ArrayList<MethodMember>();
|
||||
|
||||
private List<MethodMember> methods = new ArrayList<MethodMember>();
|
||||
|
||||
private List<FieldMember> fieldsRequiringAccessors = new ArrayList<FieldMember>();
|
||||
|
||||
private List<FieldMember> fields = new ArrayList<FieldMember>();
|
||||
|
||||
private List<String> finalInHierarchy = new ArrayList<String>();
|
||||
|
||||
public ExtractionVisitor(boolean isReloadableType) {
|
||||
super(ASM5);
|
||||
this.isReloadableType = isReloadableType;
|
||||
}
|
||||
|
||||
public TypeDescriptor getTypeDescriptor() {
|
||||
if (isReloadableType) {
|
||||
computeCatchersAndSuperdispatchers();
|
||||
}
|
||||
computeFieldsRequiringAccessors();
|
||||
computeClashes();
|
||||
TypeDescriptor td = new TypeDescriptor(typename, superclassName, interfaceNames, flags, constructors,
|
||||
methods, fields,
|
||||
fieldsRequiringAccessors, isReloadableType, registry, hasClinit, finalInHierarchy);
|
||||
if (isGroovy) {
|
||||
td.setIsGroovyType(true);
|
||||
}
|
||||
return td;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if there are clashes. A clash is where a static method takes the reloadable type as its first
|
||||
* parameter but in all other ways is the same as an existing instance method. For example this instance method
|
||||
* A.foo(String) clashes with this static method A.foo(A, String). 'clashing' means the executor will have to do
|
||||
* something to avoid a duplicate method problem and we'll have to differentiate between the two.
|
||||
*/
|
||||
private void computeClashes() {
|
||||
String clashDescriptorPrefix = "(L" + typename + ";";
|
||||
for (MethodMember member : methods) {
|
||||
if (member.isStatic()) {
|
||||
String desc = member.descriptor;
|
||||
if (desc.startsWith(clashDescriptorPrefix)) {
|
||||
// might be a clash, need to check the instance methods
|
||||
for (MethodMember member2 : methods) {
|
||||
if (member2.name.equals(member.name)) {
|
||||
// really might be a clash
|
||||
String instanceParams = member2.descriptor;
|
||||
instanceParams = instanceParams.substring(1, instanceParams.indexOf(')') + 1);
|
||||
String staticParams = desc.substring(clashDescriptorPrefix.length(),
|
||||
desc.indexOf(')') + 1);
|
||||
if (instanceParams.equals(staticParams)) {
|
||||
// CLASH
|
||||
member.bits |= MethodMember.BIT_CLASH;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TypeDescriptor getTypeDescriptorFor(String slashedname) {
|
||||
return registry.getDescriptorFor(slashedname);
|
||||
}
|
||||
|
||||
// TODO [refactor] extract the type registry relationship code into a central helper class
|
||||
private TypeDescriptor findTypeDescriptor(TypeRegistry registry, String typename) {
|
||||
// follow the pattern for a classloader: recurse up trying to find it, then recurse down trying to load it
|
||||
TypeRegistry regToTry = registry;
|
||||
TypeDescriptor td = regToTry.getDescriptorForReloadableType(typename);
|
||||
while (td == null) {
|
||||
regToTry = regToTry.getParentRegistry();
|
||||
if (regToTry == null) {
|
||||
break;
|
||||
}
|
||||
td = regToTry.getDescriptorForReloadableType(typename);
|
||||
}
|
||||
if (td == null) {
|
||||
td = getTypeDescriptorFor(typename);
|
||||
}
|
||||
return td;
|
||||
}
|
||||
|
||||
/**
|
||||
* Algorithm: Go up the superclass hierarchy for a type and determine what should be caught in this type (see
|
||||
* 'catchers' in notes.md). Methods that are private, static or final do *not* get a catcher. This method also
|
||||
* computes superdispatchers - see 'superdispatchers' in notes.md
|
||||
*
|
||||
*/
|
||||
private void walkHierarchyForCatchersAndSuperDispatchers(String superclass, List<String> superDispatchers,
|
||||
List<String> finalInHierarchy) {
|
||||
TypeDescriptor supertypeDescriptor = superclass == null ? null : findTypeDescriptor(registry, superclass);
|
||||
if (DEBUG_TYPE_DESCRIPTOR_EXTRACTOR) {
|
||||
System.out.println("Computing catchers on " + this.typename + " from superclass " + superclass);
|
||||
}
|
||||
boolean isReloadable = supertypeDescriptor.isReloadable();
|
||||
for (MethodMember method : supertypeDescriptor.getMethods()) {
|
||||
if (shouldCreateSuperDispatcherFor(method) && !superDispatchers.contains(method.nameAndDescriptor)) {
|
||||
// need a public super dispatcher - so that we can reach that super method
|
||||
// from a reloaded instance of this type
|
||||
MethodMember superdispatcher = method.superDispatcherFor();
|
||||
methods.add(superdispatcher);
|
||||
superDispatchers.add(method.nameAndDescriptor);
|
||||
}
|
||||
if (shouldCatchMethod(method) && !finalInHierarchy.contains(method.getNameAndDescriptor())) {
|
||||
// don't need the catcher if method is already defined since when the existing method is rewritten
|
||||
// it will be kind of morphed into a catcher
|
||||
// TODO what about a private method that is overridden by a static method (same name/descriptor but not
|
||||
// an overrides relationship)
|
||||
if (!isReloadable && Modifier.isFinal(method.getModifiers())) {
|
||||
// Do not create a catcher, the supertype is not reloadable and so an implementation cannot be
|
||||
// added lower in the type hierarchy
|
||||
finalInHierarchy.add(method.getNameAndDescriptor());
|
||||
continue;
|
||||
}
|
||||
MethodMember found = null;
|
||||
for (MethodMember existingMethod : methods) {
|
||||
if (existingMethod.equalsApartFromModifiers(method)) {
|
||||
found = existingMethod;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found != null) {
|
||||
continue;
|
||||
}
|
||||
MethodMember catcherCopy = method.catcherCopyOf();
|
||||
if (DEBUG_TYPE_DESCRIPTOR_EXTRACTOR) {
|
||||
System.out.println("Adding catcher for " + method.nameAndDescriptor);
|
||||
}
|
||||
methods.add(catcherCopy);
|
||||
}
|
||||
else {
|
||||
if (method.isFinal()) {
|
||||
finalInHierarchy.add(method.getNameAndDescriptor());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (supertypeDescriptor.supertypeName != null) {
|
||||
walkHierarchyForCatchersAndSuperDispatchers(supertypeDescriptor.supertypeName, superDispatchers,
|
||||
finalInHierarchy);
|
||||
}
|
||||
if (Modifier.isAbstract(this.flags) && !this.isEnum/* && !Modifier.isInterface(this.flags)*/) {
|
||||
// abstract class may be missing methods that it can implement from the interfaces
|
||||
for (String interfaceName : supertypeDescriptor.superinterfaceNames) {
|
||||
addCatchersForNonImplementedMethodsFrom(interfaceName, finalInHierarchy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute and add the catch methods and super dispatch methods that apply to this type.
|
||||
*/
|
||||
private void computeCatchersAndSuperdispatchers() {
|
||||
if (Modifier.isInterface(this.flags)) { // Don't need catchers in interfaces
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO [review design] review the need to create catchers for methods where the supertype is reloadable.
|
||||
// Can we just add them to the topmost reloadable type?
|
||||
List<String> doNotCatch = new ArrayList<String>();
|
||||
walkHierarchyForCatchersAndSuperDispatchers(superclassName, new ArrayList<String>(), doNotCatch);
|
||||
|
||||
// ought to look in interfaces if we are an abstract class
|
||||
if (Modifier.isAbstract(this.flags) && !this.isEnum/* && !Modifier.isInterface(this.flags)*/) {
|
||||
// abstract class may be missing methods that it can implement from the interfaces
|
||||
for (String interfaceName : interfaceNames) {
|
||||
addCatchersForNonImplementedMethodsFrom(interfaceName, doNotCatch);
|
||||
}
|
||||
}
|
||||
if (DEBUG_TYPE_DESCRIPTOR_EXTRACTOR) {
|
||||
System.out.println("For " + this.typename + " setting finalsInHierarchy to " + doNotCatch);
|
||||
}
|
||||
finalInHierarchy.addAll(doNotCatch);
|
||||
}
|
||||
|
||||
// TODO should clone and finalize be in here?
|
||||
private boolean shouldCreateSuperDispatcherFor(MethodMember method) {
|
||||
return method.isProtected() && !(
|
||||
(method.getName().equals("finalize") && method.getDescriptor().equals("()V")) ||
|
||||
(method.getName().equals("clone") && method.getDescriptor().equals("()Ljava/lang/Object;")));
|
||||
}
|
||||
|
||||
private void addCatchersForNonImplementedMethodsFrom(String interfacename, List<String> finalInNonReloadableType) {
|
||||
TypeDescriptor interfaceDescriptor = findTypeDescriptor(registry, interfacename);
|
||||
for (MethodMember method : interfaceDescriptor.getMethods()) {
|
||||
// If this class doesn't implement this interface method, add it
|
||||
boolean found = false;
|
||||
for (MethodMember existingMethod : methods) {
|
||||
if (existingMethod.equalsApartFromModifiers(method)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found && !finalInNonReloadableType.contains(method.getNameAndDescriptor())) {
|
||||
if (DEBUG_TYPE_DESCRIPTOR_EXTRACTOR) {
|
||||
Log.log("adding catcher for [" + method + "] from [" + interfacename + "] to [" + this.typename
|
||||
+ "]");
|
||||
}
|
||||
methods.add(method.catcherCopyOfWithAbstractRemoved());
|
||||
}
|
||||
}
|
||||
for (String interfaceName : interfaceDescriptor.superinterfaceNames) {
|
||||
addCatchersForNonImplementedMethodsFrom(interfaceName, finalInNonReloadableType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected fields in reloadable parents of a class need an accessor adding to the reloadable type so that the
|
||||
* fields can be reached from the executor.
|
||||
*/
|
||||
private void computeFieldsRequiringAccessors() {
|
||||
String type = superclassName;
|
||||
while (type != null) {
|
||||
TypeDescriptor supertypeDescriptor = findTypeDescriptor(registry, type);
|
||||
if (!supertypeDescriptor.isReloadable()) {
|
||||
for (FieldMember field : supertypeDescriptor.getFields()) {
|
||||
if (field.isProtected()) {
|
||||
boolean found = false;
|
||||
for (FieldMember existingField : fields) {
|
||||
if (existingField.getName().equals(field.getName())) {
|
||||
// no need for accessor... this type defines a field that overrides it
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
fieldsRequiringAccessors.add(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
type = supertypeDescriptor.supertypeName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a method gets a catcher. Deliberately not catching final methods, static methods, private
|
||||
* methods or finalize()V.
|
||||
*
|
||||
* @return true if it should be caught
|
||||
*/
|
||||
private boolean shouldCatchMethod(MethodMember method) {
|
||||
return !(method.isPrivateOrStaticOrFinal()
|
||||
|| method.getName().endsWith(Constants.methodSuffixSuperDispatcher) || (method.getName().equals(
|
||||
"finalize") && method.getDescriptor().equals("()V")));
|
||||
}
|
||||
|
||||
public void visit(int version, int flags, String name, String signature, String superclassName,
|
||||
String[] interfaceNames) {
|
||||
this.flags = flags;
|
||||
this.superclassName = superclassName;
|
||||
this.interfaceNames = interfaceNames;
|
||||
if (superclassName != null && superclassName.equals("java/lang/Enum")) {
|
||||
this.isEnum = true;
|
||||
}
|
||||
this.typename = name;
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String classDesc, boolean isRuntime) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitAttribute(Attribute attribute) {
|
||||
}
|
||||
|
||||
public void visitInnerClass(String name, String outername, String innerName, int access) {
|
||||
if (name.equals(typename)) {
|
||||
this.flags = access;
|
||||
}
|
||||
}
|
||||
|
||||
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
|
||||
fields.add(new FieldMember(typename, access, name, desc, signature));
|
||||
if (name.equals("$callSiteArray")) {
|
||||
isGroovy = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit a method in the class and build an appropriate representation for it to include in the extracted
|
||||
* output.
|
||||
*/
|
||||
public MethodVisitor visitMethod(int flags, String name, String descriptor, String genericSignature,
|
||||
String[] exceptions) {
|
||||
if (name.charAt(0) != '<') {
|
||||
methods.add(new MethodMember(flags, name, descriptor, genericSignature, exceptions));
|
||||
}
|
||||
else {
|
||||
if (name.equals("<init>")) {
|
||||
//Even though constructors are not reloadable at present, we need to add them to type descriptors to know
|
||||
//about their original modifiers (these are promoted to public to allow executors access to them).
|
||||
constructors.add(new MethodMember(flags, name, descriptor, genericSignature, exceptions));
|
||||
}
|
||||
else if (name.equals("<clinit>")) {
|
||||
hasClinit = true;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitOuterClass(String owner, String name, String desc) {
|
||||
}
|
||||
|
||||
public void visitSource(String source, String debug) {
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Similar to the AspectJ type pattern model - used for defining reloadable type inclusions/exclusions.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public abstract class TypePattern {
|
||||
|
||||
public boolean matches(String dottedname) {
|
||||
if (GlobalConfiguration.assertsMode) {
|
||||
Utils.assertDotted(dottedname);
|
||||
}
|
||||
return internalMatches(dottedname);
|
||||
}
|
||||
|
||||
protected abstract boolean internalMatches(String input);
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Similar to the AspectJ type pattern model - used for defining reloadable type inclusions/exclusions.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public abstract class TypePattern {
|
||||
|
||||
public boolean matches(String dottedname) {
|
||||
if (GlobalConfiguration.assertsMode) {
|
||||
Utils.assertDotted(dottedname);
|
||||
}
|
||||
return internalMatches(dottedname);
|
||||
}
|
||||
|
||||
protected abstract boolean internalMatches(String input);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,120 +1,120 @@
|
||||
/*
|
||||
* 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.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.IllegalClassFormatException;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springsource.loaded.GlobalConfiguration;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
|
||||
|
||||
/**
|
||||
* Class pre-processor.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class ClassPreProcessorAgentAdapter implements ClassFileTransformer {
|
||||
|
||||
private static Logger log = Logger.getLogger(ClassPreProcessorAgentAdapter.class.getName());
|
||||
|
||||
private static SpringLoadedPreProcessor preProcessor;
|
||||
|
||||
private static ClassPreProcessorAgentAdapter instance;
|
||||
|
||||
public ClassPreProcessorAgentAdapter() {
|
||||
instance = this;
|
||||
}
|
||||
|
||||
static {
|
||||
try {
|
||||
preProcessor = new SpringLoadedPreProcessor();
|
||||
preProcessor.initialize();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new ExceptionInInitializerError("could not initialize JSR163 preprocessor due to: " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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));
|
||||
}
|
||||
|
||||
// TODO determine if this is the right behaviour for hot code replace:
|
||||
// Handling class redefinition (hot code replace) - what to do depends on whether the type is a reloadable type or not
|
||||
// If reloadable - return the class as originally defined, and treat this new input data as the new version to make live
|
||||
// If not-reloadable - rewrite the call sites and attempt hot code replace
|
||||
|
||||
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);
|
||||
// CurrentLiveVersion clv = rtype.getLiveVersion();
|
||||
// String suffix = "0";
|
||||
// if (clv != null) {
|
||||
// suffix = clv.getVersionStamp() + "H";
|
||||
// }
|
||||
// rtype.loadNewVersion(suffix, bytes);
|
||||
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
|
||||
log.info("Tricking HCR for " + className);
|
||||
}
|
||||
return rtype.bytesLoaded; // returning original bytes
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// System.err.println("transform(" + loader.getClass().getName() + ",classname=" + className +
|
||||
// ",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;
|
||||
}
|
||||
}
|
||||
|
||||
public static void reload(ClassLoader loader, String className, Class<?> classBeingRedefined,
|
||||
ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException {
|
||||
instance.transform(loader, className, classBeingRedefined, protectionDomain, bytes);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.IllegalClassFormatException;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springsource.loaded.GlobalConfiguration;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
|
||||
|
||||
/**
|
||||
* Class pre-processor.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class ClassPreProcessorAgentAdapter implements ClassFileTransformer {
|
||||
|
||||
private static Logger log = Logger.getLogger(ClassPreProcessorAgentAdapter.class.getName());
|
||||
|
||||
private static SpringLoadedPreProcessor preProcessor;
|
||||
|
||||
private static ClassPreProcessorAgentAdapter instance;
|
||||
|
||||
public ClassPreProcessorAgentAdapter() {
|
||||
instance = this;
|
||||
}
|
||||
|
||||
static {
|
||||
try {
|
||||
preProcessor = new SpringLoadedPreProcessor();
|
||||
preProcessor.initialize();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new ExceptionInInitializerError("could not initialize JSR163 preprocessor due to: " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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));
|
||||
}
|
||||
|
||||
// TODO determine if this is the right behaviour for hot code replace:
|
||||
// Handling class redefinition (hot code replace) - what to do depends on whether the type is a reloadable type or not
|
||||
// If reloadable - return the class as originally defined, and treat this new input data as the new version to make live
|
||||
// If not-reloadable - rewrite the call sites and attempt hot code replace
|
||||
|
||||
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);
|
||||
// CurrentLiveVersion clv = rtype.getLiveVersion();
|
||||
// String suffix = "0";
|
||||
// if (clv != null) {
|
||||
// suffix = clv.getVersionStamp() + "H";
|
||||
// }
|
||||
// rtype.loadNewVersion(suffix, bytes);
|
||||
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
|
||||
log.info("Tricking HCR for " + className);
|
||||
}
|
||||
return rtype.bytesLoaded; // returning original bytes
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// System.err.println("transform(" + loader.getClass().getName() + ",classname=" + className +
|
||||
// ",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;
|
||||
}
|
||||
}
|
||||
|
||||
public static void reload(ClassLoader loader, String className, Class<?> classBeingRedefined,
|
||||
ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException {
|
||||
instance.transform(loader, className, classBeingRedefined, protectionDomain, bytes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,294 +1,294 @@
|
||||
/*
|
||||
* 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.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springsource.loaded.FileChangeListener;
|
||||
import org.springsource.loaded.GlobalConfiguration;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
|
||||
/**
|
||||
* A simple watcher for the file system. Uses a thread to keep an eye on a number of files and calls back registered
|
||||
* interested parties when a change is observed. The thread only starts when there is something to watch. The thread is
|
||||
* given a name indicating the classloader for which it is watching files. Once it starts to watch files the name will
|
||||
* be enhanced to indicate how many.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class FileSystemWatcher {
|
||||
|
||||
// the thread being managed
|
||||
private Thread thread;
|
||||
|
||||
// whether the thread is running
|
||||
private boolean threadRunning = false;
|
||||
|
||||
// The Watcher running inside the thread
|
||||
private Watcher watchThread;
|
||||
|
||||
public FileSystemWatcher(FileChangeListener listener, int typeRegistryId, String classloadername) {
|
||||
watchThread = new Watcher(listener, typeRegistryId, classloadername);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the thread if it isn't already started.
|
||||
*/
|
||||
private void ensureWatchThreadRunning() {
|
||||
if (!threadRunning) {
|
||||
thread = new Thread(watchThread);
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
watchThread.setThread(thread);
|
||||
watchThread.updateName();
|
||||
threadRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the thread.
|
||||
*/
|
||||
public void shutdown() {
|
||||
if (threadRunning) {
|
||||
watchThread.timeToStop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new file to the list of those being monitored. If the file is something that can be watched, then this
|
||||
* method will cause the thread to start (if it hasn't already been started).
|
||||
*
|
||||
* @param fileToMonitor the file to start monitor
|
||||
*/
|
||||
public void register(File fileToMonitor) {
|
||||
if (watchThread.addFile(fileToMonitor)) {
|
||||
ensureWatchThreadRunning();
|
||||
watchThread.updateName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the filesystem watching to be paused/unpaused.
|
||||
*
|
||||
* @param shouldBePaused watching should be paused?
|
||||
*/
|
||||
public void setPaused(boolean shouldBePaused) {
|
||||
watchThread.paused = shouldBePaused;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Watcher implements Runnable {
|
||||
|
||||
private static Logger log = Logger.getLogger(Watcher.class.getName());
|
||||
|
||||
long lastScanTime;
|
||||
|
||||
// TODO configurable scan interval?
|
||||
private static long interval = 1100;// ms
|
||||
|
||||
List<File> watchListFiles = new ArrayList<File>();
|
||||
|
||||
List<Long> watchListLMTs = new ArrayList<Long>();
|
||||
|
||||
FileChangeListener listener;
|
||||
|
||||
private boolean timeToStop = false;
|
||||
|
||||
public boolean paused = false;
|
||||
|
||||
private Thread thread = null;
|
||||
|
||||
private int typeRegistryId;
|
||||
|
||||
private String classloadername;
|
||||
|
||||
private int registryLivenessCount = 0;
|
||||
|
||||
private static int registryLivenessCountInterval = 300;
|
||||
|
||||
public Watcher(FileChangeListener listener, int typeRegistryId, String classloadername) {
|
||||
this.listener = listener;
|
||||
this.typeRegistryId = typeRegistryId;
|
||||
this.classloadername = classloadername;
|
||||
}
|
||||
|
||||
public void setThread(Thread thread) {
|
||||
this.thread = thread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new File that the thread should start watching. If the file does not exist nothing happens (this may be
|
||||
* because a class has been generated on the fly and really there is nothing to watch on disk).
|
||||
*
|
||||
* @param fileToWatch the new file to watch
|
||||
* @return true if the file is now being watched, false otherwise
|
||||
*/
|
||||
public boolean addFile(File fileToWatch) {
|
||||
if (!fileToWatch.exists()) {
|
||||
return false;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
|
||||
log.info("Now watching " + fileToWatch);
|
||||
}
|
||||
int insertionPos = findPosition(fileToWatch);
|
||||
if (insertionPos == -1) {
|
||||
watchListFiles.add(fileToWatch);
|
||||
watchListLMTs.add(fileToWatch.lastModified());
|
||||
}
|
||||
else {
|
||||
watchListFiles.add(insertionPos, fileToWatch);
|
||||
watchListLMTs.add(insertionPos, fileToWatch.lastModified());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateName() {
|
||||
if (thread != null) {
|
||||
thread.setName("FileSystemWatcher: files=#" + watchListFiles.size() + " cl=" + classloadername);
|
||||
}
|
||||
}
|
||||
|
||||
private int findPosition(File file) {
|
||||
String filename = file.getName();
|
||||
int len = watchListFiles.size();
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
for (int f = 0; f < len; f++) {
|
||||
File file2 = watchListFiles.get(f);
|
||||
int cmp = file2.getName().compareTo(filename);
|
||||
// as we are using 'names' we are only considering the last part, so foo/bar/Goo.class and foo/Goo.class look the same
|
||||
// and will return cmp==0. Not really sure it matters about using fq names
|
||||
if (cmp > 0) {
|
||||
return f;
|
||||
}
|
||||
else if (GlobalConfiguration.assertsMode && cmp == 0) {
|
||||
// Are we watching the same file twice, that is bad!
|
||||
if (file2.getAbsoluteFile().toString().equals(file.getAbsoluteFile().toString())) {
|
||||
log.severe("Watching the same file twice: " + file.getAbsoluteFile().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (!timeToStop) {
|
||||
registryLivenessCount++;
|
||||
if ((registryLivenessCount % registryLivenessCountInterval) == 0) {
|
||||
// Time to check if the registry is still alive!
|
||||
if (!TypeRegistry.typeRegistryExistsForId(typeRegistryId)) {
|
||||
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
|
||||
log.info("TypeRegistry " + typeRegistryId + " gone, no point in thread continuing!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
registryLivenessCount = 0;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(interval);
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
if (!paused) {
|
||||
List<File> changedFiles = new ArrayList<File>();
|
||||
synchronized (this) {
|
||||
int len = watchListFiles.size();
|
||||
for (int f = 0; f < len; f++) {
|
||||
File file = watchListFiles.get(f);
|
||||
long lastModTime = file.lastModified();
|
||||
if (lastModTime > watchListLMTs.get(f)) {
|
||||
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
|
||||
log.info("Observed last modification time change for " + file + " (lastScanTime="
|
||||
+ lastScanTime + ")");
|
||||
}
|
||||
watchListLMTs.set(f, lastModTime);
|
||||
changedFiles.add(file);
|
||||
}
|
||||
}
|
||||
lastScanTime = System.currentTimeMillis();
|
||||
}
|
||||
for (File changedFile : changedFiles) {
|
||||
determineChangesSince(changedFile, lastScanTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* problem is that we check some file X, it hasn't changed - we then take longer than interval to check all the
|
||||
* other files we are watching.
|
||||
*/
|
||||
|
||||
private void determineChangesSince(File file, long lastScanTime) {
|
||||
try {
|
||||
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
|
||||
log.info("Firing file changed event " + file);
|
||||
}
|
||||
listener.fileChanged(file);
|
||||
if (file.isDirectory()) {
|
||||
File[] filesOfInterest = file.listFiles(new RecentChangeFilter(lastScanTime));
|
||||
for (File f : filesOfInterest) {
|
||||
if (f.isDirectory()) {
|
||||
determineChangesSince(f, lastScanTime);
|
||||
}
|
||||
else {
|
||||
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
|
||||
log.info("Observed last modification time change for " + f + " (lastScanTime="
|
||||
+ lastScanTime + ")");
|
||||
log.info("Firing file changed event " + file);
|
||||
}
|
||||
listener.fileChanged(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
if (log.isLoggable(Level.SEVERE)) {
|
||||
log.log(Level.SEVERE, "FileWatcher caught serious error, see cause", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class RecentChangeFilter implements FileFilter {
|
||||
|
||||
private long lastScanTime;
|
||||
|
||||
public RecentChangeFilter(long lastScanTime) {
|
||||
this.lastScanTime = lastScanTime;
|
||||
}
|
||||
|
||||
public boolean accept(File pathname) {
|
||||
return (pathname.lastModified() > lastScanTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void timeToStop() {
|
||||
timeToStop = true;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springsource.loaded.FileChangeListener;
|
||||
import org.springsource.loaded.GlobalConfiguration;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
|
||||
/**
|
||||
* A simple watcher for the file system. Uses a thread to keep an eye on a number of files and calls back registered
|
||||
* interested parties when a change is observed. The thread only starts when there is something to watch. The thread is
|
||||
* given a name indicating the classloader for which it is watching files. Once it starts to watch files the name will
|
||||
* be enhanced to indicate how many.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class FileSystemWatcher {
|
||||
|
||||
// the thread being managed
|
||||
private Thread thread;
|
||||
|
||||
// whether the thread is running
|
||||
private boolean threadRunning = false;
|
||||
|
||||
// The Watcher running inside the thread
|
||||
private Watcher watchThread;
|
||||
|
||||
public FileSystemWatcher(FileChangeListener listener, int typeRegistryId, String classloadername) {
|
||||
watchThread = new Watcher(listener, typeRegistryId, classloadername);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the thread if it isn't already started.
|
||||
*/
|
||||
private void ensureWatchThreadRunning() {
|
||||
if (!threadRunning) {
|
||||
thread = new Thread(watchThread);
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
watchThread.setThread(thread);
|
||||
watchThread.updateName();
|
||||
threadRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the thread.
|
||||
*/
|
||||
public void shutdown() {
|
||||
if (threadRunning) {
|
||||
watchThread.timeToStop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new file to the list of those being monitored. If the file is something that can be watched, then this
|
||||
* method will cause the thread to start (if it hasn't already been started).
|
||||
*
|
||||
* @param fileToMonitor the file to start monitor
|
||||
*/
|
||||
public void register(File fileToMonitor) {
|
||||
if (watchThread.addFile(fileToMonitor)) {
|
||||
ensureWatchThreadRunning();
|
||||
watchThread.updateName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the filesystem watching to be paused/unpaused.
|
||||
*
|
||||
* @param shouldBePaused watching should be paused?
|
||||
*/
|
||||
public void setPaused(boolean shouldBePaused) {
|
||||
watchThread.paused = shouldBePaused;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Watcher implements Runnable {
|
||||
|
||||
private static Logger log = Logger.getLogger(Watcher.class.getName());
|
||||
|
||||
long lastScanTime;
|
||||
|
||||
// TODO configurable scan interval?
|
||||
private static long interval = 1100;// ms
|
||||
|
||||
List<File> watchListFiles = new ArrayList<File>();
|
||||
|
||||
List<Long> watchListLMTs = new ArrayList<Long>();
|
||||
|
||||
FileChangeListener listener;
|
||||
|
||||
private boolean timeToStop = false;
|
||||
|
||||
public boolean paused = false;
|
||||
|
||||
private Thread thread = null;
|
||||
|
||||
private int typeRegistryId;
|
||||
|
||||
private String classloadername;
|
||||
|
||||
private int registryLivenessCount = 0;
|
||||
|
||||
private static int registryLivenessCountInterval = 300;
|
||||
|
||||
public Watcher(FileChangeListener listener, int typeRegistryId, String classloadername) {
|
||||
this.listener = listener;
|
||||
this.typeRegistryId = typeRegistryId;
|
||||
this.classloadername = classloadername;
|
||||
}
|
||||
|
||||
public void setThread(Thread thread) {
|
||||
this.thread = thread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new File that the thread should start watching. If the file does not exist nothing happens (this may be
|
||||
* because a class has been generated on the fly and really there is nothing to watch on disk).
|
||||
*
|
||||
* @param fileToWatch the new file to watch
|
||||
* @return true if the file is now being watched, false otherwise
|
||||
*/
|
||||
public boolean addFile(File fileToWatch) {
|
||||
if (!fileToWatch.exists()) {
|
||||
return false;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
|
||||
log.info("Now watching " + fileToWatch);
|
||||
}
|
||||
int insertionPos = findPosition(fileToWatch);
|
||||
if (insertionPos == -1) {
|
||||
watchListFiles.add(fileToWatch);
|
||||
watchListLMTs.add(fileToWatch.lastModified());
|
||||
}
|
||||
else {
|
||||
watchListFiles.add(insertionPos, fileToWatch);
|
||||
watchListLMTs.add(insertionPos, fileToWatch.lastModified());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateName() {
|
||||
if (thread != null) {
|
||||
thread.setName("FileSystemWatcher: files=#" + watchListFiles.size() + " cl=" + classloadername);
|
||||
}
|
||||
}
|
||||
|
||||
private int findPosition(File file) {
|
||||
String filename = file.getName();
|
||||
int len = watchListFiles.size();
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
for (int f = 0; f < len; f++) {
|
||||
File file2 = watchListFiles.get(f);
|
||||
int cmp = file2.getName().compareTo(filename);
|
||||
// as we are using 'names' we are only considering the last part, so foo/bar/Goo.class and foo/Goo.class look the same
|
||||
// and will return cmp==0. Not really sure it matters about using fq names
|
||||
if (cmp > 0) {
|
||||
return f;
|
||||
}
|
||||
else if (GlobalConfiguration.assertsMode && cmp == 0) {
|
||||
// Are we watching the same file twice, that is bad!
|
||||
if (file2.getAbsoluteFile().toString().equals(file.getAbsoluteFile().toString())) {
|
||||
log.severe("Watching the same file twice: " + file.getAbsoluteFile().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (!timeToStop) {
|
||||
registryLivenessCount++;
|
||||
if ((registryLivenessCount % registryLivenessCountInterval) == 0) {
|
||||
// Time to check if the registry is still alive!
|
||||
if (!TypeRegistry.typeRegistryExistsForId(typeRegistryId)) {
|
||||
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
|
||||
log.info("TypeRegistry " + typeRegistryId + " gone, no point in thread continuing!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
registryLivenessCount = 0;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(interval);
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
if (!paused) {
|
||||
List<File> changedFiles = new ArrayList<File>();
|
||||
synchronized (this) {
|
||||
int len = watchListFiles.size();
|
||||
for (int f = 0; f < len; f++) {
|
||||
File file = watchListFiles.get(f);
|
||||
long lastModTime = file.lastModified();
|
||||
if (lastModTime > watchListLMTs.get(f)) {
|
||||
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
|
||||
log.info("Observed last modification time change for " + file + " (lastScanTime="
|
||||
+ lastScanTime + ")");
|
||||
}
|
||||
watchListLMTs.set(f, lastModTime);
|
||||
changedFiles.add(file);
|
||||
}
|
||||
}
|
||||
lastScanTime = System.currentTimeMillis();
|
||||
}
|
||||
for (File changedFile : changedFiles) {
|
||||
determineChangesSince(changedFile, lastScanTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* problem is that we check some file X, it hasn't changed - we then take longer than interval to check all the
|
||||
* other files we are watching.
|
||||
*/
|
||||
|
||||
private void determineChangesSince(File file, long lastScanTime) {
|
||||
try {
|
||||
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
|
||||
log.info("Firing file changed event " + file);
|
||||
}
|
||||
listener.fileChanged(file);
|
||||
if (file.isDirectory()) {
|
||||
File[] filesOfInterest = file.listFiles(new RecentChangeFilter(lastScanTime));
|
||||
for (File f : filesOfInterest) {
|
||||
if (f.isDirectory()) {
|
||||
determineChangesSince(f, lastScanTime);
|
||||
}
|
||||
else {
|
||||
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
|
||||
log.info("Observed last modification time change for " + f + " (lastScanTime="
|
||||
+ lastScanTime + ")");
|
||||
log.info("Firing file changed event " + file);
|
||||
}
|
||||
listener.fileChanged(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
if (log.isLoggable(Level.SEVERE)) {
|
||||
log.log(Level.SEVERE, "FileWatcher caught serious error, see cause", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class RecentChangeFilter implements FileFilter {
|
||||
|
||||
private long lastScanTime;
|
||||
|
||||
public RecentChangeFilter(long lastScanTime) {
|
||||
this.lastScanTime = lastScanTime;
|
||||
}
|
||||
|
||||
public boolean accept(File pathname) {
|
||||
return (pathname.lastModified() > lastScanTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void timeToStop() {
|
||||
timeToStop = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,60 +1,60 @@
|
||||
/*
|
||||
* 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.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springsource.loaded.FileChangeListener;
|
||||
import org.springsource.loaded.GlobalConfiguration;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class ReloadableFileChangeListener implements FileChangeListener {
|
||||
|
||||
private static Logger log = Logger.getLogger(ReloadableFileChangeListener.class.getName());
|
||||
|
||||
private TypeRegistry typeRegistry;
|
||||
|
||||
private Map<File, ReloadableType> correspondingReloadableTypes = new HashMap<File, ReloadableType>();
|
||||
|
||||
public ReloadableFileChangeListener(TypeRegistry typeRegistry) {
|
||||
this.typeRegistry = typeRegistry;
|
||||
}
|
||||
|
||||
public void fileChanged(File file) {
|
||||
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
|
||||
log.info(" processing change for " + file);
|
||||
}
|
||||
ReloadableType rtype = correspondingReloadableTypes.get(file);
|
||||
typeRegistry.loadNewVersion(rtype, file);
|
||||
}
|
||||
|
||||
public void register(ReloadableType rtype, File file) {
|
||||
correspondingReloadableTypes.put(file, rtype);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springsource.loaded.FileChangeListener;
|
||||
import org.springsource.loaded.GlobalConfiguration;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class ReloadableFileChangeListener implements FileChangeListener {
|
||||
|
||||
private static Logger log = Logger.getLogger(ReloadableFileChangeListener.class.getName());
|
||||
|
||||
private TypeRegistry typeRegistry;
|
||||
|
||||
private Map<File, ReloadableType> correspondingReloadableTypes = new HashMap<File, ReloadableType>();
|
||||
|
||||
public ReloadableFileChangeListener(TypeRegistry typeRegistry) {
|
||||
this.typeRegistry = typeRegistry;
|
||||
}
|
||||
|
||||
public void fileChanged(File file) {
|
||||
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
|
||||
log.info(" processing change for " + file);
|
||||
}
|
||||
ReloadableType rtype = correspondingReloadableTypes.get(file);
|
||||
typeRegistry.loadNewVersion(rtype, file);
|
||||
}
|
||||
|
||||
public void register(ReloadableType rtype, File file) {
|
||||
correspondingReloadableTypes.put(file, rtype);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,62 +1,62 @@
|
||||
/*
|
||||
* Copyright 2010-2014 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.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
|
||||
/**
|
||||
* Basic agent implementation. This agent is declared in the META-INF/MANIFEST.MF file - that is how it is 'plugged in'
|
||||
* to the JVM when '-javaagent:springloaded.jar' is used.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class SpringLoadedAgent {
|
||||
|
||||
private static ClassFileTransformer transformer = new ClassPreProcessorAgentAdapter();
|
||||
|
||||
private static Instrumentation instrumentation;
|
||||
|
||||
public static void premain(String options, Instrumentation inst) {
|
||||
// Handle duplicate agents
|
||||
if (instrumentation != null) {
|
||||
return;
|
||||
}
|
||||
instrumentation = inst;
|
||||
instrumentation.addTransformer(transformer);
|
||||
}
|
||||
|
||||
public static void agentmain(String options, Instrumentation inst) {
|
||||
if (instrumentation != null) {
|
||||
return;
|
||||
}
|
||||
instrumentation = inst;
|
||||
instrumentation.addTransformer(transformer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the Instrumentation instance
|
||||
*/
|
||||
public static Instrumentation getInstrumentation() {
|
||||
if (instrumentation == null) {
|
||||
throw new UnsupportedOperationException("Java 5 was not started with preMain -javaagent for SpringLoaded");
|
||||
}
|
||||
return instrumentation;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2010-2014 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.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
|
||||
/**
|
||||
* Basic agent implementation. This agent is declared in the META-INF/MANIFEST.MF file - that is how it is 'plugged in'
|
||||
* to the JVM when '-javaagent:springloaded.jar' is used.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class SpringLoadedAgent {
|
||||
|
||||
private static ClassFileTransformer transformer = new ClassPreProcessorAgentAdapter();
|
||||
|
||||
private static Instrumentation instrumentation;
|
||||
|
||||
public static void premain(String options, Instrumentation inst) {
|
||||
// Handle duplicate agents
|
||||
if (instrumentation != null) {
|
||||
return;
|
||||
}
|
||||
instrumentation = inst;
|
||||
instrumentation.addTransformer(transformer);
|
||||
}
|
||||
|
||||
public static void agentmain(String options, Instrumentation inst) {
|
||||
if (instrumentation != null) {
|
||||
return;
|
||||
}
|
||||
instrumentation = inst;
|
||||
instrumentation.addTransformer(transformer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the Instrumentation instance
|
||||
*/
|
||||
public static Instrumentation getInstrumentation() {
|
||||
if (instrumentation == null) {
|
||||
throw new UnsupportedOperationException("Java 5 was not started with preMain -javaagent for SpringLoaded");
|
||||
}
|
||||
return instrumentation;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,281 +1,281 @@
|
||||
/*
|
||||
* 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.test.infra;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.springsource.loaded.Utils;
|
||||
|
||||
|
||||
/**
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class ClassPrinter extends ClassVisitor implements Opcodes {
|
||||
|
||||
private PrintStream destination;
|
||||
|
||||
private boolean includeBytecode;
|
||||
|
||||
private int includeFlags = 0x0000;
|
||||
|
||||
public final static int INCLUDE_BYTECODE = 0x0001;
|
||||
|
||||
public final static int INCLUDE_LINE_NUMBERS = 0x0002;
|
||||
|
||||
public static void main(String[] argv) throws Exception {
|
||||
ClassReader reader = new ClassReader(Utils.loadBytesFromStream(new FileInputStream(new File(argv[0]))));
|
||||
reader.accept(new ClassPrinter(System.out, INCLUDE_BYTECODE), 0);
|
||||
}
|
||||
|
||||
public ClassPrinter(PrintStream destination) {
|
||||
this(destination, INCLUDE_BYTECODE);
|
||||
}
|
||||
|
||||
public ClassPrinter(PrintStream destination, int includeFlags) {
|
||||
super(ASM5);
|
||||
this.destination = destination;
|
||||
this.includeFlags = includeFlags;
|
||||
}
|
||||
|
||||
public static void print(String message, byte[] bytes) {
|
||||
System.out.println(message);
|
||||
print(bytes, true);
|
||||
}
|
||||
|
||||
public static void print(byte[] bytes) {
|
||||
print(bytes, true);
|
||||
}
|
||||
|
||||
public static void print(byte[] bytes, int includeFlags) {
|
||||
ClassReader reader = new ClassReader(bytes);
|
||||
reader.accept(new ClassPrinter(System.out, includeFlags), 0);
|
||||
}
|
||||
|
||||
public static void print(byte[] bytes, boolean includeBytecode) {
|
||||
ClassReader reader = new ClassReader(bytes);
|
||||
reader.accept(new ClassPrinter(System.out, includeBytecode ? INCLUDE_BYTECODE : 0), 0);
|
||||
}
|
||||
|
||||
public static void print(PrintStream printStream, byte[] bytes, boolean includeBytecode) {
|
||||
ClassReader reader = new ClassReader(bytes);
|
||||
reader.accept(new ClassPrinter(printStream, includeBytecode ? INCLUDE_BYTECODE : 0), 0);
|
||||
}
|
||||
|
||||
public static void print(String message, byte[] bytes, boolean includeBytecode) {
|
||||
System.out.println(message);
|
||||
print(bytes, includeBytecode);
|
||||
}
|
||||
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
destination.println("CLASS: " + name + " v" + Integer.toString(version) + " " + toHex(access, 4) + "("
|
||||
+ toAccessForClass(access) + ") super " + superName
|
||||
+ (interfaces == null || interfaces.length == 0 ? "" : " interfaces" + toString(interfaces)));
|
||||
}
|
||||
|
||||
private String toString(Object[] os) {
|
||||
if (os == null) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Object o : os) {
|
||||
sb.append(o).append(" ");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String toAccessForClass(int flags) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if ((flags & Opcodes.ACC_PUBLIC) != 0) {
|
||||
sb.append("public ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_PRIVATE) != 0) {
|
||||
sb.append("private ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_PROTECTED) != 0) {
|
||||
sb.append("protected ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_STATIC) != 0) {
|
||||
sb.append("static ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_FINAL) != 0) {
|
||||
sb.append("final ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_SYNCHRONIZED) != 0) {
|
||||
sb.append("synchronized ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_BRIDGE) != 0) {
|
||||
sb.append("bridge ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_VARARGS) != 0) {
|
||||
sb.append("varargs ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_NATIVE) != 0) {
|
||||
sb.append("native ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_ABSTRACT) != 0) {
|
||||
sb.append("abstract ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_SYNTHETIC) != 0) {
|
||||
sb.append("synthetic ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_DEPRECATED) != 0) {
|
||||
sb.append("deprecated ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_INTERFACE) != 0) {
|
||||
sb.append("interface ");
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
public static String toAccessForMember(int flags) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if ((flags & Opcodes.ACC_PUBLIC) != 0) {
|
||||
sb.append("public ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_PRIVATE) != 0) {
|
||||
sb.append("private ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_STATIC) != 0) {
|
||||
sb.append("static ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_PROTECTED) != 0) {
|
||||
sb.append("protected ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_FINAL) != 0) {
|
||||
sb.append("final ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_SUPER) != 0) {
|
||||
sb.append("super ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_INTERFACE) != 0) {
|
||||
sb.append("interface ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_ABSTRACT) != 0) {
|
||||
sb.append("abstract ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_SYNTHETIC) != 0) {
|
||||
sb.append("synthetic ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_ANNOTATION) != 0) {
|
||||
sb.append("annotation ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_ENUM) != 0) {
|
||||
sb.append("enum ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_DEPRECATED) != 0) {
|
||||
sb.append("deprecated ");
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
private String toHex(int i, int len) {
|
||||
StringBuilder sb = new StringBuilder("00000000");
|
||||
sb.append(Integer.toHexString(i));
|
||||
return "0x" + sb.substring(sb.length() - len);
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
destination.print("ANNOTATION " + desc + " vis?" + visible + " VALUE ");
|
||||
return new AnnotationVisitorPrinter();
|
||||
}
|
||||
|
||||
class AnnotationVisitorPrinter extends AnnotationVisitor {
|
||||
|
||||
public AnnotationVisitorPrinter() {
|
||||
super(ASM5);
|
||||
}
|
||||
|
||||
public void visit(String name, Object value) {
|
||||
destination.print(name + "=" + value + " ");
|
||||
}
|
||||
|
||||
public void visitEnum(String name, String desc, String value) {
|
||||
destination.print(name + "=" + desc + "." + value + " ");
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String name, String desc) {
|
||||
destination.print(name + "=" + desc + " ");
|
||||
return new AnnotationVisitorPrinter();
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitArray(String name) {
|
||||
destination.print(name + " ");
|
||||
return new AnnotationVisitorPrinter();
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
destination.println();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void visitAttribute(Attribute attr) {
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
destination.println();
|
||||
}
|
||||
|
||||
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("FIELD " + toHex(access, 4) + "(" + toAccessForMember(access) + ") " + name + " " + desc
|
||||
+ (signature != null ? " " + signature : ""));
|
||||
destination.println(sb.toString().trim());
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
||||
destination.println("INNERCLASS: " + name + " " + outerName + " " + innerName + " " + access);
|
||||
}
|
||||
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append("METHOD: " + toHex(access, 4) + "(" + toAccessForMember(access) + ") " + name + desc + " "
|
||||
+ fromArray(exceptions));
|
||||
destination.println(s.toString().trim());
|
||||
return (includeFlags & INCLUDE_BYTECODE) != 0 ? new MethodPrinter(destination, includeFlags) : null;
|
||||
}
|
||||
|
||||
private String fromArray(Object[] os) {
|
||||
if (os == null) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Object o : os) {
|
||||
sb.append(o).append(" ");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void visitOuterClass(String owner, String name, String desc) {
|
||||
destination.println("OUTERCLASS: " + owner + " " + name + " " + desc);
|
||||
}
|
||||
|
||||
public void visitSource(String source, String debug) {
|
||||
destination.println("SOURCE: " + source + " " + debug);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.test.infra;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.springsource.loaded.Utils;
|
||||
|
||||
|
||||
/**
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class ClassPrinter extends ClassVisitor implements Opcodes {
|
||||
|
||||
private PrintStream destination;
|
||||
|
||||
private boolean includeBytecode;
|
||||
|
||||
private int includeFlags = 0x0000;
|
||||
|
||||
public final static int INCLUDE_BYTECODE = 0x0001;
|
||||
|
||||
public final static int INCLUDE_LINE_NUMBERS = 0x0002;
|
||||
|
||||
public static void main(String[] argv) throws Exception {
|
||||
ClassReader reader = new ClassReader(Utils.loadBytesFromStream(new FileInputStream(new File(argv[0]))));
|
||||
reader.accept(new ClassPrinter(System.out, INCLUDE_BYTECODE), 0);
|
||||
}
|
||||
|
||||
public ClassPrinter(PrintStream destination) {
|
||||
this(destination, INCLUDE_BYTECODE);
|
||||
}
|
||||
|
||||
public ClassPrinter(PrintStream destination, int includeFlags) {
|
||||
super(ASM5);
|
||||
this.destination = destination;
|
||||
this.includeFlags = includeFlags;
|
||||
}
|
||||
|
||||
public static void print(String message, byte[] bytes) {
|
||||
System.out.println(message);
|
||||
print(bytes, true);
|
||||
}
|
||||
|
||||
public static void print(byte[] bytes) {
|
||||
print(bytes, true);
|
||||
}
|
||||
|
||||
public static void print(byte[] bytes, int includeFlags) {
|
||||
ClassReader reader = new ClassReader(bytes);
|
||||
reader.accept(new ClassPrinter(System.out, includeFlags), 0);
|
||||
}
|
||||
|
||||
public static void print(byte[] bytes, boolean includeBytecode) {
|
||||
ClassReader reader = new ClassReader(bytes);
|
||||
reader.accept(new ClassPrinter(System.out, includeBytecode ? INCLUDE_BYTECODE : 0), 0);
|
||||
}
|
||||
|
||||
public static void print(PrintStream printStream, byte[] bytes, boolean includeBytecode) {
|
||||
ClassReader reader = new ClassReader(bytes);
|
||||
reader.accept(new ClassPrinter(printStream, includeBytecode ? INCLUDE_BYTECODE : 0), 0);
|
||||
}
|
||||
|
||||
public static void print(String message, byte[] bytes, boolean includeBytecode) {
|
||||
System.out.println(message);
|
||||
print(bytes, includeBytecode);
|
||||
}
|
||||
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
destination.println("CLASS: " + name + " v" + Integer.toString(version) + " " + toHex(access, 4) + "("
|
||||
+ toAccessForClass(access) + ") super " + superName
|
||||
+ (interfaces == null || interfaces.length == 0 ? "" : " interfaces" + toString(interfaces)));
|
||||
}
|
||||
|
||||
private String toString(Object[] os) {
|
||||
if (os == null) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Object o : os) {
|
||||
sb.append(o).append(" ");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String toAccessForClass(int flags) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if ((flags & Opcodes.ACC_PUBLIC) != 0) {
|
||||
sb.append("public ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_PRIVATE) != 0) {
|
||||
sb.append("private ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_PROTECTED) != 0) {
|
||||
sb.append("protected ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_STATIC) != 0) {
|
||||
sb.append("static ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_FINAL) != 0) {
|
||||
sb.append("final ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_SYNCHRONIZED) != 0) {
|
||||
sb.append("synchronized ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_BRIDGE) != 0) {
|
||||
sb.append("bridge ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_VARARGS) != 0) {
|
||||
sb.append("varargs ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_NATIVE) != 0) {
|
||||
sb.append("native ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_ABSTRACT) != 0) {
|
||||
sb.append("abstract ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_SYNTHETIC) != 0) {
|
||||
sb.append("synthetic ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_DEPRECATED) != 0) {
|
||||
sb.append("deprecated ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_INTERFACE) != 0) {
|
||||
sb.append("interface ");
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
public static String toAccessForMember(int flags) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if ((flags & Opcodes.ACC_PUBLIC) != 0) {
|
||||
sb.append("public ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_PRIVATE) != 0) {
|
||||
sb.append("private ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_STATIC) != 0) {
|
||||
sb.append("static ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_PROTECTED) != 0) {
|
||||
sb.append("protected ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_FINAL) != 0) {
|
||||
sb.append("final ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_SUPER) != 0) {
|
||||
sb.append("super ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_INTERFACE) != 0) {
|
||||
sb.append("interface ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_ABSTRACT) != 0) {
|
||||
sb.append("abstract ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_SYNTHETIC) != 0) {
|
||||
sb.append("synthetic ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_ANNOTATION) != 0) {
|
||||
sb.append("annotation ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_ENUM) != 0) {
|
||||
sb.append("enum ");
|
||||
}
|
||||
if ((flags & Opcodes.ACC_DEPRECATED) != 0) {
|
||||
sb.append("deprecated ");
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
private String toHex(int i, int len) {
|
||||
StringBuilder sb = new StringBuilder("00000000");
|
||||
sb.append(Integer.toHexString(i));
|
||||
return "0x" + sb.substring(sb.length() - len);
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
destination.print("ANNOTATION " + desc + " vis?" + visible + " VALUE ");
|
||||
return new AnnotationVisitorPrinter();
|
||||
}
|
||||
|
||||
class AnnotationVisitorPrinter extends AnnotationVisitor {
|
||||
|
||||
public AnnotationVisitorPrinter() {
|
||||
super(ASM5);
|
||||
}
|
||||
|
||||
public void visit(String name, Object value) {
|
||||
destination.print(name + "=" + value + " ");
|
||||
}
|
||||
|
||||
public void visitEnum(String name, String desc, String value) {
|
||||
destination.print(name + "=" + desc + "." + value + " ");
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String name, String desc) {
|
||||
destination.print(name + "=" + desc + " ");
|
||||
return new AnnotationVisitorPrinter();
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitArray(String name) {
|
||||
destination.print(name + " ");
|
||||
return new AnnotationVisitorPrinter();
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
destination.println();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void visitAttribute(Attribute attr) {
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
destination.println();
|
||||
}
|
||||
|
||||
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("FIELD " + toHex(access, 4) + "(" + toAccessForMember(access) + ") " + name + " " + desc
|
||||
+ (signature != null ? " " + signature : ""));
|
||||
destination.println(sb.toString().trim());
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
||||
destination.println("INNERCLASS: " + name + " " + outerName + " " + innerName + " " + access);
|
||||
}
|
||||
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append("METHOD: " + toHex(access, 4) + "(" + toAccessForMember(access) + ") " + name + desc + " "
|
||||
+ fromArray(exceptions));
|
||||
destination.println(s.toString().trim());
|
||||
return (includeFlags & INCLUDE_BYTECODE) != 0 ? new MethodPrinter(destination, includeFlags) : null;
|
||||
}
|
||||
|
||||
private String fromArray(Object[] os) {
|
||||
if (os == null) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Object o : os) {
|
||||
sb.append(o).append(" ");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void visitOuterClass(String owner, String name, String desc) {
|
||||
destination.println("OUTERCLASS: " + owner + " " + name + " " + desc);
|
||||
}
|
||||
|
||||
public void visitSource(String source, String debug) {
|
||||
destination.println("SOURCE: " + source + " " + debug);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,262 +1,262 @@
|
||||
/*
|
||||
* 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.test.infra;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.Handle;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.springsource.loaded.Utils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class MethodPrinter extends MethodVisitor implements Opcodes {
|
||||
|
||||
PrintStream to;
|
||||
|
||||
int includeFlags;
|
||||
|
||||
List<Label> labels = new ArrayList<Label>();
|
||||
|
||||
private String toString(Label label) {
|
||||
int idx = labels.indexOf(label);
|
||||
if (idx != -1) {
|
||||
return "L" + idx;
|
||||
}
|
||||
labels.add(label);
|
||||
return "L" + labels.indexOf(label);
|
||||
}
|
||||
|
||||
public MethodPrinter(PrintStream destination) {
|
||||
this(destination, 0);
|
||||
}
|
||||
|
||||
public MethodPrinter(PrintStream destination, int includeFlags) {
|
||||
super(ASM5);
|
||||
this.includeFlags = includeFlags;
|
||||
this.to = destination;
|
||||
}
|
||||
|
||||
public void visitCode() {
|
||||
to.print(" CODE\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
|
||||
to.println(" INVOKEDYNAMIC " + name + "." + desc + " bsm=" + toString(bsm));
|
||||
}
|
||||
|
||||
private String toString(Handle bsm) {
|
||||
return "#" + bsm.getTag() + " " + bsm.getOwner() + "." + bsm.getName() + bsm.getDesc();
|
||||
}
|
||||
|
||||
// TODO include 'itf' flag in output (maybe only if true)
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
|
||||
if (opcode == Opcodes.INVOKESTATIC) {
|
||||
to.println(" INVOKESTATIC " + owner + "." + name + desc);
|
||||
}
|
||||
else if (opcode == Opcodes.INVOKESPECIAL) {
|
||||
to.println(" INVOKESPECIAL " + owner + "." + name + desc);
|
||||
}
|
||||
else if (opcode == Opcodes.INVOKEVIRTUAL) {
|
||||
to.println(" INVOKEVIRTUAL " + owner + "." + name + desc);
|
||||
}
|
||||
else if (opcode == Opcodes.INVOKEINTERFACE) {
|
||||
to.println(" INVOKEINTERFACE " + owner + "." + name + desc);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(":" + opcode);
|
||||
}
|
||||
}
|
||||
|
||||
// -- pas de implemented
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
to.print("ANNOTATION " + desc + " vis?" + visible + " VALUE ");
|
||||
return new AnnotationVisitorPrinter();
|
||||
}
|
||||
|
||||
class AnnotationVisitorPrinter extends AnnotationVisitor {
|
||||
|
||||
public AnnotationVisitorPrinter() {
|
||||
super(ASM5);
|
||||
}
|
||||
|
||||
public void visit(String name, Object value) {
|
||||
to.print(name + "=" + value + " ");
|
||||
}
|
||||
|
||||
public void visitEnum(String name, String desc, String value) {
|
||||
to.print(name + "=" + desc + "." + value + " ");
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String name, String desc) {
|
||||
to.print(name + "=" + desc + " ");
|
||||
return new AnnotationVisitorPrinter();
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitArray(String name) {
|
||||
to.print(name + " ");
|
||||
return new AnnotationVisitorPrinter();
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
to.println();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotationDefault() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitAttribute(Attribute attr) {
|
||||
to.println(" ATTRIBUTE: " + attr);
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
}
|
||||
|
||||
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
|
||||
if (opcode == Opcodes.GETSTATIC) {
|
||||
to.println(" GETSTATIC " + owner + "." + name + " " + desc);
|
||||
}
|
||||
else if (opcode == Opcodes.PUTSTATIC) {
|
||||
to.println(" PUTSTATIC " + owner + "." + name + " " + desc);
|
||||
}
|
||||
else if (opcode == Opcodes.GETFIELD) {
|
||||
to.println(" GETFIELD " + owner + "." + name + " " + desc);
|
||||
}
|
||||
else if (opcode == Opcodes.PUTFIELD) {
|
||||
to.println(" PUTFIELD " + owner + "." + name + " " + desc);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(":" + opcode);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
|
||||
}
|
||||
|
||||
public void visitIincInsn(int var, int increment) {
|
||||
}
|
||||
|
||||
public void visitInsn(int opcode) {
|
||||
to.println(" " + Utils.toOpcodeString(opcode));
|
||||
}
|
||||
|
||||
public void visitIntInsn(int opcode, int operand) {
|
||||
to.println(" " + Utils.toOpcodeString(opcode) + " " + operand);
|
||||
}
|
||||
|
||||
public void visitJumpInsn(int opcode, Label label) {
|
||||
to.println(" " + Utils.toOpcodeString(opcode) + " " + toString(label));
|
||||
}
|
||||
|
||||
public void visitLabel(Label label) {
|
||||
to.println(" " + toString(label));
|
||||
}
|
||||
|
||||
public void visitLdcInsn(Object cst) {
|
||||
to.println(" LDC " + cst);
|
||||
}
|
||||
|
||||
public void visitLineNumber(int line, Label start) {
|
||||
if ((includeFlags & ClassPrinter.INCLUDE_LINE_NUMBERS) != 0) {
|
||||
to.println(" LINE:" + line);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
|
||||
}
|
||||
|
||||
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
|
||||
}
|
||||
|
||||
public void visitMaxs(int maxStack, int maxLocals) {
|
||||
}
|
||||
|
||||
public void visitMultiANewArrayInsn(String desc, int dims) {
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
|
||||
}
|
||||
|
||||
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
|
||||
}
|
||||
|
||||
public void visitTypeInsn(int opcode, String type) {
|
||||
if (opcode == Opcodes.NEW) { // 187
|
||||
to.println(" NEW " + type);
|
||||
}
|
||||
else if (opcode == Opcodes.ANEWARRAY) { // 189
|
||||
to.println(" ANEWARRAY " + type);
|
||||
}
|
||||
else if (opcode == Opcodes.CHECKCAST) { // 192
|
||||
to.println(" CHECKCAST " + type);
|
||||
}
|
||||
else if (opcode == Opcodes.INSTANCEOF) { // 193
|
||||
to.println(" INSTANCEOF " + type);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(":" + opcode);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitVarInsn(int opcode, int var) {
|
||||
if (opcode == Opcodes.ALOAD) {
|
||||
to.println(" ALOAD " + var);
|
||||
}
|
||||
else if (opcode == Opcodes.ASTORE) {
|
||||
to.println(" ASTORE " + var);
|
||||
}
|
||||
else if (opcode == Opcodes.ILOAD) {
|
||||
to.println(" ILOAD " + var);
|
||||
}
|
||||
else if (opcode == FLOAD) {
|
||||
to.println(" FLOAD " + var);
|
||||
}
|
||||
else if (opcode == LLOAD) {
|
||||
to.println(" LLOAD " + var);
|
||||
}
|
||||
else if (opcode == DLOAD) {
|
||||
to.println(" DLOAD " + var);
|
||||
}
|
||||
else if (opcode == ISTORE) {
|
||||
to.println(" ISTORE " + var);
|
||||
}
|
||||
else if (opcode == LSTORE) {
|
||||
to.println(" LSTORE " + var);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(":" + opcode);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.test.infra;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.Handle;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.springsource.loaded.Utils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class MethodPrinter extends MethodVisitor implements Opcodes {
|
||||
|
||||
PrintStream to;
|
||||
|
||||
int includeFlags;
|
||||
|
||||
List<Label> labels = new ArrayList<Label>();
|
||||
|
||||
private String toString(Label label) {
|
||||
int idx = labels.indexOf(label);
|
||||
if (idx != -1) {
|
||||
return "L" + idx;
|
||||
}
|
||||
labels.add(label);
|
||||
return "L" + labels.indexOf(label);
|
||||
}
|
||||
|
||||
public MethodPrinter(PrintStream destination) {
|
||||
this(destination, 0);
|
||||
}
|
||||
|
||||
public MethodPrinter(PrintStream destination, int includeFlags) {
|
||||
super(ASM5);
|
||||
this.includeFlags = includeFlags;
|
||||
this.to = destination;
|
||||
}
|
||||
|
||||
public void visitCode() {
|
||||
to.print(" CODE\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
|
||||
to.println(" INVOKEDYNAMIC " + name + "." + desc + " bsm=" + toString(bsm));
|
||||
}
|
||||
|
||||
private String toString(Handle bsm) {
|
||||
return "#" + bsm.getTag() + " " + bsm.getOwner() + "." + bsm.getName() + bsm.getDesc();
|
||||
}
|
||||
|
||||
// TODO include 'itf' flag in output (maybe only if true)
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
|
||||
if (opcode == Opcodes.INVOKESTATIC) {
|
||||
to.println(" INVOKESTATIC " + owner + "." + name + desc);
|
||||
}
|
||||
else if (opcode == Opcodes.INVOKESPECIAL) {
|
||||
to.println(" INVOKESPECIAL " + owner + "." + name + desc);
|
||||
}
|
||||
else if (opcode == Opcodes.INVOKEVIRTUAL) {
|
||||
to.println(" INVOKEVIRTUAL " + owner + "." + name + desc);
|
||||
}
|
||||
else if (opcode == Opcodes.INVOKEINTERFACE) {
|
||||
to.println(" INVOKEINTERFACE " + owner + "." + name + desc);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(":" + opcode);
|
||||
}
|
||||
}
|
||||
|
||||
// -- pas de implemented
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
to.print("ANNOTATION " + desc + " vis?" + visible + " VALUE ");
|
||||
return new AnnotationVisitorPrinter();
|
||||
}
|
||||
|
||||
class AnnotationVisitorPrinter extends AnnotationVisitor {
|
||||
|
||||
public AnnotationVisitorPrinter() {
|
||||
super(ASM5);
|
||||
}
|
||||
|
||||
public void visit(String name, Object value) {
|
||||
to.print(name + "=" + value + " ");
|
||||
}
|
||||
|
||||
public void visitEnum(String name, String desc, String value) {
|
||||
to.print(name + "=" + desc + "." + value + " ");
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String name, String desc) {
|
||||
to.print(name + "=" + desc + " ");
|
||||
return new AnnotationVisitorPrinter();
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitArray(String name) {
|
||||
to.print(name + " ");
|
||||
return new AnnotationVisitorPrinter();
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
to.println();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotationDefault() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitAttribute(Attribute attr) {
|
||||
to.println(" ATTRIBUTE: " + attr);
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
}
|
||||
|
||||
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
|
||||
if (opcode == Opcodes.GETSTATIC) {
|
||||
to.println(" GETSTATIC " + owner + "." + name + " " + desc);
|
||||
}
|
||||
else if (opcode == Opcodes.PUTSTATIC) {
|
||||
to.println(" PUTSTATIC " + owner + "." + name + " " + desc);
|
||||
}
|
||||
else if (opcode == Opcodes.GETFIELD) {
|
||||
to.println(" GETFIELD " + owner + "." + name + " " + desc);
|
||||
}
|
||||
else if (opcode == Opcodes.PUTFIELD) {
|
||||
to.println(" PUTFIELD " + owner + "." + name + " " + desc);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(":" + opcode);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
|
||||
}
|
||||
|
||||
public void visitIincInsn(int var, int increment) {
|
||||
}
|
||||
|
||||
public void visitInsn(int opcode) {
|
||||
to.println(" " + Utils.toOpcodeString(opcode));
|
||||
}
|
||||
|
||||
public void visitIntInsn(int opcode, int operand) {
|
||||
to.println(" " + Utils.toOpcodeString(opcode) + " " + operand);
|
||||
}
|
||||
|
||||
public void visitJumpInsn(int opcode, Label label) {
|
||||
to.println(" " + Utils.toOpcodeString(opcode) + " " + toString(label));
|
||||
}
|
||||
|
||||
public void visitLabel(Label label) {
|
||||
to.println(" " + toString(label));
|
||||
}
|
||||
|
||||
public void visitLdcInsn(Object cst) {
|
||||
to.println(" LDC " + cst);
|
||||
}
|
||||
|
||||
public void visitLineNumber(int line, Label start) {
|
||||
if ((includeFlags & ClassPrinter.INCLUDE_LINE_NUMBERS) != 0) {
|
||||
to.println(" LINE:" + line);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
|
||||
}
|
||||
|
||||
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
|
||||
}
|
||||
|
||||
public void visitMaxs(int maxStack, int maxLocals) {
|
||||
}
|
||||
|
||||
public void visitMultiANewArrayInsn(String desc, int dims) {
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
|
||||
}
|
||||
|
||||
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
|
||||
}
|
||||
|
||||
public void visitTypeInsn(int opcode, String type) {
|
||||
if (opcode == Opcodes.NEW) { // 187
|
||||
to.println(" NEW " + type);
|
||||
}
|
||||
else if (opcode == Opcodes.ANEWARRAY) { // 189
|
||||
to.println(" ANEWARRAY " + type);
|
||||
}
|
||||
else if (opcode == Opcodes.CHECKCAST) { // 192
|
||||
to.println(" CHECKCAST " + type);
|
||||
}
|
||||
else if (opcode == Opcodes.INSTANCEOF) { // 193
|
||||
to.println(" INSTANCEOF " + type);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(":" + opcode);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitVarInsn(int opcode, int var) {
|
||||
if (opcode == Opcodes.ALOAD) {
|
||||
to.println(" ALOAD " + var);
|
||||
}
|
||||
else if (opcode == Opcodes.ASTORE) {
|
||||
to.println(" ASTORE " + var);
|
||||
}
|
||||
else if (opcode == Opcodes.ILOAD) {
|
||||
to.println(" ILOAD " + var);
|
||||
}
|
||||
else if (opcode == FLOAD) {
|
||||
to.println(" FLOAD " + var);
|
||||
}
|
||||
else if (opcode == LLOAD) {
|
||||
to.println(" LLOAD " + var);
|
||||
}
|
||||
else if (opcode == DLOAD) {
|
||||
to.println(" DLOAD " + var);
|
||||
}
|
||||
else if (opcode == ISTORE) {
|
||||
to.println(" ISTORE " + var);
|
||||
}
|
||||
else if (opcode == LSTORE) {
|
||||
to.println(" LSTORE " + var);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(":" + opcode);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.ClassRenamer;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for renaming of a class.
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class ClassRenamerTests extends SpringLoadedTests {
|
||||
|
||||
/**
|
||||
* Load the byteform of a class, manipulate the bytes to rename it, then try and define it and use it under the new
|
||||
* name
|
||||
*/
|
||||
@Test
|
||||
public void simpleRename() {
|
||||
byte[] classbytes = loadBytesForClass("data.Fruity002");
|
||||
byte[] renamedbytes = ClassRenamer.rename("data.Fruity", classbytes);
|
||||
Class<?> clazz = loadit("data.Fruity", renamedbytes);
|
||||
Object value = run(clazz, "getFruit");
|
||||
Assert.assertEquals("orange", value);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.ClassRenamer;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for renaming of a class.
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class ClassRenamerTests extends SpringLoadedTests {
|
||||
|
||||
/**
|
||||
* Load the byteform of a class, manipulate the bytes to rename it, then try and define it and use it under the new
|
||||
* name
|
||||
*/
|
||||
@Test
|
||||
public void simpleRename() {
|
||||
byte[] classbytes = loadBytesForClass("data.Fruity002");
|
||||
byte[] renamedbytes = ClassRenamer.rename("data.Fruity", classbytes);
|
||||
Class<?> clazz = loadit("data.Fruity", renamedbytes);
|
||||
Object value = run(clazz, "getFruit");
|
||||
Assert.assertEquals("orange", value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
|
||||
|
||||
/**
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class DebuggingTests extends SpringLoadedTests {
|
||||
|
||||
@Test
|
||||
public void rewrite() throws Exception {
|
||||
TypeRegistry typeRegistry = getTypeRegistry("data.HelloWorld");
|
||||
ReloadableType rtype = typeRegistry.addType("data.HelloWorld", loadBytesForClass("data.HelloWorld"));
|
||||
runUnguarded(rtype.getClazz(), "greet");
|
||||
|
||||
// Just transform the existing version into a dispatcher/executor
|
||||
rtype.loadNewVersion("000", rtype.bytesInitial);
|
||||
Assert.assertEquals("Greet from HelloWorld", runUnguarded(rtype.getClazz(), "greet").stdout);
|
||||
|
||||
// Load a real new version
|
||||
rtype.loadNewVersion("002", retrieveRename("data.HelloWorld", "data.HelloWorld002"));
|
||||
Assert.assertEquals("Greet from HelloWorld 2", runUnguarded(rtype.getClazz(), "greet").stdout);
|
||||
// ClassPrinter.print(rtype.getLatestExecutorBytes());
|
||||
// ClassPrinter.print(rtype.bytesInitial);
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
|
||||
|
||||
/**
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class DebuggingTests extends SpringLoadedTests {
|
||||
|
||||
@Test
|
||||
public void rewrite() throws Exception {
|
||||
TypeRegistry typeRegistry = getTypeRegistry("data.HelloWorld");
|
||||
ReloadableType rtype = typeRegistry.addType("data.HelloWorld", loadBytesForClass("data.HelloWorld"));
|
||||
runUnguarded(rtype.getClazz(), "greet");
|
||||
|
||||
// Just transform the existing version into a dispatcher/executor
|
||||
rtype.loadNewVersion("000", rtype.bytesInitial);
|
||||
Assert.assertEquals("Greet from HelloWorld", runUnguarded(rtype.getClazz(), "greet").stdout);
|
||||
|
||||
// Load a real new version
|
||||
rtype.loadNewVersion("002", retrieveRename("data.HelloWorld", "data.HelloWorld002"));
|
||||
Assert.assertEquals("Greet from HelloWorld 2", runUnguarded(rtype.getClazz(), "greet").stdout);
|
||||
// ClassPrinter.print(rtype.getLatestExecutorBytes());
|
||||
// ClassPrinter.print(rtype.bytesInitial);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,154 +1,154 @@
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.MethodInvokerRewriter;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
import org.springsource.loaded.test.infra.Result;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for creation of the dispatcher instances that forward to the executors
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class DispatcherBuilderTests extends SpringLoadedTests {
|
||||
|
||||
@Test
|
||||
public void reload() throws Exception {
|
||||
String tclass = "builder.DispatcherTestOne";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(tclass);
|
||||
|
||||
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
|
||||
|
||||
// Simply reloads itself to trigger new version handling code paths in both infrastructure and generated code
|
||||
rtype.loadNewVersion("2", rtype.bytesInitial);
|
||||
|
||||
// if we made it here, hurrah, we didn't crash - let's call that success!
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticMethod() throws Exception {
|
||||
String tclass = "dispatcher.Staticmethod";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(tclass);
|
||||
|
||||
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
|
||||
|
||||
// Simply reloads itself to trigger new version handling code paths in both infrastructure and generated code
|
||||
rtype.loadNewVersion("2", rtype.bytesInitial);
|
||||
// ClassPrinter.print(rtype.interfaceBytes);
|
||||
// ClassPrinter.print(rtype.getLatestExecutorBytes());
|
||||
|
||||
byte[] callerbytes = loadBytesForClass("dispatcher.StaticmethodCaller");
|
||||
byte[] rewrittenBytes = MethodInvokerRewriter.rewrite(typeRegistry, callerbytes);
|
||||
Class<?> callerClazz = loadit("dispatcher.StaticmethodCaller", rewrittenBytes);
|
||||
|
||||
result = runUnguarded(callerClazz, "run");
|
||||
Assert.assertTrue(result.stdout.indexOf("abc") != -1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reloadToString() throws Exception {
|
||||
String tclass = "builder.DispatcherTestOne";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(tclass);
|
||||
|
||||
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
|
||||
|
||||
Result r = runUnguarded(rtype.getClazz(), "toString");
|
||||
String firstToString = (String) r.returnValue; // default toString()
|
||||
// Simply reloads itself to trigger new version handling code paths in both infrastructure and generated code
|
||||
rtype.loadNewVersion("2", retrieveRename(tclass, "builder.DispatcherTestOne002"));
|
||||
|
||||
r = runUnguarded(rtype.getClazz(), "toString");
|
||||
String secondToString = (String) r.returnValue;
|
||||
Assert.assertNotSame(firstToString, secondToString);
|
||||
Assert.assertEquals("abc", secondToString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test we can differentiate between methods that would clash (static method on the target that takes the instance
|
||||
* as first parameter and an instance method) - these clash methods would normally create
|
||||
*/
|
||||
@Test
|
||||
public void callingClashingMethods() throws Exception {
|
||||
String tclass = "dispatcher.C";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(tclass);
|
||||
|
||||
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
|
||||
|
||||
rtype.loadNewVersion("2", rtype.bytesInitial);
|
||||
byte[] callerbytes = loadBytesForClass("dispatcher.CallC");
|
||||
byte[] rewrittenBytes = MethodInvokerRewriter.rewrite(typeRegistry, callerbytes);
|
||||
Class<?> callerClazz = loadit("dispatcher.CallC", rewrittenBytes);
|
||||
|
||||
result = runUnguarded(callerClazz, "runInstance");
|
||||
if (result.stdout.indexOf("instance foo running") == -1) {
|
||||
Assert.fail("Did not find 'instance foo running' in:\n" + result.stdout);
|
||||
}
|
||||
|
||||
result = runUnguarded(callerClazz, "runStatic");
|
||||
if (result.stdout.indexOf("static foo running") == -1) {
|
||||
Assert.fail("Did not find 'static foo running' in:\n" + result.stdout);
|
||||
}
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void checkDynamicDispatcher() throws Exception {
|
||||
// String tclass = "builder.DispatcherTestOne";
|
||||
// TypeRegistry typeRegistry = getTypeRegistry(tclass);
|
||||
//
|
||||
// ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
|
||||
//
|
||||
// // Load a version that has a new method in it: foo(I)Ljava/lang/String;
|
||||
// rtype.loadNewVersion("2", retrieveRename(tclass, "builder.DispatcherTestOne003"));
|
||||
//
|
||||
// // @formatter:off
|
||||
// Assert.assertEquals(
|
||||
// // Build a string consisting of the method name and descriptor:
|
||||
// " ALOAD 3\n"+
|
||||
//
|
||||
// // Is it foo(I)Ljava/lang/String; ?
|
||||
// " LDC foo(I)Ljava/lang/String;\n"+
|
||||
// " INVOKEVIRTUAL java/lang/String.equals(Ljava/lang/Object;)Z\n"+
|
||||
// " IFEQ L0\n"+
|
||||
//
|
||||
// // It is! So call the real method on the dispatcher after preparing the arguments
|
||||
// " ALOAD 2\n"+
|
||||
// " CHECKCAST builder/DispatcherTestOne\n"+
|
||||
// " ALOAD 1\n"+
|
||||
// " LDC 0\n"+
|
||||
// " AALOAD\n"+
|
||||
// " CHECKCAST java/lang/Integer\n"+
|
||||
// " INVOKEVIRTUAL java/lang/Integer.intValue()I\n"+
|
||||
// " INVOKESTATIC builder/DispatcherTestOne__E2.foo(Lbuilder/DispatcherTestOne;I)Ljava/lang/String;\n"+
|
||||
// " ARETURN\n"+
|
||||
//
|
||||
// // Is isnt! So throw an exception
|
||||
// " L0\n"+
|
||||
// " NEW java/lang/IllegalStateException\n"+
|
||||
// " DUP\n"+
|
||||
// " INVOKESPECIAL java/lang/IllegalStateException.<init>()V\n"+
|
||||
// " ATHROW\n",
|
||||
// toStringMethod(rtype.getLatestDispatcherBytes(), Constants.mDynamicDispatchName, false));
|
||||
// // @formatter:on
|
||||
// }
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.MethodInvokerRewriter;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
import org.springsource.loaded.test.infra.Result;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for creation of the dispatcher instances that forward to the executors
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class DispatcherBuilderTests extends SpringLoadedTests {
|
||||
|
||||
@Test
|
||||
public void reload() throws Exception {
|
||||
String tclass = "builder.DispatcherTestOne";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(tclass);
|
||||
|
||||
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
|
||||
|
||||
// Simply reloads itself to trigger new version handling code paths in both infrastructure and generated code
|
||||
rtype.loadNewVersion("2", rtype.bytesInitial);
|
||||
|
||||
// if we made it here, hurrah, we didn't crash - let's call that success!
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticMethod() throws Exception {
|
||||
String tclass = "dispatcher.Staticmethod";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(tclass);
|
||||
|
||||
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
|
||||
|
||||
// Simply reloads itself to trigger new version handling code paths in both infrastructure and generated code
|
||||
rtype.loadNewVersion("2", rtype.bytesInitial);
|
||||
// ClassPrinter.print(rtype.interfaceBytes);
|
||||
// ClassPrinter.print(rtype.getLatestExecutorBytes());
|
||||
|
||||
byte[] callerbytes = loadBytesForClass("dispatcher.StaticmethodCaller");
|
||||
byte[] rewrittenBytes = MethodInvokerRewriter.rewrite(typeRegistry, callerbytes);
|
||||
Class<?> callerClazz = loadit("dispatcher.StaticmethodCaller", rewrittenBytes);
|
||||
|
||||
result = runUnguarded(callerClazz, "run");
|
||||
Assert.assertTrue(result.stdout.indexOf("abc") != -1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reloadToString() throws Exception {
|
||||
String tclass = "builder.DispatcherTestOne";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(tclass);
|
||||
|
||||
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
|
||||
|
||||
Result r = runUnguarded(rtype.getClazz(), "toString");
|
||||
String firstToString = (String) r.returnValue; // default toString()
|
||||
// Simply reloads itself to trigger new version handling code paths in both infrastructure and generated code
|
||||
rtype.loadNewVersion("2", retrieveRename(tclass, "builder.DispatcherTestOne002"));
|
||||
|
||||
r = runUnguarded(rtype.getClazz(), "toString");
|
||||
String secondToString = (String) r.returnValue;
|
||||
Assert.assertNotSame(firstToString, secondToString);
|
||||
Assert.assertEquals("abc", secondToString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test we can differentiate between methods that would clash (static method on the target that takes the instance
|
||||
* as first parameter and an instance method) - these clash methods would normally create
|
||||
*/
|
||||
@Test
|
||||
public void callingClashingMethods() throws Exception {
|
||||
String tclass = "dispatcher.C";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(tclass);
|
||||
|
||||
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
|
||||
|
||||
rtype.loadNewVersion("2", rtype.bytesInitial);
|
||||
byte[] callerbytes = loadBytesForClass("dispatcher.CallC");
|
||||
byte[] rewrittenBytes = MethodInvokerRewriter.rewrite(typeRegistry, callerbytes);
|
||||
Class<?> callerClazz = loadit("dispatcher.CallC", rewrittenBytes);
|
||||
|
||||
result = runUnguarded(callerClazz, "runInstance");
|
||||
if (result.stdout.indexOf("instance foo running") == -1) {
|
||||
Assert.fail("Did not find 'instance foo running' in:\n" + result.stdout);
|
||||
}
|
||||
|
||||
result = runUnguarded(callerClazz, "runStatic");
|
||||
if (result.stdout.indexOf("static foo running") == -1) {
|
||||
Assert.fail("Did not find 'static foo running' in:\n" + result.stdout);
|
||||
}
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void checkDynamicDispatcher() throws Exception {
|
||||
// String tclass = "builder.DispatcherTestOne";
|
||||
// TypeRegistry typeRegistry = getTypeRegistry(tclass);
|
||||
//
|
||||
// ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
|
||||
//
|
||||
// // Load a version that has a new method in it: foo(I)Ljava/lang/String;
|
||||
// rtype.loadNewVersion("2", retrieveRename(tclass, "builder.DispatcherTestOne003"));
|
||||
//
|
||||
// // @formatter:off
|
||||
// Assert.assertEquals(
|
||||
// // Build a string consisting of the method name and descriptor:
|
||||
// " ALOAD 3\n"+
|
||||
//
|
||||
// // Is it foo(I)Ljava/lang/String; ?
|
||||
// " LDC foo(I)Ljava/lang/String;\n"+
|
||||
// " INVOKEVIRTUAL java/lang/String.equals(Ljava/lang/Object;)Z\n"+
|
||||
// " IFEQ L0\n"+
|
||||
//
|
||||
// // It is! So call the real method on the dispatcher after preparing the arguments
|
||||
// " ALOAD 2\n"+
|
||||
// " CHECKCAST builder/DispatcherTestOne\n"+
|
||||
// " ALOAD 1\n"+
|
||||
// " LDC 0\n"+
|
||||
// " AALOAD\n"+
|
||||
// " CHECKCAST java/lang/Integer\n"+
|
||||
// " INVOKEVIRTUAL java/lang/Integer.intValue()I\n"+
|
||||
// " INVOKESTATIC builder/DispatcherTestOne__E2.foo(Lbuilder/DispatcherTestOne;I)Ljava/lang/String;\n"+
|
||||
// " ARETURN\n"+
|
||||
//
|
||||
// // Is isnt! So throw an exception
|
||||
// " L0\n"+
|
||||
// " NEW java/lang/IllegalStateException\n"+
|
||||
// " DUP\n"+
|
||||
// " INVOKESPECIAL java/lang/IllegalStateException.<init>()V\n"+
|
||||
// " ATHROW\n",
|
||||
// toStringMethod(rtype.getLatestDispatcherBytes(), Constants.mDynamicDispatchName, false));
|
||||
// // @formatter:on
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -1,490 +1,490 @@
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
import org.springsource.loaded.Utils;
|
||||
import org.springsource.loaded.test.infra.ClassPrinter;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for creation of the executor instances that run the code
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class ExecutorBuilderTests extends SpringLoadedTests {
|
||||
|
||||
/**
|
||||
* Check properties of the newly created executor.
|
||||
*/
|
||||
@Test
|
||||
public void basicExternals() throws Exception {
|
||||
String t = "executor.TestOne";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
reload(rtype, "37");
|
||||
Class<?> clazz = rtype.getLatestExecutorClass();
|
||||
Assert.assertEquals(Utils.getExecutorName(t, "37"), clazz.getName());
|
||||
Assert.assertEquals(3, clazz.getDeclaredMethods().length);
|
||||
Assert.assertEquals(1, clazz.getDeclaredFields().length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check internal structure of the newly created executor.
|
||||
*/
|
||||
@Test
|
||||
public void basicInternalsLocalVariables() throws Exception {
|
||||
String t = "executor.TestOne";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
reload(rtype, "37");
|
||||
checkLocalVariables(rtype.getLatestExecutorBytes(), "foo(Lexecutor/TestOne;Ljava/lang/String;)J",
|
||||
"thiz:Lexecutor/TestOne;", "s:Ljava/lang/String;");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void codeStructure() throws Exception {
|
||||
String tclass = "executor.TestOne";
|
||||
|
||||
TypeRegistry typeRegistry = getTypeRegistry(tclass);
|
||||
|
||||
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
|
||||
|
||||
// Reload it (triggers creation of dispatcher/executor)
|
||||
rtype.loadNewVersion("2", rtype.bytesInitial);
|
||||
|
||||
// @formatter:off
|
||||
checkType(
|
||||
rtype.getLatestExecutorBytes(),
|
||||
"CLASS: executor/TestOne$$E2 v50 0x0001(public) super java/lang/Object\n"
|
||||
+
|
||||
"SOURCE: TestOne.java null\n"
|
||||
+
|
||||
"FIELD 0x0001(public) i I\n"
|
||||
+
|
||||
"METHOD: 0x0009(public static) ___init___(Lexecutor/TestOne;)V\n"
|
||||
+
|
||||
" CODE\n"
|
||||
+
|
||||
" L0\n"
|
||||
+
|
||||
" ALOAD 0\n"
|
||||
+
|
||||
" POP\n"
|
||||
+
|
||||
" L1\n"
|
||||
+
|
||||
" ALOAD 0\n"
|
||||
+
|
||||
" BIPUSH 101\n"
|
||||
+
|
||||
" LDC 0\n"
|
||||
+
|
||||
" LDC i\n"
|
||||
+
|
||||
" INVOKESTATIC org/springsource/loaded/TypeRegistry.instanceFieldInterceptionRequired(ILjava/lang/String;)Z\n"
|
||||
+
|
||||
" IFEQ L2\n"
|
||||
+
|
||||
" INVOKESTATIC java/lang/Integer.valueOf(I)Ljava/lang/Integer;\n"
|
||||
+
|
||||
" SWAP\n"
|
||||
+
|
||||
" DUP_X1\n"
|
||||
+
|
||||
" LDC i\n"
|
||||
+
|
||||
" INVOKESPECIAL executor/TestOne.r$set(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)V\n"
|
||||
+
|
||||
" GOTO L3\n" +
|
||||
" L2\n" +
|
||||
" PUTFIELD executor/TestOne.i I\n" +
|
||||
" L3\n" +
|
||||
" RETURN\n" +
|
||||
" L4\n" +
|
||||
"METHOD: 0x0009(public static) foo(Lexecutor/TestOne;Ljava/lang/String;)J\n" +
|
||||
" CODE\n" +
|
||||
" L0\n" +
|
||||
" ALOAD 1\n" +
|
||||
" INVOKESTATIC java/lang/Long.parseLong(Ljava/lang/String;)J\n" +
|
||||
" LRETURN\n" +
|
||||
" L1\n" +
|
||||
"METHOD: 0x0009(public static) hashCode(Lexecutor/TestOne;)I\n" +
|
||||
" CODE\n" +
|
||||
" L0\n" +
|
||||
" BIPUSH 37\n" +
|
||||
" IRETURN\n" +
|
||||
" L1\n" +
|
||||
"\n");
|
||||
|
||||
Assert.assertEquals(
|
||||
" L0\n" +
|
||||
" ALOAD 1\n" +
|
||||
" INVOKESTATIC java/lang/Long.parseLong(Ljava/lang/String;)J\n" +
|
||||
" LRETURN\n" +
|
||||
" L1\n",
|
||||
toStringMethod(rtype.getLatestExecutorBytes(), "foo", false));
|
||||
// @formatter:on
|
||||
|
||||
// @formatter:off
|
||||
Assert.assertEquals(
|
||||
" L0\n" +
|
||||
" BIPUSH 37\n" +
|
||||
" IRETURN\n" +
|
||||
" L1\n",
|
||||
toStringMethod(rtype.getLatestExecutorBytes(), "hashCode", false));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void secondVersion() throws Exception {
|
||||
String tclass = "executor.TestOne";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(tclass);
|
||||
|
||||
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
|
||||
|
||||
rtype.loadNewVersion("2", retrieveRename(tclass, tclass + "2"));
|
||||
|
||||
// testing executor is for second version and not first
|
||||
|
||||
// @formatter:off
|
||||
checkType(
|
||||
rtype.getLatestExecutorBytes(),
|
||||
"CLASS: executor/TestOne$$E2 v50 0x0001(public) super java/lang/Object\n"
|
||||
+
|
||||
"SOURCE: TestOne2.java null\n"
|
||||
+
|
||||
"FIELD 0x0001(public) i I\n"
|
||||
+
|
||||
"METHOD: 0x0009(public static) ___init___(Lexecutor/TestOne;)V\n"
|
||||
+
|
||||
" CODE\n"
|
||||
+
|
||||
" L0\n"
|
||||
+
|
||||
" ALOAD 0\n"
|
||||
+
|
||||
" POP\n"
|
||||
+
|
||||
" RETURN\n"
|
||||
+
|
||||
" L1\n"
|
||||
+
|
||||
"METHOD: 0x0009(public static) foo(Lexecutor/TestOne;Ljava/lang/String;)J\n"
|
||||
+
|
||||
" CODE\n"
|
||||
+
|
||||
" L0\n"
|
||||
+
|
||||
" ALOAD 1\n"
|
||||
+
|
||||
" INVOKESTATIC java/lang/Long.parseLong(Ljava/lang/String;)J\n"
|
||||
+
|
||||
" LRETURN\n"
|
||||
+
|
||||
" L1\n"
|
||||
+
|
||||
"METHOD: 0x0009(public static) hashCode(Lexecutor/TestOne;)I\n"
|
||||
+
|
||||
" CODE\n"
|
||||
+
|
||||
" L0\n"
|
||||
+
|
||||
" ALOAD 0\n"
|
||||
+
|
||||
" LDC 0\n"
|
||||
+
|
||||
" LDC i\n"
|
||||
+
|
||||
" INVOKESTATIC org/springsource/loaded/TypeRegistry.instanceFieldInterceptionRequired(ILjava/lang/String;)Z\n"
|
||||
+
|
||||
" IFEQ L1\n"
|
||||
+
|
||||
" DUP\n"
|
||||
+
|
||||
" LDC i\n"
|
||||
+
|
||||
" INVOKESPECIAL executor/TestOne.r$get(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;\n"
|
||||
+
|
||||
" CHECKCAST java/lang/Integer\n" +
|
||||
" INVOKEVIRTUAL java/lang/Integer.intValue()I\n" +
|
||||
" GOTO L2\n" +
|
||||
" L1\n" +
|
||||
" GETFIELD executor/TestOne.i I\n" +
|
||||
" L2\n" +
|
||||
" ICONST_2\n" +
|
||||
" IMUL\n" +
|
||||
" IRETURN\n" +
|
||||
" L3\n" +
|
||||
"\n");
|
||||
|
||||
Assert.assertEquals(
|
||||
" L0\n" +
|
||||
" ALOAD 1\n" +
|
||||
" INVOKESTATIC java/lang/Long.parseLong(Ljava/lang/String;)J\n" +
|
||||
" LRETURN\n" +
|
||||
" L1\n",
|
||||
toStringMethod(rtype.getLatestExecutorBytes(), "foo", false));
|
||||
// @formatter:on
|
||||
//
|
||||
// @formatter:off
|
||||
Assert.assertEquals(
|
||||
" L0\n"
|
||||
+
|
||||
" ALOAD 0\n"
|
||||
+
|
||||
" LDC 0\n"
|
||||
+
|
||||
" LDC i\n"
|
||||
+
|
||||
" INVOKESTATIC org/springsource/loaded/TypeRegistry.instanceFieldInterceptionRequired(ILjava/lang/String;)Z\n"
|
||||
+
|
||||
" IFEQ L1\n"
|
||||
+
|
||||
" DUP\n"
|
||||
+
|
||||
" LDC i\n"
|
||||
+
|
||||
" INVOKESPECIAL executor/TestOne.r$get(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;\n"
|
||||
+
|
||||
" CHECKCAST java/lang/Integer\n" +
|
||||
" INVOKEVIRTUAL java/lang/Integer.intValue()I\n" +
|
||||
" GOTO L2\n" +
|
||||
" L1\n" +
|
||||
" GETFIELD executor/TestOne.i I\n" +
|
||||
" L2\n" +
|
||||
" ICONST_2\n" +
|
||||
" IMUL\n" +
|
||||
" IRETURN\n" +
|
||||
" L3\n",
|
||||
toStringMethod(rtype.getLatestExecutorBytes(), "hashCode", false));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing that type level annotations are copied to the executor (to answer later reflection questions).
|
||||
*/
|
||||
@Test
|
||||
public void typeLevelAnnotations() {
|
||||
String t = "executor.A";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
reload(rtype, "2");
|
||||
Class<?> clazz = rtype.getLatestExecutorClass();
|
||||
Assert.assertEquals(Utils.getExecutorName(t, "2"), clazz.getName());
|
||||
Annotation[] annos = clazz.getAnnotations();
|
||||
Assert.assertNotNull(annos);
|
||||
Assert.assertEquals(1, annos.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing that type level annotations are copied to the executor. This loads a different form of the type with a
|
||||
* second annotation.
|
||||
*/
|
||||
@Test
|
||||
public void typeLevelAnnotations2() {
|
||||
String t = "executor.A";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
rtype.loadNewVersion("2", retrieveRename(t, t + "2"));
|
||||
Class<?> clazz = rtype.getLatestExecutorClass();
|
||||
Assert.assertEquals(Utils.getExecutorName(t, "2"), clazz.getName());
|
||||
Annotation[] annos = clazz.getAnnotations();
|
||||
Assert.assertNotNull(annos);
|
||||
Assert.assertEquals(2, annos.length);
|
||||
Set<String> s = new HashSet<String>();
|
||||
for (Annotation anno : annos) {
|
||||
s.add(anno.toString());
|
||||
}
|
||||
Assert.assertTrue(s.remove("@common.Marker()"));
|
||||
// Allow for alternate toString() variant
|
||||
if (!s.remove("@common.Anno(someValue=37, longValue=2, id=abc)")) {
|
||||
Assert.assertTrue(s.remove("@common.Anno(longValue=2, someValue=37, id=abc)"));
|
||||
}
|
||||
Assert.assertEquals(0, s.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodLevelAnnotations() throws Exception {
|
||||
String t = "executor.B";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
reload(rtype, "37");
|
||||
checkAnnotations(rtype.bytesLoaded, "m()V", "@common.Marker()");
|
||||
checkAnnotations(rtype.bytesLoaded, "m2()V");
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "m(Lexecutor/B;)V", "@common.Marker()");
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "m2(Lexecutor/B;)V");
|
||||
rtype.loadNewVersion("39", retrieveRename("executor.B", "executor.B2"));
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "m(Lexecutor/B;)V");
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "m2(Lexecutor/B;)V", "@common.Marker()",
|
||||
"@common.Anno(id=abc)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodLevelAnnotationsOnInterfaces() throws Exception {
|
||||
String t = "executor.I";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
reload(rtype, "37");
|
||||
checkAnnotations(rtype.bytesLoaded, "m()V", "@common.Marker()");
|
||||
checkAnnotations(rtype.bytesLoaded, "m2()V");
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "m(Lexecutor/I;)V", "@common.Marker()");
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "m2(Lexecutor/I;)V");
|
||||
rtype.loadNewVersion("39", retrieveRename("executor.I", "executor.I2"));
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "m(Lexecutor/I;)V");
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "m2(Lexecutor/I;)V", "@common.Marker()",
|
||||
"@common.Anno(id=abc)");
|
||||
Method m = rtype.getLatestExecutorClass().getDeclaredMethod("m2", rtype.getClazz());
|
||||
assertEquals("@common.Marker()", m.getAnnotations()[0].toString());
|
||||
assertIsOneOfThese(printAnnotation(m.getAnnotations()[1]), "@common.Anno(someValue=37, longValue=2, id=abc)",
|
||||
"@common.Anno(longValue=2, someValue=37, id=abc)");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the actual value is one of the possible options.
|
||||
*/
|
||||
private void assertIsOneOfThese(String actual, String... possibleValues) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int i = 0; i < possibleValues.length; i++) {
|
||||
if (actual.equals(possibleValues[i])) {
|
||||
return;
|
||||
}
|
||||
buf.append("'" + possibleValues[i] + "'").append("\n");
|
||||
}
|
||||
fail("The value:\n'" + actual + "'\n does not match one of these possible options:\n" + buf.toString());
|
||||
}
|
||||
|
||||
//
|
||||
private String printAnnotation(Annotation a) {
|
||||
return a.toString();
|
||||
// StringBuilder buf = new StringBuilder();
|
||||
// printAnnotationHelper(buf,a);
|
||||
// return buf.toString();
|
||||
}
|
||||
|
||||
//
|
||||
// private void printAnnotationHelper(StringBuilder buf, Annotation a) {
|
||||
// Class<?> clazz = a.annotationType();a.toString()
|
||||
// clazz.getDeclaredFields()[0].get
|
||||
// System.out.println(a.annotationType());
|
||||
//
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void methodLevelAnnotationsOnInterfaces2() throws Exception {
|
||||
String t = "reflection.methodannotations.InterfaceTarget";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
checkAnnotations(rtype.bytesLoaded, "privMethod()V", "@reflection.AnnoT3(value=Foo)");
|
||||
reload(rtype, "37");
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "privMethod(Lreflection/methodannotations/InterfaceTarget;)V",
|
||||
"@reflection.AnnoT3(value=Foo)");
|
||||
rtype.loadNewVersion("39", retrieveRename(t, t + "002"));
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "privMethod(Lreflection/methodannotations/InterfaceTarget;)V",
|
||||
"@reflection.AnnoT3(value=Bar)");
|
||||
Method m = rtype.getLatestExecutorClass().getDeclaredMethod("privMethod", rtype.getClazz());
|
||||
assertEquals("@reflection.AnnoT3(value=Bar)", m.getAnnotations()[0].toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clashingInstanceStaticMethods() throws Exception {
|
||||
String t = "executor.C";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
reload(rtype, "37");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticInitializerReloading1() throws Exception {
|
||||
String t = "clinit.One";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
result = runUnguarded(rtype.getClazz(), "run");
|
||||
assertEquals("5", result.returnValue);
|
||||
rtype.loadNewVersion("39", retrieveRename(t, t + "2"));
|
||||
rtype.runStaticInitializer(); // call is made on reloadable type
|
||||
result = runUnguarded(rtype.getClazz(), "run");
|
||||
assertEquals("7", result.returnValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticInitializerReloading2() throws Exception {
|
||||
String t = "clinit.One";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
result = runUnguarded(rtype.getClazz(), "run");
|
||||
assertEquals("5", result.returnValue);
|
||||
rtype.loadNewVersion("39", retrieveRename(t, t + "2"));
|
||||
|
||||
// use the 'new' ___clinit___ method to drive the static initializer
|
||||
Method staticInitializer = rtype.getClazz().getMethod("___clinit___");
|
||||
assertNotNull(staticInitializer);
|
||||
staticInitializer.invoke(null);
|
||||
|
||||
result = runUnguarded(rtype.getClazz(), "run");
|
||||
assertEquals("7", result.returnValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dealing with final fields
|
||||
*/
|
||||
@Test
|
||||
public void staticInitializerReloading3() throws Exception {
|
||||
String t = "clinit.Two";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
result = runUnguarded(rtype.getClazz(), "run");
|
||||
assertEquals("55", result.returnValue);
|
||||
rtype.loadNewVersion("39", retrieveRename(t, t + "2"));
|
||||
rtype.runStaticInitializer();
|
||||
result = runUnguarded(rtype.getClazz(), "run");
|
||||
assertEquals("99", result.returnValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Type that doesn't really have a clinit
|
||||
*/
|
||||
@Test
|
||||
public void staticInitializerReloading4() throws Exception {
|
||||
String t = "clinit.Three";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
result = runUnguarded(rtype.getClazz(), "run");
|
||||
assertEquals("1", result.returnValue);
|
||||
rtype.loadNewVersion("2", retrieveRename(t, t + "2"));
|
||||
rtype.runStaticInitializer();
|
||||
result = runUnguarded(rtype.getClazz(), "run");
|
||||
assertEquals("1", result.returnValue);
|
||||
rtype.loadNewVersion("3", retrieveRename(t, t + "3"));
|
||||
rtype.runStaticInitializer();
|
||||
result = runUnguarded(rtype.getClazz(), "run");
|
||||
ClassPrinter.print(rtype.getLatestExecutorBytes());
|
||||
assertEquals("4", result.returnValue);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
import org.springsource.loaded.Utils;
|
||||
import org.springsource.loaded.test.infra.ClassPrinter;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for creation of the executor instances that run the code
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class ExecutorBuilderTests extends SpringLoadedTests {
|
||||
|
||||
/**
|
||||
* Check properties of the newly created executor.
|
||||
*/
|
||||
@Test
|
||||
public void basicExternals() throws Exception {
|
||||
String t = "executor.TestOne";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
reload(rtype, "37");
|
||||
Class<?> clazz = rtype.getLatestExecutorClass();
|
||||
Assert.assertEquals(Utils.getExecutorName(t, "37"), clazz.getName());
|
||||
Assert.assertEquals(3, clazz.getDeclaredMethods().length);
|
||||
Assert.assertEquals(1, clazz.getDeclaredFields().length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check internal structure of the newly created executor.
|
||||
*/
|
||||
@Test
|
||||
public void basicInternalsLocalVariables() throws Exception {
|
||||
String t = "executor.TestOne";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
reload(rtype, "37");
|
||||
checkLocalVariables(rtype.getLatestExecutorBytes(), "foo(Lexecutor/TestOne;Ljava/lang/String;)J",
|
||||
"thiz:Lexecutor/TestOne;", "s:Ljava/lang/String;");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void codeStructure() throws Exception {
|
||||
String tclass = "executor.TestOne";
|
||||
|
||||
TypeRegistry typeRegistry = getTypeRegistry(tclass);
|
||||
|
||||
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
|
||||
|
||||
// Reload it (triggers creation of dispatcher/executor)
|
||||
rtype.loadNewVersion("2", rtype.bytesInitial);
|
||||
|
||||
// @formatter:off
|
||||
checkType(
|
||||
rtype.getLatestExecutorBytes(),
|
||||
"CLASS: executor/TestOne$$E2 v50 0x0001(public) super java/lang/Object\n"
|
||||
+
|
||||
"SOURCE: TestOne.java null\n"
|
||||
+
|
||||
"FIELD 0x0001(public) i I\n"
|
||||
+
|
||||
"METHOD: 0x0009(public static) ___init___(Lexecutor/TestOne;)V\n"
|
||||
+
|
||||
" CODE\n"
|
||||
+
|
||||
" L0\n"
|
||||
+
|
||||
" ALOAD 0\n"
|
||||
+
|
||||
" POP\n"
|
||||
+
|
||||
" L1\n"
|
||||
+
|
||||
" ALOAD 0\n"
|
||||
+
|
||||
" BIPUSH 101\n"
|
||||
+
|
||||
" LDC 0\n"
|
||||
+
|
||||
" LDC i\n"
|
||||
+
|
||||
" INVOKESTATIC org/springsource/loaded/TypeRegistry.instanceFieldInterceptionRequired(ILjava/lang/String;)Z\n"
|
||||
+
|
||||
" IFEQ L2\n"
|
||||
+
|
||||
" INVOKESTATIC java/lang/Integer.valueOf(I)Ljava/lang/Integer;\n"
|
||||
+
|
||||
" SWAP\n"
|
||||
+
|
||||
" DUP_X1\n"
|
||||
+
|
||||
" LDC i\n"
|
||||
+
|
||||
" INVOKESPECIAL executor/TestOne.r$set(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)V\n"
|
||||
+
|
||||
" GOTO L3\n" +
|
||||
" L2\n" +
|
||||
" PUTFIELD executor/TestOne.i I\n" +
|
||||
" L3\n" +
|
||||
" RETURN\n" +
|
||||
" L4\n" +
|
||||
"METHOD: 0x0009(public static) foo(Lexecutor/TestOne;Ljava/lang/String;)J\n" +
|
||||
" CODE\n" +
|
||||
" L0\n" +
|
||||
" ALOAD 1\n" +
|
||||
" INVOKESTATIC java/lang/Long.parseLong(Ljava/lang/String;)J\n" +
|
||||
" LRETURN\n" +
|
||||
" L1\n" +
|
||||
"METHOD: 0x0009(public static) hashCode(Lexecutor/TestOne;)I\n" +
|
||||
" CODE\n" +
|
||||
" L0\n" +
|
||||
" BIPUSH 37\n" +
|
||||
" IRETURN\n" +
|
||||
" L1\n" +
|
||||
"\n");
|
||||
|
||||
Assert.assertEquals(
|
||||
" L0\n" +
|
||||
" ALOAD 1\n" +
|
||||
" INVOKESTATIC java/lang/Long.parseLong(Ljava/lang/String;)J\n" +
|
||||
" LRETURN\n" +
|
||||
" L1\n",
|
||||
toStringMethod(rtype.getLatestExecutorBytes(), "foo", false));
|
||||
// @formatter:on
|
||||
|
||||
// @formatter:off
|
||||
Assert.assertEquals(
|
||||
" L0\n" +
|
||||
" BIPUSH 37\n" +
|
||||
" IRETURN\n" +
|
||||
" L1\n",
|
||||
toStringMethod(rtype.getLatestExecutorBytes(), "hashCode", false));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void secondVersion() throws Exception {
|
||||
String tclass = "executor.TestOne";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(tclass);
|
||||
|
||||
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
|
||||
|
||||
rtype.loadNewVersion("2", retrieveRename(tclass, tclass + "2"));
|
||||
|
||||
// testing executor is for second version and not first
|
||||
|
||||
// @formatter:off
|
||||
checkType(
|
||||
rtype.getLatestExecutorBytes(),
|
||||
"CLASS: executor/TestOne$$E2 v50 0x0001(public) super java/lang/Object\n"
|
||||
+
|
||||
"SOURCE: TestOne2.java null\n"
|
||||
+
|
||||
"FIELD 0x0001(public) i I\n"
|
||||
+
|
||||
"METHOD: 0x0009(public static) ___init___(Lexecutor/TestOne;)V\n"
|
||||
+
|
||||
" CODE\n"
|
||||
+
|
||||
" L0\n"
|
||||
+
|
||||
" ALOAD 0\n"
|
||||
+
|
||||
" POP\n"
|
||||
+
|
||||
" RETURN\n"
|
||||
+
|
||||
" L1\n"
|
||||
+
|
||||
"METHOD: 0x0009(public static) foo(Lexecutor/TestOne;Ljava/lang/String;)J\n"
|
||||
+
|
||||
" CODE\n"
|
||||
+
|
||||
" L0\n"
|
||||
+
|
||||
" ALOAD 1\n"
|
||||
+
|
||||
" INVOKESTATIC java/lang/Long.parseLong(Ljava/lang/String;)J\n"
|
||||
+
|
||||
" LRETURN\n"
|
||||
+
|
||||
" L1\n"
|
||||
+
|
||||
"METHOD: 0x0009(public static) hashCode(Lexecutor/TestOne;)I\n"
|
||||
+
|
||||
" CODE\n"
|
||||
+
|
||||
" L0\n"
|
||||
+
|
||||
" ALOAD 0\n"
|
||||
+
|
||||
" LDC 0\n"
|
||||
+
|
||||
" LDC i\n"
|
||||
+
|
||||
" INVOKESTATIC org/springsource/loaded/TypeRegistry.instanceFieldInterceptionRequired(ILjava/lang/String;)Z\n"
|
||||
+
|
||||
" IFEQ L1\n"
|
||||
+
|
||||
" DUP\n"
|
||||
+
|
||||
" LDC i\n"
|
||||
+
|
||||
" INVOKESPECIAL executor/TestOne.r$get(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;\n"
|
||||
+
|
||||
" CHECKCAST java/lang/Integer\n" +
|
||||
" INVOKEVIRTUAL java/lang/Integer.intValue()I\n" +
|
||||
" GOTO L2\n" +
|
||||
" L1\n" +
|
||||
" GETFIELD executor/TestOne.i I\n" +
|
||||
" L2\n" +
|
||||
" ICONST_2\n" +
|
||||
" IMUL\n" +
|
||||
" IRETURN\n" +
|
||||
" L3\n" +
|
||||
"\n");
|
||||
|
||||
Assert.assertEquals(
|
||||
" L0\n" +
|
||||
" ALOAD 1\n" +
|
||||
" INVOKESTATIC java/lang/Long.parseLong(Ljava/lang/String;)J\n" +
|
||||
" LRETURN\n" +
|
||||
" L1\n",
|
||||
toStringMethod(rtype.getLatestExecutorBytes(), "foo", false));
|
||||
// @formatter:on
|
||||
//
|
||||
// @formatter:off
|
||||
Assert.assertEquals(
|
||||
" L0\n"
|
||||
+
|
||||
" ALOAD 0\n"
|
||||
+
|
||||
" LDC 0\n"
|
||||
+
|
||||
" LDC i\n"
|
||||
+
|
||||
" INVOKESTATIC org/springsource/loaded/TypeRegistry.instanceFieldInterceptionRequired(ILjava/lang/String;)Z\n"
|
||||
+
|
||||
" IFEQ L1\n"
|
||||
+
|
||||
" DUP\n"
|
||||
+
|
||||
" LDC i\n"
|
||||
+
|
||||
" INVOKESPECIAL executor/TestOne.r$get(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;\n"
|
||||
+
|
||||
" CHECKCAST java/lang/Integer\n" +
|
||||
" INVOKEVIRTUAL java/lang/Integer.intValue()I\n" +
|
||||
" GOTO L2\n" +
|
||||
" L1\n" +
|
||||
" GETFIELD executor/TestOne.i I\n" +
|
||||
" L2\n" +
|
||||
" ICONST_2\n" +
|
||||
" IMUL\n" +
|
||||
" IRETURN\n" +
|
||||
" L3\n",
|
||||
toStringMethod(rtype.getLatestExecutorBytes(), "hashCode", false));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing that type level annotations are copied to the executor (to answer later reflection questions).
|
||||
*/
|
||||
@Test
|
||||
public void typeLevelAnnotations() {
|
||||
String t = "executor.A";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
reload(rtype, "2");
|
||||
Class<?> clazz = rtype.getLatestExecutorClass();
|
||||
Assert.assertEquals(Utils.getExecutorName(t, "2"), clazz.getName());
|
||||
Annotation[] annos = clazz.getAnnotations();
|
||||
Assert.assertNotNull(annos);
|
||||
Assert.assertEquals(1, annos.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing that type level annotations are copied to the executor. This loads a different form of the type with a
|
||||
* second annotation.
|
||||
*/
|
||||
@Test
|
||||
public void typeLevelAnnotations2() {
|
||||
String t = "executor.A";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
rtype.loadNewVersion("2", retrieveRename(t, t + "2"));
|
||||
Class<?> clazz = rtype.getLatestExecutorClass();
|
||||
Assert.assertEquals(Utils.getExecutorName(t, "2"), clazz.getName());
|
||||
Annotation[] annos = clazz.getAnnotations();
|
||||
Assert.assertNotNull(annos);
|
||||
Assert.assertEquals(2, annos.length);
|
||||
Set<String> s = new HashSet<String>();
|
||||
for (Annotation anno : annos) {
|
||||
s.add(anno.toString());
|
||||
}
|
||||
Assert.assertTrue(s.remove("@common.Marker()"));
|
||||
// Allow for alternate toString() variant
|
||||
if (!s.remove("@common.Anno(someValue=37, longValue=2, id=abc)")) {
|
||||
Assert.assertTrue(s.remove("@common.Anno(longValue=2, someValue=37, id=abc)"));
|
||||
}
|
||||
Assert.assertEquals(0, s.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodLevelAnnotations() throws Exception {
|
||||
String t = "executor.B";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
reload(rtype, "37");
|
||||
checkAnnotations(rtype.bytesLoaded, "m()V", "@common.Marker()");
|
||||
checkAnnotations(rtype.bytesLoaded, "m2()V");
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "m(Lexecutor/B;)V", "@common.Marker()");
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "m2(Lexecutor/B;)V");
|
||||
rtype.loadNewVersion("39", retrieveRename("executor.B", "executor.B2"));
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "m(Lexecutor/B;)V");
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "m2(Lexecutor/B;)V", "@common.Marker()",
|
||||
"@common.Anno(id=abc)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodLevelAnnotationsOnInterfaces() throws Exception {
|
||||
String t = "executor.I";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
reload(rtype, "37");
|
||||
checkAnnotations(rtype.bytesLoaded, "m()V", "@common.Marker()");
|
||||
checkAnnotations(rtype.bytesLoaded, "m2()V");
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "m(Lexecutor/I;)V", "@common.Marker()");
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "m2(Lexecutor/I;)V");
|
||||
rtype.loadNewVersion("39", retrieveRename("executor.I", "executor.I2"));
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "m(Lexecutor/I;)V");
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "m2(Lexecutor/I;)V", "@common.Marker()",
|
||||
"@common.Anno(id=abc)");
|
||||
Method m = rtype.getLatestExecutorClass().getDeclaredMethod("m2", rtype.getClazz());
|
||||
assertEquals("@common.Marker()", m.getAnnotations()[0].toString());
|
||||
assertIsOneOfThese(printAnnotation(m.getAnnotations()[1]), "@common.Anno(someValue=37, longValue=2, id=abc)",
|
||||
"@common.Anno(longValue=2, someValue=37, id=abc)");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the actual value is one of the possible options.
|
||||
*/
|
||||
private void assertIsOneOfThese(String actual, String... possibleValues) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int i = 0; i < possibleValues.length; i++) {
|
||||
if (actual.equals(possibleValues[i])) {
|
||||
return;
|
||||
}
|
||||
buf.append("'" + possibleValues[i] + "'").append("\n");
|
||||
}
|
||||
fail("The value:\n'" + actual + "'\n does not match one of these possible options:\n" + buf.toString());
|
||||
}
|
||||
|
||||
//
|
||||
private String printAnnotation(Annotation a) {
|
||||
return a.toString();
|
||||
// StringBuilder buf = new StringBuilder();
|
||||
// printAnnotationHelper(buf,a);
|
||||
// return buf.toString();
|
||||
}
|
||||
|
||||
//
|
||||
// private void printAnnotationHelper(StringBuilder buf, Annotation a) {
|
||||
// Class<?> clazz = a.annotationType();a.toString()
|
||||
// clazz.getDeclaredFields()[0].get
|
||||
// System.out.println(a.annotationType());
|
||||
//
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void methodLevelAnnotationsOnInterfaces2() throws Exception {
|
||||
String t = "reflection.methodannotations.InterfaceTarget";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
checkAnnotations(rtype.bytesLoaded, "privMethod()V", "@reflection.AnnoT3(value=Foo)");
|
||||
reload(rtype, "37");
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "privMethod(Lreflection/methodannotations/InterfaceTarget;)V",
|
||||
"@reflection.AnnoT3(value=Foo)");
|
||||
rtype.loadNewVersion("39", retrieveRename(t, t + "002"));
|
||||
checkAnnotations(rtype.getLatestExecutorBytes(), "privMethod(Lreflection/methodannotations/InterfaceTarget;)V",
|
||||
"@reflection.AnnoT3(value=Bar)");
|
||||
Method m = rtype.getLatestExecutorClass().getDeclaredMethod("privMethod", rtype.getClazz());
|
||||
assertEquals("@reflection.AnnoT3(value=Bar)", m.getAnnotations()[0].toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clashingInstanceStaticMethods() throws Exception {
|
||||
String t = "executor.C";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
reload(rtype, "37");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticInitializerReloading1() throws Exception {
|
||||
String t = "clinit.One";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
result = runUnguarded(rtype.getClazz(), "run");
|
||||
assertEquals("5", result.returnValue);
|
||||
rtype.loadNewVersion("39", retrieveRename(t, t + "2"));
|
||||
rtype.runStaticInitializer(); // call is made on reloadable type
|
||||
result = runUnguarded(rtype.getClazz(), "run");
|
||||
assertEquals("7", result.returnValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticInitializerReloading2() throws Exception {
|
||||
String t = "clinit.One";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
result = runUnguarded(rtype.getClazz(), "run");
|
||||
assertEquals("5", result.returnValue);
|
||||
rtype.loadNewVersion("39", retrieveRename(t, t + "2"));
|
||||
|
||||
// use the 'new' ___clinit___ method to drive the static initializer
|
||||
Method staticInitializer = rtype.getClazz().getMethod("___clinit___");
|
||||
assertNotNull(staticInitializer);
|
||||
staticInitializer.invoke(null);
|
||||
|
||||
result = runUnguarded(rtype.getClazz(), "run");
|
||||
assertEquals("7", result.returnValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dealing with final fields
|
||||
*/
|
||||
@Test
|
||||
public void staticInitializerReloading3() throws Exception {
|
||||
String t = "clinit.Two";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
result = runUnguarded(rtype.getClazz(), "run");
|
||||
assertEquals("55", result.returnValue);
|
||||
rtype.loadNewVersion("39", retrieveRename(t, t + "2"));
|
||||
rtype.runStaticInitializer();
|
||||
result = runUnguarded(rtype.getClazz(), "run");
|
||||
assertEquals("99", result.returnValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Type that doesn't really have a clinit
|
||||
*/
|
||||
@Test
|
||||
public void staticInitializerReloading4() throws Exception {
|
||||
String t = "clinit.Three";
|
||||
TypeRegistry typeRegistry = getTypeRegistry(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
|
||||
result = runUnguarded(rtype.getClazz(), "run");
|
||||
assertEquals("1", result.returnValue);
|
||||
rtype.loadNewVersion("2", retrieveRename(t, t + "2"));
|
||||
rtype.runStaticInitializer();
|
||||
result = runUnguarded(rtype.getClazz(), "run");
|
||||
assertEquals("1", result.returnValue);
|
||||
rtype.loadNewVersion("3", retrieveRename(t, t + "3"));
|
||||
rtype.runStaticInitializer();
|
||||
result = runUnguarded(rtype.getClazz(), "run");
|
||||
ClassPrinter.print(rtype.getLatestExecutorBytes());
|
||||
assertEquals("4", result.returnValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,212 +1,212 @@
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.FileChangeListener;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
import org.springsource.loaded.agent.FileSystemWatcher;
|
||||
|
||||
|
||||
public class FileSystemWatcherTests {
|
||||
|
||||
/**
|
||||
* Create a folder, watch it then put a couple of files in and check they are detected
|
||||
*/
|
||||
@Ignore
|
||||
@Test
|
||||
public void dirs() throws IOException {
|
||||
TestFileChangeListener listener = new TestFileChangeListener();
|
||||
File dir = getTempDir();
|
||||
FileSystemWatcher watcher = new FileSystemWatcher(listener, -1, "test");
|
||||
watcher.register(dir);
|
||||
pause(1000);
|
||||
create(dir, "abc.txt");
|
||||
pause(1100);
|
||||
create(dir, "abcd.txt");
|
||||
pause(1100);
|
||||
watcher.shutdown();
|
||||
Assert.assertTrue(listener.changesDetected.contains("abc.txt"));
|
||||
Assert.assertTrue(listener.changesDetected.contains("abcd.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void files() throws IOException {
|
||||
TestFileChangeListener listener = new TestFileChangeListener();
|
||||
File dir = getTempDir();
|
||||
FileSystemWatcher watcher = new FileSystemWatcher(listener, -1, "test");
|
||||
pause(1000);
|
||||
File f1 = create(dir, "abc.txt");
|
||||
watcher.register(f1);
|
||||
pause(1100);
|
||||
File f2 = create(dir, "abcd.txt");
|
||||
watcher.register(f2);
|
||||
pause(1100);
|
||||
watcher.setPaused(true);
|
||||
// Whilst paused, touch both files
|
||||
touch(f2);
|
||||
touch(f1);
|
||||
watcher.setPaused(false);
|
||||
pause(3000);
|
||||
watcher.shutdown();
|
||||
System.out.println(listener.changesDetected);
|
||||
assertEquals("abc.txt", listener.changesDetected.get(0));
|
||||
assertEquals("abcd.txt", listener.changesDetected.get(1));
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void innersFirst() throws IOException {
|
||||
System.out.println("innersFirst");
|
||||
TestFileChangeListener listener = new TestFileChangeListener();
|
||||
File dir = getTempDir();
|
||||
FileSystemWatcher watcher = new FileSystemWatcher(listener, -1, "test");
|
||||
pause(1000);
|
||||
File f1 = create(dir, "Book$1.class");
|
||||
watcher.register(f1);
|
||||
pause(1100);
|
||||
File f2 = create(dir, "Book.class");
|
||||
watcher.register(f2);
|
||||
pause(1100);
|
||||
File f3 = create(dir, "Book$_2.class");
|
||||
watcher.register(f3);
|
||||
pause(1100);
|
||||
watcher.setPaused(true);
|
||||
// Whilst paused, touch both files
|
||||
touch(f3);
|
||||
touch(f2);
|
||||
touch(f1);
|
||||
watcher.setPaused(false);
|
||||
pause(3000);
|
||||
System.out.println(listener.changesDetected);
|
||||
watcher.shutdown();
|
||||
// Check that inners reported first
|
||||
assertEquals("Book$1.class", listener.changesDetected.get(0));
|
||||
assertEquals("Book$_2.class", listener.changesDetected.get(1));
|
||||
assertEquals("Book.class", listener.changesDetected.get(2));
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void innerInnersFirst() throws IOException {
|
||||
TestFileChangeListener listener = new TestFileChangeListener();
|
||||
File dir = getTempDir();
|
||||
FileSystemWatcher watcher = new FileSystemWatcher(listener, -1, "test");
|
||||
pause(1000);
|
||||
File f1 = create(dir, "Book$1.class");
|
||||
watcher.register(f1);
|
||||
pause(1100);
|
||||
File f2 = create(dir, "Book.class");
|
||||
watcher.register(f2);
|
||||
pause(1100);
|
||||
File f3 = create(dir, "Book$_2.class");
|
||||
watcher.register(f3);
|
||||
pause(1100);
|
||||
File f4 = create(dir, "Book$Foo.class");
|
||||
watcher.register(f4);
|
||||
pause(1100);
|
||||
File f5 = create(dir, "Book$Foo$1.class");
|
||||
watcher.register(f5);
|
||||
pause(1100);
|
||||
watcher.setPaused(true);
|
||||
// Whilst paused, touch both files
|
||||
touch(f5);
|
||||
touch(f4);
|
||||
touch(f3);
|
||||
touch(f2);
|
||||
touch(f1);
|
||||
watcher.setPaused(false);
|
||||
pause(1100);
|
||||
watcher.shutdown();
|
||||
// Check that inners reported first
|
||||
System.out.println(listener.changesDetected);
|
||||
assertEquals("Book$1.class", listener.changesDetected.get(0));
|
||||
assertEquals("Book$Foo$1.class", listener.changesDetected.get(1));
|
||||
assertEquals("Book$Foo.class", listener.changesDetected.get(2));
|
||||
assertEquals("Book$_2.class", listener.changesDetected.get(3));
|
||||
assertEquals("Book.class", listener.changesDetected.get(4));
|
||||
}
|
||||
|
||||
private void touch(File f) {
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(f);
|
||||
fos.write(3);
|
||||
fos.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private File create(File dir, String filename) throws IOException {
|
||||
File f = new File(dir, filename);
|
||||
boolean b = f.createNewFile();
|
||||
Assert.assertTrue(b);
|
||||
return f;
|
||||
}
|
||||
|
||||
private void pause(long millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
private File getTempDir() {
|
||||
try {
|
||||
File tempFile;
|
||||
tempFile = File.createTempFile("eternal", "");
|
||||
// File base =
|
||||
tempFile.getParentFile();
|
||||
// String name =
|
||||
// tempFile.getName();
|
||||
tempFile.delete();
|
||||
boolean b = tempFile.mkdir();
|
||||
if (!b) {
|
||||
throw new RuntimeException("Failed to create folder " + tempFile);
|
||||
}
|
||||
return tempFile;
|
||||
}
|
||||
catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static class TestFileChangeListener implements FileChangeListener {
|
||||
|
||||
List<String> changesDetected = new ArrayList<String>();
|
||||
|
||||
public void fileChanged(File file) {
|
||||
System.out.println("File change detected " + file);
|
||||
changesDetected.add(file.getName());
|
||||
}
|
||||
|
||||
public void register(ReloadableType rtype, File file) {
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.FileChangeListener;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
import org.springsource.loaded.agent.FileSystemWatcher;
|
||||
|
||||
|
||||
public class FileSystemWatcherTests {
|
||||
|
||||
/**
|
||||
* Create a folder, watch it then put a couple of files in and check they are detected
|
||||
*/
|
||||
@Ignore
|
||||
@Test
|
||||
public void dirs() throws IOException {
|
||||
TestFileChangeListener listener = new TestFileChangeListener();
|
||||
File dir = getTempDir();
|
||||
FileSystemWatcher watcher = new FileSystemWatcher(listener, -1, "test");
|
||||
watcher.register(dir);
|
||||
pause(1000);
|
||||
create(dir, "abc.txt");
|
||||
pause(1100);
|
||||
create(dir, "abcd.txt");
|
||||
pause(1100);
|
||||
watcher.shutdown();
|
||||
Assert.assertTrue(listener.changesDetected.contains("abc.txt"));
|
||||
Assert.assertTrue(listener.changesDetected.contains("abcd.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void files() throws IOException {
|
||||
TestFileChangeListener listener = new TestFileChangeListener();
|
||||
File dir = getTempDir();
|
||||
FileSystemWatcher watcher = new FileSystemWatcher(listener, -1, "test");
|
||||
pause(1000);
|
||||
File f1 = create(dir, "abc.txt");
|
||||
watcher.register(f1);
|
||||
pause(1100);
|
||||
File f2 = create(dir, "abcd.txt");
|
||||
watcher.register(f2);
|
||||
pause(1100);
|
||||
watcher.setPaused(true);
|
||||
// Whilst paused, touch both files
|
||||
touch(f2);
|
||||
touch(f1);
|
||||
watcher.setPaused(false);
|
||||
pause(3000);
|
||||
watcher.shutdown();
|
||||
System.out.println(listener.changesDetected);
|
||||
assertEquals("abc.txt", listener.changesDetected.get(0));
|
||||
assertEquals("abcd.txt", listener.changesDetected.get(1));
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void innersFirst() throws IOException {
|
||||
System.out.println("innersFirst");
|
||||
TestFileChangeListener listener = new TestFileChangeListener();
|
||||
File dir = getTempDir();
|
||||
FileSystemWatcher watcher = new FileSystemWatcher(listener, -1, "test");
|
||||
pause(1000);
|
||||
File f1 = create(dir, "Book$1.class");
|
||||
watcher.register(f1);
|
||||
pause(1100);
|
||||
File f2 = create(dir, "Book.class");
|
||||
watcher.register(f2);
|
||||
pause(1100);
|
||||
File f3 = create(dir, "Book$_2.class");
|
||||
watcher.register(f3);
|
||||
pause(1100);
|
||||
watcher.setPaused(true);
|
||||
// Whilst paused, touch both files
|
||||
touch(f3);
|
||||
touch(f2);
|
||||
touch(f1);
|
||||
watcher.setPaused(false);
|
||||
pause(3000);
|
||||
System.out.println(listener.changesDetected);
|
||||
watcher.shutdown();
|
||||
// Check that inners reported first
|
||||
assertEquals("Book$1.class", listener.changesDetected.get(0));
|
||||
assertEquals("Book$_2.class", listener.changesDetected.get(1));
|
||||
assertEquals("Book.class", listener.changesDetected.get(2));
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void innerInnersFirst() throws IOException {
|
||||
TestFileChangeListener listener = new TestFileChangeListener();
|
||||
File dir = getTempDir();
|
||||
FileSystemWatcher watcher = new FileSystemWatcher(listener, -1, "test");
|
||||
pause(1000);
|
||||
File f1 = create(dir, "Book$1.class");
|
||||
watcher.register(f1);
|
||||
pause(1100);
|
||||
File f2 = create(dir, "Book.class");
|
||||
watcher.register(f2);
|
||||
pause(1100);
|
||||
File f3 = create(dir, "Book$_2.class");
|
||||
watcher.register(f3);
|
||||
pause(1100);
|
||||
File f4 = create(dir, "Book$Foo.class");
|
||||
watcher.register(f4);
|
||||
pause(1100);
|
||||
File f5 = create(dir, "Book$Foo$1.class");
|
||||
watcher.register(f5);
|
||||
pause(1100);
|
||||
watcher.setPaused(true);
|
||||
// Whilst paused, touch both files
|
||||
touch(f5);
|
||||
touch(f4);
|
||||
touch(f3);
|
||||
touch(f2);
|
||||
touch(f1);
|
||||
watcher.setPaused(false);
|
||||
pause(1100);
|
||||
watcher.shutdown();
|
||||
// Check that inners reported first
|
||||
System.out.println(listener.changesDetected);
|
||||
assertEquals("Book$1.class", listener.changesDetected.get(0));
|
||||
assertEquals("Book$Foo$1.class", listener.changesDetected.get(1));
|
||||
assertEquals("Book$Foo.class", listener.changesDetected.get(2));
|
||||
assertEquals("Book$_2.class", listener.changesDetected.get(3));
|
||||
assertEquals("Book.class", listener.changesDetected.get(4));
|
||||
}
|
||||
|
||||
private void touch(File f) {
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(f);
|
||||
fos.write(3);
|
||||
fos.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private File create(File dir, String filename) throws IOException {
|
||||
File f = new File(dir, filename);
|
||||
boolean b = f.createNewFile();
|
||||
Assert.assertTrue(b);
|
||||
return f;
|
||||
}
|
||||
|
||||
private void pause(long millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
private File getTempDir() {
|
||||
try {
|
||||
File tempFile;
|
||||
tempFile = File.createTempFile("eternal", "");
|
||||
// File base =
|
||||
tempFile.getParentFile();
|
||||
// String name =
|
||||
// tempFile.getName();
|
||||
tempFile.delete();
|
||||
boolean b = tempFile.mkdir();
|
||||
if (!b) {
|
||||
throw new RuntimeException("Failed to create folder " + tempFile);
|
||||
}
|
||||
return tempFile;
|
||||
}
|
||||
catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static class TestFileChangeListener implements FileChangeListener {
|
||||
|
||||
List<String> changesDetected = new ArrayList<String>();
|
||||
|
||||
public void fileChanged(File file) {
|
||||
System.out.println("File change detected " + file);
|
||||
changesDetected.add(file.getName());
|
||||
}
|
||||
|
||||
public void register(ReloadableType rtype, File file) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,125 +1,125 @@
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.InterfaceExtractor;
|
||||
import org.springsource.loaded.TypeDescriptor;
|
||||
import org.springsource.loaded.TypeDescriptorExtractor;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for interface extraction.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 1.0
|
||||
*/
|
||||
public class InterfaceExtractorTest extends SpringLoadedTests {
|
||||
|
||||
/**
|
||||
* Attempt simple interface extraction for a class with one void no-arg method
|
||||
*/
|
||||
@Test
|
||||
public void simpleExtraction() {
|
||||
TypeRegistry registry = getTypeRegistry(null);
|
||||
byte[] classBytes = loadBytesForClass("data.SimpleClass");
|
||||
TypeDescriptor td = new TypeDescriptorExtractor(registry).extract(classBytes, true);
|
||||
// @formatter:off
|
||||
checkType(classBytes,
|
||||
"CLASS: data/SimpleClass v50 0x0020(synchronized) super java/lang/Object\n" +
|
||||
"SOURCE: SimpleClass.java null\n" +
|
||||
"METHOD: 0x0000() <init>()V\n" +
|
||||
" CODE\n" +
|
||||
" L0\n" +
|
||||
" ALOAD 0\n" +
|
||||
" INVOKESPECIAL java/lang/Object.<init>()V\n" +
|
||||
" RETURN\n" +
|
||||
" L1\n" +
|
||||
"METHOD: 0x0001(public) foo()V\n" +
|
||||
" CODE\n" +
|
||||
" L0\n" +
|
||||
" RETURN\n" +
|
||||
" L1\n" +
|
||||
"\n");
|
||||
// @formatter:on
|
||||
byte[] bytes = InterfaceExtractor.extract(classBytes, registry, td);
|
||||
// @formatter:off
|
||||
checkType(
|
||||
bytes,
|
||||
"CLASS: data/SimpleClass__I v50 0x0601(public abstract interface) super java/lang/Object\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) ___init___(Ldata/SimpleClass;)V\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) foo(Ldata/SimpleClass;)V\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) __execute([Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) ___clinit___()V\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) hashCode(Ldata/SimpleClass;)I\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) equals(Ldata/SimpleClass;Ljava/lang/Object;)Z\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) clone(Ldata/SimpleClass;)Ljava/lang/Object; java/lang/CloneNotSupportedException\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) toString(Ldata/SimpleClass;)Ljava/lang/String;\n" +
|
||||
"\n");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void varietyOfMethods() {
|
||||
TypeRegistry registry = getTypeRegistry(null);
|
||||
byte[] classBytes = loadBytesForClass("data.SimpleClassFour");
|
||||
TypeDescriptor td = new TypeDescriptorExtractor(registry).extract(classBytes, true);
|
||||
byte[] bytes = InterfaceExtractor.extract(classBytes, registry, td);
|
||||
// @formatter:off
|
||||
checkType(
|
||||
bytes,
|
||||
"CLASS: data/SimpleClassFour__I v50 0x0601(public abstract interface) super java/lang/Object\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) ___init___(Ldata/SimpleClassFour;I)V\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) ___init___(Ldata/SimpleClassFour;Ljava/lang/String;)V\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) boo(Ldata/SimpleClassFour;)V\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) foo(Ldata/SimpleClassFour;)V\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) goo(Ldata/SimpleClassFour;IDLjava/lang/String;)Ljava/lang/String;\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) hoo(Ldata/SimpleClassFour;J)I\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) woo(Ldata/SimpleClassFour;)V java/lang/RuntimeException java/lang/IllegalStateException\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) __execute([Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) ___clinit___()V\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) hashCode(Ldata/SimpleClassFour;)I\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) equals(Ldata/SimpleClassFour;Ljava/lang/Object;)Z\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) clone(Ldata/SimpleClassFour;)Ljava/lang/Object; java/lang/CloneNotSupportedException\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) toString(Ldata/SimpleClassFour;)Ljava/lang/String;\n" +
|
||||
"\n");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.InterfaceExtractor;
|
||||
import org.springsource.loaded.TypeDescriptor;
|
||||
import org.springsource.loaded.TypeDescriptorExtractor;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for interface extraction.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 1.0
|
||||
*/
|
||||
public class InterfaceExtractorTest extends SpringLoadedTests {
|
||||
|
||||
/**
|
||||
* Attempt simple interface extraction for a class with one void no-arg method
|
||||
*/
|
||||
@Test
|
||||
public void simpleExtraction() {
|
||||
TypeRegistry registry = getTypeRegistry(null);
|
||||
byte[] classBytes = loadBytesForClass("data.SimpleClass");
|
||||
TypeDescriptor td = new TypeDescriptorExtractor(registry).extract(classBytes, true);
|
||||
// @formatter:off
|
||||
checkType(classBytes,
|
||||
"CLASS: data/SimpleClass v50 0x0020(synchronized) super java/lang/Object\n" +
|
||||
"SOURCE: SimpleClass.java null\n" +
|
||||
"METHOD: 0x0000() <init>()V\n" +
|
||||
" CODE\n" +
|
||||
" L0\n" +
|
||||
" ALOAD 0\n" +
|
||||
" INVOKESPECIAL java/lang/Object.<init>()V\n" +
|
||||
" RETURN\n" +
|
||||
" L1\n" +
|
||||
"METHOD: 0x0001(public) foo()V\n" +
|
||||
" CODE\n" +
|
||||
" L0\n" +
|
||||
" RETURN\n" +
|
||||
" L1\n" +
|
||||
"\n");
|
||||
// @formatter:on
|
||||
byte[] bytes = InterfaceExtractor.extract(classBytes, registry, td);
|
||||
// @formatter:off
|
||||
checkType(
|
||||
bytes,
|
||||
"CLASS: data/SimpleClass__I v50 0x0601(public abstract interface) super java/lang/Object\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) ___init___(Ldata/SimpleClass;)V\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) foo(Ldata/SimpleClass;)V\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) __execute([Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) ___clinit___()V\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) hashCode(Ldata/SimpleClass;)I\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) equals(Ldata/SimpleClass;Ljava/lang/Object;)Z\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) clone(Ldata/SimpleClass;)Ljava/lang/Object; java/lang/CloneNotSupportedException\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) toString(Ldata/SimpleClass;)Ljava/lang/String;\n" +
|
||||
"\n");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void varietyOfMethods() {
|
||||
TypeRegistry registry = getTypeRegistry(null);
|
||||
byte[] classBytes = loadBytesForClass("data.SimpleClassFour");
|
||||
TypeDescriptor td = new TypeDescriptorExtractor(registry).extract(classBytes, true);
|
||||
byte[] bytes = InterfaceExtractor.extract(classBytes, registry, td);
|
||||
// @formatter:off
|
||||
checkType(
|
||||
bytes,
|
||||
"CLASS: data/SimpleClassFour__I v50 0x0601(public abstract interface) super java/lang/Object\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) ___init___(Ldata/SimpleClassFour;I)V\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) ___init___(Ldata/SimpleClassFour;Ljava/lang/String;)V\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) boo(Ldata/SimpleClassFour;)V\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) foo(Ldata/SimpleClassFour;)V\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) goo(Ldata/SimpleClassFour;IDLjava/lang/String;)Ljava/lang/String;\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) hoo(Ldata/SimpleClassFour;J)I\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) woo(Ldata/SimpleClassFour;)V java/lang/RuntimeException java/lang/IllegalStateException\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) __execute([Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) ___clinit___()V\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) hashCode(Ldata/SimpleClassFour;)I\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) equals(Ldata/SimpleClassFour;Ljava/lang/Object;)Z\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) clone(Ldata/SimpleClassFour;)Ljava/lang/Object; java/lang/CloneNotSupportedException\n"
|
||||
+
|
||||
"METHOD: 0x0401(public abstract) toString(Ldata/SimpleClassFour;)Ljava/lang/String;\n" +
|
||||
"\n");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.NameRegistry;
|
||||
|
||||
|
||||
/**
|
||||
* Test the NameRegistry behaves as expected.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public class NameRegistryTests extends SpringLoadedTests {
|
||||
|
||||
@Test
|
||||
public void byTheNumbers() {
|
||||
assertEquals(-1, NameRegistry.getIdFor("a/b/C"));
|
||||
assertEquals(0, NameRegistry.getIdOrAllocateFor("a/b/C"));
|
||||
assertEquals(0, NameRegistry.getIdFor("a/b/C"));
|
||||
assertEquals(1, NameRegistry.getIdOrAllocateFor("d/e/F"));
|
||||
assertEquals(1, NameRegistry.getIdOrAllocateFor("d/e/F"));
|
||||
// test expansion
|
||||
for (int i = 0; i < 100; i++) {
|
||||
assertEquals(2 + i, NameRegistry.getIdOrAllocateFor("a/b/C" + i));
|
||||
}
|
||||
assertEquals("a/b/C", NameRegistry.getTypenameById(0));
|
||||
assertNull(NameRegistry.getTypenameById(1000));
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.NameRegistry;
|
||||
|
||||
|
||||
/**
|
||||
* Test the NameRegistry behaves as expected.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public class NameRegistryTests extends SpringLoadedTests {
|
||||
|
||||
@Test
|
||||
public void byTheNumbers() {
|
||||
assertEquals(-1, NameRegistry.getIdFor("a/b/C"));
|
||||
assertEquals(0, NameRegistry.getIdOrAllocateFor("a/b/C"));
|
||||
assertEquals(0, NameRegistry.getIdFor("a/b/C"));
|
||||
assertEquals(1, NameRegistry.getIdOrAllocateFor("d/e/F"));
|
||||
assertEquals(1, NameRegistry.getIdOrAllocateFor("d/e/F"));
|
||||
// test expansion
|
||||
for (int i = 0; i < 100; i++) {
|
||||
assertEquals(2 + i, NameRegistry.getIdOrAllocateFor("a/b/C" + i));
|
||||
}
|
||||
assertEquals("a/b/C", NameRegistry.getTypenameById(0));
|
||||
assertNull(NameRegistry.getTypenameById(1000));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,173 +1,173 @@
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.FieldMember;
|
||||
import org.springsource.loaded.MethodMember;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
import org.springsource.loaded.TypeDescriptor;
|
||||
import org.springsource.loaded.TypeDescriptorExtractor;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for TypeDescriptor usage.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 1.0
|
||||
*/
|
||||
public class TypeDescriptorTests extends SpringLoadedTests {
|
||||
|
||||
@Test
|
||||
public void simpleMethodDescriptors() {
|
||||
TypeRegistry registry = getTypeRegistry("data.SimpleClass");
|
||||
byte[] bytes = loadBytesForClass("data.SimpleClass");
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, true);
|
||||
assertEquals("data/SimpleClass", typeDescriptor.getName());
|
||||
assertEquals("java/lang/Object", typeDescriptor.getSupertypeName());
|
||||
assertEquals(0, typeDescriptor.getSuperinterfacesName().length);
|
||||
assertEquals(0x20, typeDescriptor.getModifiers());
|
||||
assertEquals(0, typeDescriptor.getFields().length);
|
||||
assertEquals(5, typeDescriptor.getMethods().length); // will include catchers
|
||||
assertEquals("0x1 foo()V", typeDescriptor.getMethods()[0].toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void complexMethodDescriptors() {
|
||||
TypeRegistry registry = getTypeRegistry("data.ComplexClass");
|
||||
byte[] bytes = loadBytesForClass("data.ComplexClass");
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, true);
|
||||
assertEquals("data/ComplexClass", typeDescriptor.getName());
|
||||
assertEquals("data/SimpleClass", typeDescriptor.getSupertypeName());
|
||||
assertEquals(1, typeDescriptor.getSuperinterfacesName().length);
|
||||
assertEquals("java/io/Serializable", typeDescriptor.getSuperinterfacesName()[0]);
|
||||
assertEquals(0x20, typeDescriptor.getModifiers());
|
||||
assertEquals(3, typeDescriptor.getFields().length);
|
||||
assertEquals(9, typeDescriptor.getMethods().length);
|
||||
assertEquals("0x2 privateMethod()I", typeDescriptor.getMethods()[0].toString());
|
||||
assertEquals("0x1 publicMethod()Ljava/lang/String;", typeDescriptor.getMethods()[1].toString());
|
||||
assertEquals("0x0 defaultMethod()Ljava/util/List;", typeDescriptor.getMethods()[2].toString());
|
||||
assertEquals("0x0 thrower()V throws java/lang/Exception java/lang/IllegalStateException",
|
||||
typeDescriptor.getMethods()[3].toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fieldDescriptors() {
|
||||
TypeRegistry registry = getTypeRegistry("");
|
||||
byte[] bytes = loadBytesForClass("data.SomeFields");
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, false);
|
||||
FieldMember[] fields = typeDescriptor.getFields();
|
||||
assertEquals(4, fields.length);
|
||||
FieldMember privateField = fields[0];
|
||||
assertEquals(Modifier.PRIVATE, privateField.getModifiers());
|
||||
assertEquals("privateField", privateField.getName());
|
||||
assertEquals("I", privateField.getDescriptor());
|
||||
assertNull(privateField.getGenericSignature());
|
||||
assertEquals("0x2 I privateField", privateField.toString());
|
||||
|
||||
FieldMember publicField = fields[1];
|
||||
assertEquals(Modifier.PUBLIC, publicField.getModifiers());
|
||||
assertEquals("publicField", publicField.getName());
|
||||
assertEquals("Ljava/lang/String;", publicField.getDescriptor());
|
||||
assertNull(publicField.getGenericSignature());
|
||||
assertEquals("0x1 Ljava/lang/String; publicField", publicField.toString());
|
||||
|
||||
FieldMember defaultField = fields[2];
|
||||
assertEquals(0, defaultField.getModifiers());
|
||||
assertEquals("defaultField", defaultField.getName());
|
||||
assertEquals("Ljava/util/List;", defaultField.getDescriptor());
|
||||
assertEquals("Ljava/util/List<Ljava/lang/String;>;", defaultField.getGenericSignature());
|
||||
assertEquals("0x0 Ljava/util/List; defaultField [Ljava/util/List<Ljava/lang/String;>;]",
|
||||
defaultField.toString());
|
||||
|
||||
FieldMember protectedField = fields[3];
|
||||
assertEquals(Modifier.PROTECTED, protectedField.getModifiers());
|
||||
assertEquals("protectedField", protectedField.getName());
|
||||
assertEquals("Ljava/util/Map;", protectedField.getDescriptor());
|
||||
assertEquals("Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/Integer;>;>;",
|
||||
protectedField.getGenericSignature());
|
||||
assertEquals(
|
||||
"0x4 Ljava/util/Map; protectedField [Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/Integer;>;>;]",
|
||||
protectedField.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorDescriptors() {
|
||||
TypeRegistry registry = getTypeRegistry("");
|
||||
byte[] bytes = loadBytesForClass("data.SomeConstructors");
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, false);
|
||||
MethodMember[] ctors = typeDescriptor.getConstructors();
|
||||
assertEquals(3, ctors.length);
|
||||
|
||||
MethodMember publicCtor = ctors[0];
|
||||
assertEquals(Modifier.PUBLIC, publicCtor.getModifiers());
|
||||
assertEquals("<init>", publicCtor.getName());
|
||||
assertEquals("()V", publicCtor.getDescriptor());
|
||||
assertNull(publicCtor.getGenericSignature());
|
||||
assertEquals("0x1 <init>()V", publicCtor.toString());
|
||||
|
||||
MethodMember privateCtor = ctors[1];
|
||||
assertEquals(Modifier.PRIVATE, privateCtor.getModifiers());
|
||||
assertEquals("<init>", privateCtor.getName());
|
||||
assertEquals("(Ljava/lang/String;I)V", privateCtor.getDescriptor());
|
||||
assertNull(privateCtor.getGenericSignature());
|
||||
assertEquals("0x2 <init>(Ljava/lang/String;I)V", privateCtor.toString());
|
||||
|
||||
MethodMember protCtor = ctors[2];
|
||||
assertEquals(Modifier.PROTECTED, protCtor.getModifiers());
|
||||
assertEquals("<init>", protCtor.getName());
|
||||
assertEquals("(J)V", protCtor.getDescriptor());
|
||||
assertNull(protCtor.getGenericSignature());
|
||||
assertEquals("0x4 <init>(J)V", protCtor.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorDescriptorsAfterReloading() {
|
||||
TypeRegistry registry = getTypeRegistry("");
|
||||
String d = "data.SomeConstructors";
|
||||
ReloadableType rtype = registry.addType(d, loadBytesForClass(d));
|
||||
MethodMember[] latestConstructors = rtype.getLatestTypeDescriptor().getConstructors();
|
||||
assertEquals(3, latestConstructors.length);
|
||||
rtype.loadNewVersion("2", retrieveRename(d, d + "002"));
|
||||
latestConstructors = rtype.getLatestTypeDescriptor().getConstructors();
|
||||
assertEquals(1, latestConstructors.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultConstructorDescriptor() {
|
||||
TypeRegistry registry = getTypeRegistry("");
|
||||
byte[] bytes = loadBytesForClass("data.SomeConstructors2");
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, false);
|
||||
MethodMember[] ctors = typeDescriptor.getConstructors();
|
||||
assertEquals(1, ctors.length);
|
||||
|
||||
MethodMember publicCtor = ctors[0];
|
||||
// visibility matches type vis (for public/default)
|
||||
assertEquals(Modifier.PUBLIC, publicCtor.getModifiers());
|
||||
assertEquals("<init>", publicCtor.getName());
|
||||
assertEquals("()V", publicCtor.getDescriptor());
|
||||
assertNull(publicCtor.getGenericSignature());
|
||||
assertEquals("0x1 <init>()V", publicCtor.toString());
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.FieldMember;
|
||||
import org.springsource.loaded.MethodMember;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
import org.springsource.loaded.TypeDescriptor;
|
||||
import org.springsource.loaded.TypeDescriptorExtractor;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for TypeDescriptor usage.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 1.0
|
||||
*/
|
||||
public class TypeDescriptorTests extends SpringLoadedTests {
|
||||
|
||||
@Test
|
||||
public void simpleMethodDescriptors() {
|
||||
TypeRegistry registry = getTypeRegistry("data.SimpleClass");
|
||||
byte[] bytes = loadBytesForClass("data.SimpleClass");
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, true);
|
||||
assertEquals("data/SimpleClass", typeDescriptor.getName());
|
||||
assertEquals("java/lang/Object", typeDescriptor.getSupertypeName());
|
||||
assertEquals(0, typeDescriptor.getSuperinterfacesName().length);
|
||||
assertEquals(0x20, typeDescriptor.getModifiers());
|
||||
assertEquals(0, typeDescriptor.getFields().length);
|
||||
assertEquals(5, typeDescriptor.getMethods().length); // will include catchers
|
||||
assertEquals("0x1 foo()V", typeDescriptor.getMethods()[0].toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void complexMethodDescriptors() {
|
||||
TypeRegistry registry = getTypeRegistry("data.ComplexClass");
|
||||
byte[] bytes = loadBytesForClass("data.ComplexClass");
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, true);
|
||||
assertEquals("data/ComplexClass", typeDescriptor.getName());
|
||||
assertEquals("data/SimpleClass", typeDescriptor.getSupertypeName());
|
||||
assertEquals(1, typeDescriptor.getSuperinterfacesName().length);
|
||||
assertEquals("java/io/Serializable", typeDescriptor.getSuperinterfacesName()[0]);
|
||||
assertEquals(0x20, typeDescriptor.getModifiers());
|
||||
assertEquals(3, typeDescriptor.getFields().length);
|
||||
assertEquals(9, typeDescriptor.getMethods().length);
|
||||
assertEquals("0x2 privateMethod()I", typeDescriptor.getMethods()[0].toString());
|
||||
assertEquals("0x1 publicMethod()Ljava/lang/String;", typeDescriptor.getMethods()[1].toString());
|
||||
assertEquals("0x0 defaultMethod()Ljava/util/List;", typeDescriptor.getMethods()[2].toString());
|
||||
assertEquals("0x0 thrower()V throws java/lang/Exception java/lang/IllegalStateException",
|
||||
typeDescriptor.getMethods()[3].toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fieldDescriptors() {
|
||||
TypeRegistry registry = getTypeRegistry("");
|
||||
byte[] bytes = loadBytesForClass("data.SomeFields");
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, false);
|
||||
FieldMember[] fields = typeDescriptor.getFields();
|
||||
assertEquals(4, fields.length);
|
||||
FieldMember privateField = fields[0];
|
||||
assertEquals(Modifier.PRIVATE, privateField.getModifiers());
|
||||
assertEquals("privateField", privateField.getName());
|
||||
assertEquals("I", privateField.getDescriptor());
|
||||
assertNull(privateField.getGenericSignature());
|
||||
assertEquals("0x2 I privateField", privateField.toString());
|
||||
|
||||
FieldMember publicField = fields[1];
|
||||
assertEquals(Modifier.PUBLIC, publicField.getModifiers());
|
||||
assertEquals("publicField", publicField.getName());
|
||||
assertEquals("Ljava/lang/String;", publicField.getDescriptor());
|
||||
assertNull(publicField.getGenericSignature());
|
||||
assertEquals("0x1 Ljava/lang/String; publicField", publicField.toString());
|
||||
|
||||
FieldMember defaultField = fields[2];
|
||||
assertEquals(0, defaultField.getModifiers());
|
||||
assertEquals("defaultField", defaultField.getName());
|
||||
assertEquals("Ljava/util/List;", defaultField.getDescriptor());
|
||||
assertEquals("Ljava/util/List<Ljava/lang/String;>;", defaultField.getGenericSignature());
|
||||
assertEquals("0x0 Ljava/util/List; defaultField [Ljava/util/List<Ljava/lang/String;>;]",
|
||||
defaultField.toString());
|
||||
|
||||
FieldMember protectedField = fields[3];
|
||||
assertEquals(Modifier.PROTECTED, protectedField.getModifiers());
|
||||
assertEquals("protectedField", protectedField.getName());
|
||||
assertEquals("Ljava/util/Map;", protectedField.getDescriptor());
|
||||
assertEquals("Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/Integer;>;>;",
|
||||
protectedField.getGenericSignature());
|
||||
assertEquals(
|
||||
"0x4 Ljava/util/Map; protectedField [Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/Integer;>;>;]",
|
||||
protectedField.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorDescriptors() {
|
||||
TypeRegistry registry = getTypeRegistry("");
|
||||
byte[] bytes = loadBytesForClass("data.SomeConstructors");
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, false);
|
||||
MethodMember[] ctors = typeDescriptor.getConstructors();
|
||||
assertEquals(3, ctors.length);
|
||||
|
||||
MethodMember publicCtor = ctors[0];
|
||||
assertEquals(Modifier.PUBLIC, publicCtor.getModifiers());
|
||||
assertEquals("<init>", publicCtor.getName());
|
||||
assertEquals("()V", publicCtor.getDescriptor());
|
||||
assertNull(publicCtor.getGenericSignature());
|
||||
assertEquals("0x1 <init>()V", publicCtor.toString());
|
||||
|
||||
MethodMember privateCtor = ctors[1];
|
||||
assertEquals(Modifier.PRIVATE, privateCtor.getModifiers());
|
||||
assertEquals("<init>", privateCtor.getName());
|
||||
assertEquals("(Ljava/lang/String;I)V", privateCtor.getDescriptor());
|
||||
assertNull(privateCtor.getGenericSignature());
|
||||
assertEquals("0x2 <init>(Ljava/lang/String;I)V", privateCtor.toString());
|
||||
|
||||
MethodMember protCtor = ctors[2];
|
||||
assertEquals(Modifier.PROTECTED, protCtor.getModifiers());
|
||||
assertEquals("<init>", protCtor.getName());
|
||||
assertEquals("(J)V", protCtor.getDescriptor());
|
||||
assertNull(protCtor.getGenericSignature());
|
||||
assertEquals("0x4 <init>(J)V", protCtor.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorDescriptorsAfterReloading() {
|
||||
TypeRegistry registry = getTypeRegistry("");
|
||||
String d = "data.SomeConstructors";
|
||||
ReloadableType rtype = registry.addType(d, loadBytesForClass(d));
|
||||
MethodMember[] latestConstructors = rtype.getLatestTypeDescriptor().getConstructors();
|
||||
assertEquals(3, latestConstructors.length);
|
||||
rtype.loadNewVersion("2", retrieveRename(d, d + "002"));
|
||||
latestConstructors = rtype.getLatestTypeDescriptor().getConstructors();
|
||||
assertEquals(1, latestConstructors.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultConstructorDescriptor() {
|
||||
TypeRegistry registry = getTypeRegistry("");
|
||||
byte[] bytes = loadBytesForClass("data.SomeConstructors2");
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, false);
|
||||
MethodMember[] ctors = typeDescriptor.getConstructors();
|
||||
assertEquals(1, ctors.length);
|
||||
|
||||
MethodMember publicCtor = ctors[0];
|
||||
// visibility matches type vis (for public/default)
|
||||
assertEquals(Modifier.PUBLIC, publicCtor.getModifiers());
|
||||
assertEquals("<init>", publicCtor.getName());
|
||||
assertEquals("()V", publicCtor.getDescriptor());
|
||||
assertNull(publicCtor.getGenericSignature());
|
||||
assertEquals("0x1 <init>()V", publicCtor.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,295 +1,295 @@
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.GlobalConfiguration;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
import org.springsource.loaded.TypeDescriptor;
|
||||
import org.springsource.loaded.TypePattern;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for the TypeRegistry that exercise it in the same way it will actively be used when managing ReloadableType
|
||||
* instances.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 1.0
|
||||
*/
|
||||
public class TypeRegistryTests extends SpringLoadedTests {
|
||||
|
||||
@Test
|
||||
public void basics() {
|
||||
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
|
||||
assertNotNull(typeRegistry);
|
||||
typeRegistry = TypeRegistry.getTypeRegistryFor(null);
|
||||
assertNull(typeRegistry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same instance for two different calls passing the same classloader.
|
||||
*/
|
||||
@Test
|
||||
public void sameInstance() {
|
||||
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
|
||||
assertNotNull(typeRegistry);
|
||||
TypeRegistry typeRegistry2 = TypeRegistry.getTypeRegistryFor(binLoader);
|
||||
assertNotNull(typeRegistry2);
|
||||
assertTrue(typeRegistry == typeRegistry2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadingDescriptors() {
|
||||
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
|
||||
TypeDescriptor jloDescriptor = typeRegistry.getDescriptorFor("java/lang/Object");
|
||||
assertNotNull(jloDescriptor);
|
||||
assertEquals("java/lang/Object", jloDescriptor.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void descriptorsWithCatchers() {
|
||||
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
|
||||
TypeDescriptor dscDescriptor = typeRegistry.getDescriptorFor("data/SimpleClass");
|
||||
assertNotNull(dscDescriptor);
|
||||
assertEquals("data/SimpleClass", dscDescriptor.getName());
|
||||
// check for a catcher
|
||||
assertNotNull(findMethod("0x1 toString()Ljava/lang/String;", dscDescriptor));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void descriptorsWithCatchers2() {
|
||||
// more complicated hierarchy
|
||||
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
|
||||
TypeDescriptor topDescriptor = typeRegistry.getDescriptorFor("catchers/Top");
|
||||
assertNotNull(topDescriptor);
|
||||
// Checking no toString() catcher because Top defines a toString()
|
||||
assertEquals(5, topDescriptor.getMethods().length);
|
||||
|
||||
// if 'Top' is not considered reloadable we will get an entry for 'foo' that is inherited from it
|
||||
TypeDescriptor middleDescriptor = typeRegistry.getDescriptorFor("catchers/Middle");
|
||||
assertNotNull(middleDescriptor);
|
||||
assertEquals(5, middleDescriptor.getMethods().length);
|
||||
|
||||
TypeDescriptor bottomDescriptor = typeRegistry.getDescriptorFor("catchers/Bottom");
|
||||
assertNotNull(bottomDescriptor);
|
||||
System.out.println(bottomDescriptor.toString());
|
||||
assertEquals(5, bottomDescriptor.getMethods().length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void includesExcludes() {
|
||||
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
|
||||
assertNotNull(typeRegistry);
|
||||
|
||||
Properties p = new Properties();
|
||||
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo.Bar");
|
||||
typeRegistry.configure(p);
|
||||
List<TypePattern> tps = typeRegistry.getInclusionPatterns();
|
||||
assertEquals(1, tps.size());
|
||||
assertEquals("text:com.foo.Bar", tps.get(0).toString());
|
||||
|
||||
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo.Bar,org.springsource..*");
|
||||
typeRegistry.configure(p);
|
||||
tps = typeRegistry.getInclusionPatterns();
|
||||
System.out.println(tps);
|
||||
assertEquals(2, tps.size());
|
||||
assertEquals("text:com.foo.Bar", tps.get(0).toString());
|
||||
assertEquals("text:org.springsource..*", tps.get(1).toString());
|
||||
assertTrue(typeRegistry.isReloadableTypeName("com/foo/Bar"));
|
||||
assertFalse(typeRegistry.isReloadableTypeName("com/foo/Garr"));
|
||||
assertTrue(typeRegistry.isReloadableTypeName("org/springsource/Garr"));
|
||||
assertTrue(typeRegistry.isReloadableTypeName("org/springsource/sub/Garr"));
|
||||
assertFalse(typeRegistry.isReloadableTypeName("Boo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void includesExcludes2() {
|
||||
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
|
||||
assertNotNull(typeRegistry);
|
||||
|
||||
Properties p = new Properties();
|
||||
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo.Bar");
|
||||
typeRegistry.configure(p);
|
||||
List<TypePattern> tps = typeRegistry.getInclusionPatterns();
|
||||
assertEquals(1, tps.size());
|
||||
assertEquals("text:com.foo.Bar", tps.get(0).toString());
|
||||
assertFalse(tps.get(0).matches("com.foo.Gar"));
|
||||
assertTrue(tps.get(0).matches("com.foo.Bar"));
|
||||
|
||||
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo.Bar,org.springsource..*");
|
||||
typeRegistry.configure(p);
|
||||
tps = typeRegistry.getInclusionPatterns();
|
||||
assertEquals(2, tps.size());
|
||||
// exclude should be first
|
||||
assertEquals("text:com.foo.Bar", tps.get(0).toString());
|
||||
assertEquals("text:org.springsource..*", tps.get(1).toString());
|
||||
assertFalse(tps.get(0).matches("com.foo.Gar"));
|
||||
assertTrue(tps.get(0).matches("com.foo.Bar"));
|
||||
|
||||
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo..*");
|
||||
typeRegistry.configure(p);
|
||||
tps = typeRegistry.getInclusionPatterns();
|
||||
assertEquals(1, tps.size());
|
||||
assertFalse(tps.get(0).matches("com.goo.Bar"));
|
||||
assertTrue(tps.get(0).matches("com.foo.Bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void includesExcludes3() {
|
||||
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
|
||||
assertNotNull(typeRegistry);
|
||||
|
||||
Properties p = new Properties();
|
||||
p.setProperty(TypeRegistry.Key_Inclusions, "*");
|
||||
typeRegistry.configure(p);
|
||||
List<TypePattern> tps = typeRegistry.getInclusionPatterns();
|
||||
assertEquals(1, tps.size());
|
||||
assertEquals("text:*", tps.get(0).toString());
|
||||
assertTrue(tps.get(0).matches("wibble"));
|
||||
|
||||
p.setProperty(TypeRegistry.Key_Exclusions, "*");
|
||||
typeRegistry.configure(p);
|
||||
tps = typeRegistry.getExclusionPatterns();
|
||||
assertEquals("text:*", tps.get(0).toString());
|
||||
assertTrue(tps.get(0).matches("wibble"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadTypeBadNames() {
|
||||
TypeRegistry typeRegistry = getTypeRegistry("data.SimpleClass002");
|
||||
assertFalse(typeRegistry.isReloadableTypeName("data/SimpleClass"));
|
||||
assertFalse(typeRegistry.isReloadableTypeName("com/bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadType2() {
|
||||
TypeRegistry typeRegistry = getTypeRegistry("data.SimpleClass");
|
||||
assertTrue(typeRegistry.isReloadableTypeName("data/SimpleClass"));
|
||||
byte[] dsc = loadBytesForClass("data.SimpleClass");
|
||||
ReloadableType rtype = typeRegistry.addType("data.SimpleClass", dsc);
|
||||
assertNotNull(rtype);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rebasePaths() {
|
||||
TypeRegistry typeRegistry = getTypeRegistry("data.SimpleClass");
|
||||
Properties p = new Properties();
|
||||
p.setProperty(TypeRegistry.Key_ReloadableRebase, "a/b/c=d/e/f,g/h=x/y");
|
||||
typeRegistry.configure(p);
|
||||
Map<String, String> rebases = typeRegistry.getRebasePaths();
|
||||
assertEquals(2, rebases.keySet().size());
|
||||
String value = rebases.get("a/b/c");
|
||||
assertEquals("d/e/f", value);
|
||||
assertEquals("x/y", rebases.get("g/h"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that when the child classloader being managed by the type registry has reached the limit, it is recreated
|
||||
* and types are then defined on the fly as it is used (dispatchers/executors).
|
||||
*/
|
||||
@Test
|
||||
public void classloaderRecreation() throws Exception {
|
||||
String one = "basic.Basic";
|
||||
String two = "basic.BasicB";
|
||||
|
||||
GlobalConfiguration.maxClassDefinitions = 4;
|
||||
|
||||
TypeRegistry typeRegistry = getTypeRegistry(one + "," + two);
|
||||
|
||||
ReloadableType tOne = typeRegistry.addType(one, loadBytesForClass(one));
|
||||
ReloadableType tTwo = typeRegistry.addType(two, loadBytesForClass(two));
|
||||
|
||||
result = runUnguarded(tOne.getClazz(), "getValue");
|
||||
assertEquals(5, result.returnValue);
|
||||
|
||||
// Should be nothing defined in the child loader
|
||||
assertEquals(0, typeRegistry.getChildClassLoader().getDefinedCount());
|
||||
|
||||
tOne.loadNewVersion("002", retrieveRename(one, one + "002"));
|
||||
// Should be dispatcher and executor for the reloaded type
|
||||
assertEquals(2, typeRegistry.getChildClassLoader().getDefinedCount());
|
||||
assertEquals(7, runUnguarded(tOne.getClazz(), "getValue").returnValue);
|
||||
|
||||
tTwo.loadNewVersion("002", tTwo.bytesInitial);
|
||||
assertEquals(4, typeRegistry.getChildClassLoader().getDefinedCount());
|
||||
result = runUnguarded(tOne.getClazz(), "getValue");
|
||||
assertEquals(5, runUnguarded(tTwo.getClazz(), "getValue").returnValue);
|
||||
|
||||
Class<?> cOneExecutor = tOne.getLatestExecutorClass();
|
||||
|
||||
tOne.loadNewVersion("003", tOne.bytesInitial);
|
||||
|
||||
// Now on this reload the child classloader should be recreated as it already has more
|
||||
// than 2 defined.
|
||||
// Note: this will currently cause us to redefine all the reloadable types
|
||||
// according to their most recent version. An optimization may be to only
|
||||
// define them on demand
|
||||
tTwo.loadNewVersion("002", tTwo.bytesInitial);
|
||||
assertEquals(4, typeRegistry.getChildClassLoader().getDefinedCount());
|
||||
assertEquals(5, runUnguarded(tTwo.getClazz(), "getValue").returnValue);
|
||||
|
||||
// But what about calling the older types?
|
||||
assertEquals(5, runUnguarded(tOne.getClazz(), "getValue").returnValue);
|
||||
if (cOneExecutor == tOne.getLatestExecutorClass()) {
|
||||
fail("Why are we not using a new executor? the old one should have been removed, freeing up the classloader");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checking that the counting is working correctly for the managed classloader.
|
||||
*/
|
||||
@Test
|
||||
public void classloaderCounting() throws Exception {
|
||||
String one = "basic.Basic";
|
||||
String two = "basic.BasicB";
|
||||
String three = "basic.BasicC";
|
||||
|
||||
TypeRegistry typeRegistry = getTypeRegistry(one + "," + two + "," + three);
|
||||
|
||||
ReloadableType tOne = typeRegistry.addType(one, loadBytesForClass(one));
|
||||
ReloadableType tTwo = typeRegistry.addType(two, loadBytesForClass(two));
|
||||
ReloadableType tThree = typeRegistry.addType(three, loadBytesForClass(three));
|
||||
|
||||
result = runUnguarded(tOne.getClazz(), "getValue");
|
||||
assertEquals(5, result.returnValue);
|
||||
|
||||
// Should be nothing defined in the child loader
|
||||
assertEquals(0, typeRegistry.getChildClassLoader().getDefinedCount());
|
||||
|
||||
tOne.loadNewVersion("002", retrieveRename(one, one + "002"));
|
||||
// Should be dispatcher and executor for the reloaded type
|
||||
assertEquals(2, typeRegistry.getChildClassLoader().getDefinedCount());
|
||||
|
||||
tTwo.loadNewVersion("002", tTwo.bytesInitial);
|
||||
assertEquals(4, typeRegistry.getChildClassLoader().getDefinedCount());
|
||||
|
||||
tThree.loadNewVersion("002", tThree.bytesInitial);
|
||||
assertEquals(6, typeRegistry.getChildClassLoader().getDefinedCount());
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.GlobalConfiguration;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
import org.springsource.loaded.TypeDescriptor;
|
||||
import org.springsource.loaded.TypePattern;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for the TypeRegistry that exercise it in the same way it will actively be used when managing ReloadableType
|
||||
* instances.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 1.0
|
||||
*/
|
||||
public class TypeRegistryTests extends SpringLoadedTests {
|
||||
|
||||
@Test
|
||||
public void basics() {
|
||||
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
|
||||
assertNotNull(typeRegistry);
|
||||
typeRegistry = TypeRegistry.getTypeRegistryFor(null);
|
||||
assertNull(typeRegistry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same instance for two different calls passing the same classloader.
|
||||
*/
|
||||
@Test
|
||||
public void sameInstance() {
|
||||
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
|
||||
assertNotNull(typeRegistry);
|
||||
TypeRegistry typeRegistry2 = TypeRegistry.getTypeRegistryFor(binLoader);
|
||||
assertNotNull(typeRegistry2);
|
||||
assertTrue(typeRegistry == typeRegistry2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadingDescriptors() {
|
||||
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
|
||||
TypeDescriptor jloDescriptor = typeRegistry.getDescriptorFor("java/lang/Object");
|
||||
assertNotNull(jloDescriptor);
|
||||
assertEquals("java/lang/Object", jloDescriptor.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void descriptorsWithCatchers() {
|
||||
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
|
||||
TypeDescriptor dscDescriptor = typeRegistry.getDescriptorFor("data/SimpleClass");
|
||||
assertNotNull(dscDescriptor);
|
||||
assertEquals("data/SimpleClass", dscDescriptor.getName());
|
||||
// check for a catcher
|
||||
assertNotNull(findMethod("0x1 toString()Ljava/lang/String;", dscDescriptor));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void descriptorsWithCatchers2() {
|
||||
// more complicated hierarchy
|
||||
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
|
||||
TypeDescriptor topDescriptor = typeRegistry.getDescriptorFor("catchers/Top");
|
||||
assertNotNull(topDescriptor);
|
||||
// Checking no toString() catcher because Top defines a toString()
|
||||
assertEquals(5, topDescriptor.getMethods().length);
|
||||
|
||||
// if 'Top' is not considered reloadable we will get an entry for 'foo' that is inherited from it
|
||||
TypeDescriptor middleDescriptor = typeRegistry.getDescriptorFor("catchers/Middle");
|
||||
assertNotNull(middleDescriptor);
|
||||
assertEquals(5, middleDescriptor.getMethods().length);
|
||||
|
||||
TypeDescriptor bottomDescriptor = typeRegistry.getDescriptorFor("catchers/Bottom");
|
||||
assertNotNull(bottomDescriptor);
|
||||
System.out.println(bottomDescriptor.toString());
|
||||
assertEquals(5, bottomDescriptor.getMethods().length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void includesExcludes() {
|
||||
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
|
||||
assertNotNull(typeRegistry);
|
||||
|
||||
Properties p = new Properties();
|
||||
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo.Bar");
|
||||
typeRegistry.configure(p);
|
||||
List<TypePattern> tps = typeRegistry.getInclusionPatterns();
|
||||
assertEquals(1, tps.size());
|
||||
assertEquals("text:com.foo.Bar", tps.get(0).toString());
|
||||
|
||||
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo.Bar,org.springsource..*");
|
||||
typeRegistry.configure(p);
|
||||
tps = typeRegistry.getInclusionPatterns();
|
||||
System.out.println(tps);
|
||||
assertEquals(2, tps.size());
|
||||
assertEquals("text:com.foo.Bar", tps.get(0).toString());
|
||||
assertEquals("text:org.springsource..*", tps.get(1).toString());
|
||||
assertTrue(typeRegistry.isReloadableTypeName("com/foo/Bar"));
|
||||
assertFalse(typeRegistry.isReloadableTypeName("com/foo/Garr"));
|
||||
assertTrue(typeRegistry.isReloadableTypeName("org/springsource/Garr"));
|
||||
assertTrue(typeRegistry.isReloadableTypeName("org/springsource/sub/Garr"));
|
||||
assertFalse(typeRegistry.isReloadableTypeName("Boo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void includesExcludes2() {
|
||||
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
|
||||
assertNotNull(typeRegistry);
|
||||
|
||||
Properties p = new Properties();
|
||||
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo.Bar");
|
||||
typeRegistry.configure(p);
|
||||
List<TypePattern> tps = typeRegistry.getInclusionPatterns();
|
||||
assertEquals(1, tps.size());
|
||||
assertEquals("text:com.foo.Bar", tps.get(0).toString());
|
||||
assertFalse(tps.get(0).matches("com.foo.Gar"));
|
||||
assertTrue(tps.get(0).matches("com.foo.Bar"));
|
||||
|
||||
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo.Bar,org.springsource..*");
|
||||
typeRegistry.configure(p);
|
||||
tps = typeRegistry.getInclusionPatterns();
|
||||
assertEquals(2, tps.size());
|
||||
// exclude should be first
|
||||
assertEquals("text:com.foo.Bar", tps.get(0).toString());
|
||||
assertEquals("text:org.springsource..*", tps.get(1).toString());
|
||||
assertFalse(tps.get(0).matches("com.foo.Gar"));
|
||||
assertTrue(tps.get(0).matches("com.foo.Bar"));
|
||||
|
||||
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo..*");
|
||||
typeRegistry.configure(p);
|
||||
tps = typeRegistry.getInclusionPatterns();
|
||||
assertEquals(1, tps.size());
|
||||
assertFalse(tps.get(0).matches("com.goo.Bar"));
|
||||
assertTrue(tps.get(0).matches("com.foo.Bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void includesExcludes3() {
|
||||
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
|
||||
assertNotNull(typeRegistry);
|
||||
|
||||
Properties p = new Properties();
|
||||
p.setProperty(TypeRegistry.Key_Inclusions, "*");
|
||||
typeRegistry.configure(p);
|
||||
List<TypePattern> tps = typeRegistry.getInclusionPatterns();
|
||||
assertEquals(1, tps.size());
|
||||
assertEquals("text:*", tps.get(0).toString());
|
||||
assertTrue(tps.get(0).matches("wibble"));
|
||||
|
||||
p.setProperty(TypeRegistry.Key_Exclusions, "*");
|
||||
typeRegistry.configure(p);
|
||||
tps = typeRegistry.getExclusionPatterns();
|
||||
assertEquals("text:*", tps.get(0).toString());
|
||||
assertTrue(tps.get(0).matches("wibble"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadTypeBadNames() {
|
||||
TypeRegistry typeRegistry = getTypeRegistry("data.SimpleClass002");
|
||||
assertFalse(typeRegistry.isReloadableTypeName("data/SimpleClass"));
|
||||
assertFalse(typeRegistry.isReloadableTypeName("com/bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadType2() {
|
||||
TypeRegistry typeRegistry = getTypeRegistry("data.SimpleClass");
|
||||
assertTrue(typeRegistry.isReloadableTypeName("data/SimpleClass"));
|
||||
byte[] dsc = loadBytesForClass("data.SimpleClass");
|
||||
ReloadableType rtype = typeRegistry.addType("data.SimpleClass", dsc);
|
||||
assertNotNull(rtype);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rebasePaths() {
|
||||
TypeRegistry typeRegistry = getTypeRegistry("data.SimpleClass");
|
||||
Properties p = new Properties();
|
||||
p.setProperty(TypeRegistry.Key_ReloadableRebase, "a/b/c=d/e/f,g/h=x/y");
|
||||
typeRegistry.configure(p);
|
||||
Map<String, String> rebases = typeRegistry.getRebasePaths();
|
||||
assertEquals(2, rebases.keySet().size());
|
||||
String value = rebases.get("a/b/c");
|
||||
assertEquals("d/e/f", value);
|
||||
assertEquals("x/y", rebases.get("g/h"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that when the child classloader being managed by the type registry has reached the limit, it is recreated
|
||||
* and types are then defined on the fly as it is used (dispatchers/executors).
|
||||
*/
|
||||
@Test
|
||||
public void classloaderRecreation() throws Exception {
|
||||
String one = "basic.Basic";
|
||||
String two = "basic.BasicB";
|
||||
|
||||
GlobalConfiguration.maxClassDefinitions = 4;
|
||||
|
||||
TypeRegistry typeRegistry = getTypeRegistry(one + "," + two);
|
||||
|
||||
ReloadableType tOne = typeRegistry.addType(one, loadBytesForClass(one));
|
||||
ReloadableType tTwo = typeRegistry.addType(two, loadBytesForClass(two));
|
||||
|
||||
result = runUnguarded(tOne.getClazz(), "getValue");
|
||||
assertEquals(5, result.returnValue);
|
||||
|
||||
// Should be nothing defined in the child loader
|
||||
assertEquals(0, typeRegistry.getChildClassLoader().getDefinedCount());
|
||||
|
||||
tOne.loadNewVersion("002", retrieveRename(one, one + "002"));
|
||||
// Should be dispatcher and executor for the reloaded type
|
||||
assertEquals(2, typeRegistry.getChildClassLoader().getDefinedCount());
|
||||
assertEquals(7, runUnguarded(tOne.getClazz(), "getValue").returnValue);
|
||||
|
||||
tTwo.loadNewVersion("002", tTwo.bytesInitial);
|
||||
assertEquals(4, typeRegistry.getChildClassLoader().getDefinedCount());
|
||||
result = runUnguarded(tOne.getClazz(), "getValue");
|
||||
assertEquals(5, runUnguarded(tTwo.getClazz(), "getValue").returnValue);
|
||||
|
||||
Class<?> cOneExecutor = tOne.getLatestExecutorClass();
|
||||
|
||||
tOne.loadNewVersion("003", tOne.bytesInitial);
|
||||
|
||||
// Now on this reload the child classloader should be recreated as it already has more
|
||||
// than 2 defined.
|
||||
// Note: this will currently cause us to redefine all the reloadable types
|
||||
// according to their most recent version. An optimization may be to only
|
||||
// define them on demand
|
||||
tTwo.loadNewVersion("002", tTwo.bytesInitial);
|
||||
assertEquals(4, typeRegistry.getChildClassLoader().getDefinedCount());
|
||||
assertEquals(5, runUnguarded(tTwo.getClazz(), "getValue").returnValue);
|
||||
|
||||
// But what about calling the older types?
|
||||
assertEquals(5, runUnguarded(tOne.getClazz(), "getValue").returnValue);
|
||||
if (cOneExecutor == tOne.getLatestExecutorClass()) {
|
||||
fail("Why are we not using a new executor? the old one should have been removed, freeing up the classloader");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checking that the counting is working correctly for the managed classloader.
|
||||
*/
|
||||
@Test
|
||||
public void classloaderCounting() throws Exception {
|
||||
String one = "basic.Basic";
|
||||
String two = "basic.BasicB";
|
||||
String three = "basic.BasicC";
|
||||
|
||||
TypeRegistry typeRegistry = getTypeRegistry(one + "," + two + "," + three);
|
||||
|
||||
ReloadableType tOne = typeRegistry.addType(one, loadBytesForClass(one));
|
||||
ReloadableType tTwo = typeRegistry.addType(two, loadBytesForClass(two));
|
||||
ReloadableType tThree = typeRegistry.addType(three, loadBytesForClass(three));
|
||||
|
||||
result = runUnguarded(tOne.getClazz(), "getValue");
|
||||
assertEquals(5, result.returnValue);
|
||||
|
||||
// Should be nothing defined in the child loader
|
||||
assertEquals(0, typeRegistry.getChildClassLoader().getDefinedCount());
|
||||
|
||||
tOne.loadNewVersion("002", retrieveRename(one, one + "002"));
|
||||
// Should be dispatcher and executor for the reloaded type
|
||||
assertEquals(2, typeRegistry.getChildClassLoader().getDefinedCount());
|
||||
|
||||
tTwo.loadNewVersion("002", tTwo.bytesInitial);
|
||||
assertEquals(4, typeRegistry.getChildClassLoader().getDefinedCount());
|
||||
|
||||
tThree.loadNewVersion("002", tThree.bytesInitial);
|
||||
assertEquals(6, typeRegistry.getChildClassLoader().getDefinedCount());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,128 +1,128 @@
|
||||
/*
|
||||
* 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.test.infra;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.springsource.loaded.Constants;
|
||||
import org.springsource.loaded.Utils;
|
||||
|
||||
|
||||
/**
|
||||
* MethodVisitor that records events - very useful for testing
|
||||
*/
|
||||
public class FakeMethodVisitor extends MethodVisitor implements Constants {
|
||||
|
||||
public FakeMethodVisitor() {
|
||||
super(ASM5);
|
||||
}
|
||||
|
||||
StringBuilder events = new StringBuilder();
|
||||
|
||||
public String getEvents() {
|
||||
return events.toString().trim();
|
||||
}
|
||||
|
||||
public void clearEvents() {
|
||||
events = new StringBuilder();
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotationDefault() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitAttribute(Attribute attr) {
|
||||
|
||||
}
|
||||
|
||||
public void visitCode() {
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
}
|
||||
|
||||
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
|
||||
}
|
||||
|
||||
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
|
||||
}
|
||||
|
||||
public void visitIincInsn(int var, int increment) {
|
||||
}
|
||||
|
||||
public void visitInsn(int opcode) {
|
||||
events.append("visitInsn(" + Utils.toOpcodeString(opcode) + ") ");
|
||||
}
|
||||
|
||||
public void visitIntInsn(int opcode, int operand) {
|
||||
}
|
||||
|
||||
public void visitJumpInsn(int opcode, Label label) {
|
||||
}
|
||||
|
||||
public void visitLabel(Label label) {
|
||||
}
|
||||
|
||||
public void visitLdcInsn(Object cst) {
|
||||
events.append("visitLdcInsn(" + cst + ") ");
|
||||
}
|
||||
|
||||
public void visitLineNumber(int line, Label start) {
|
||||
}
|
||||
|
||||
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
|
||||
}
|
||||
|
||||
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
|
||||
}
|
||||
|
||||
public void visitMaxs(int maxStack, int maxLocals) {
|
||||
}
|
||||
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
|
||||
events.append("visitMethodInsn(" + Utils.toOpcodeString(opcode) + "," + owner + "," + name + "," + desc + ") ");
|
||||
}
|
||||
|
||||
public void visitMultiANewArrayInsn(String desc, int dims) {
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
|
||||
}
|
||||
|
||||
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
|
||||
|
||||
}
|
||||
|
||||
public void visitTypeInsn(int opcode, String type) {
|
||||
events.append("visitTypeInsn(" + Utils.toOpcodeString(opcode) + "," + type + ") ");
|
||||
}
|
||||
|
||||
public void visitVarInsn(int opcode, int var) {
|
||||
events.append("visitVarInsn(" + Utils.toOpcodeString(opcode) + "," + var + ") ");
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.test.infra;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.springsource.loaded.Constants;
|
||||
import org.springsource.loaded.Utils;
|
||||
|
||||
|
||||
/**
|
||||
* MethodVisitor that records events - very useful for testing
|
||||
*/
|
||||
public class FakeMethodVisitor extends MethodVisitor implements Constants {
|
||||
|
||||
public FakeMethodVisitor() {
|
||||
super(ASM5);
|
||||
}
|
||||
|
||||
StringBuilder events = new StringBuilder();
|
||||
|
||||
public String getEvents() {
|
||||
return events.toString().trim();
|
||||
}
|
||||
|
||||
public void clearEvents() {
|
||||
events = new StringBuilder();
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotationDefault() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitAttribute(Attribute attr) {
|
||||
|
||||
}
|
||||
|
||||
public void visitCode() {
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
}
|
||||
|
||||
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
|
||||
}
|
||||
|
||||
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
|
||||
}
|
||||
|
||||
public void visitIincInsn(int var, int increment) {
|
||||
}
|
||||
|
||||
public void visitInsn(int opcode) {
|
||||
events.append("visitInsn(" + Utils.toOpcodeString(opcode) + ") ");
|
||||
}
|
||||
|
||||
public void visitIntInsn(int opcode, int operand) {
|
||||
}
|
||||
|
||||
public void visitJumpInsn(int opcode, Label label) {
|
||||
}
|
||||
|
||||
public void visitLabel(Label label) {
|
||||
}
|
||||
|
||||
public void visitLdcInsn(Object cst) {
|
||||
events.append("visitLdcInsn(" + cst + ") ");
|
||||
}
|
||||
|
||||
public void visitLineNumber(int line, Label start) {
|
||||
}
|
||||
|
||||
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
|
||||
}
|
||||
|
||||
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
|
||||
}
|
||||
|
||||
public void visitMaxs(int maxStack, int maxLocals) {
|
||||
}
|
||||
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
|
||||
events.append("visitMethodInsn(" + Utils.toOpcodeString(opcode) + "," + owner + "," + name + "," + desc + ") ");
|
||||
}
|
||||
|
||||
public void visitMultiANewArrayInsn(String desc, int dims) {
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
|
||||
}
|
||||
|
||||
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
|
||||
|
||||
}
|
||||
|
||||
public void visitTypeInsn(int opcode, String type) {
|
||||
events.append("visitTypeInsn(" + Utils.toOpcodeString(opcode) + "," + type + ") ");
|
||||
}
|
||||
|
||||
public void visitVarInsn(int opcode, int var) {
|
||||
events.append("visitVarInsn(" + Utils.toOpcodeString(opcode) + "," + var + ") ");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,99 +1,99 @@
|
||||
/*
|
||||
* 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.test.infra;
|
||||
|
||||
/**
|
||||
* Captures the result of executing a method: the return value from the method, the stdout and the stderr.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 1.0
|
||||
*/
|
||||
public class Result implements IResult {
|
||||
|
||||
public Object returnValue;
|
||||
|
||||
public final String stdout;
|
||||
|
||||
public final String stderr;
|
||||
|
||||
public Result(Object returnValue, String stdout, String stderr) {
|
||||
this.returnValue = returnValue;
|
||||
if (stdout.endsWith("\n")) {
|
||||
stdout = stdout.substring(0, stdout.length() - 1);
|
||||
}
|
||||
if (stderr.endsWith("\n")) {
|
||||
stderr = stderr.substring(0, stderr.length() - 1);
|
||||
}
|
||||
this.stdout = stdout.trim();
|
||||
this.stderr = stderr.trim();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("============================\n");
|
||||
sb.append("ReturnValue was '" + returnValue + "'\n");
|
||||
sb.append("STDOUT was\n" + stdout + "\n");
|
||||
sb.append("STDERR was\n" + stderr + "\n");
|
||||
sb.append("============================\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((returnValue == null) ? 0 : returnValue.hashCode());
|
||||
result = prime * result + ((stderr == null) ? 0 : stderr.hashCode());
|
||||
result = prime * result + ((stdout == null) ? 0 : stdout.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Result other = (Result) obj;
|
||||
if (returnValue == null) {
|
||||
if (other.returnValue != null)
|
||||
return false;
|
||||
}
|
||||
else if (!returnValue.equals(other.returnValue))
|
||||
return false;
|
||||
if (stderr == null) {
|
||||
if (other.stderr != null)
|
||||
return false;
|
||||
}
|
||||
else if (!stderr.equals(other.stderr))
|
||||
return false;
|
||||
if (stdout == null) {
|
||||
if (other.stdout != null)
|
||||
return false;
|
||||
}
|
||||
else if (!stdout.equals(other.stdout))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getSummary() {
|
||||
return "" + returnValue;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.test.infra;
|
||||
|
||||
/**
|
||||
* Captures the result of executing a method: the return value from the method, the stdout and the stderr.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 1.0
|
||||
*/
|
||||
public class Result implements IResult {
|
||||
|
||||
public Object returnValue;
|
||||
|
||||
public final String stdout;
|
||||
|
||||
public final String stderr;
|
||||
|
||||
public Result(Object returnValue, String stdout, String stderr) {
|
||||
this.returnValue = returnValue;
|
||||
if (stdout.endsWith("\n")) {
|
||||
stdout = stdout.substring(0, stdout.length() - 1);
|
||||
}
|
||||
if (stderr.endsWith("\n")) {
|
||||
stderr = stderr.substring(0, stderr.length() - 1);
|
||||
}
|
||||
this.stdout = stdout.trim();
|
||||
this.stderr = stderr.trim();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("============================\n");
|
||||
sb.append("ReturnValue was '" + returnValue + "'\n");
|
||||
sb.append("STDOUT was\n" + stdout + "\n");
|
||||
sb.append("STDERR was\n" + stderr + "\n");
|
||||
sb.append("============================\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((returnValue == null) ? 0 : returnValue.hashCode());
|
||||
result = prime * result + ((stderr == null) ? 0 : stderr.hashCode());
|
||||
result = prime * result + ((stdout == null) ? 0 : stdout.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Result other = (Result) obj;
|
||||
if (returnValue == null) {
|
||||
if (other.returnValue != null)
|
||||
return false;
|
||||
}
|
||||
else if (!returnValue.equals(other.returnValue))
|
||||
return false;
|
||||
if (stderr == null) {
|
||||
if (other.stderr != null)
|
||||
return false;
|
||||
}
|
||||
else if (!stderr.equals(other.stderr))
|
||||
return false;
|
||||
if (stdout == null) {
|
||||
if (other.stdout != null)
|
||||
return false;
|
||||
}
|
||||
else if (!stdout.equals(other.stdout))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getSummary() {
|
||||
return "" + returnValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,139 +1,139 @@
|
||||
/*
|
||||
* 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.test.infra;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import org.springsource.loaded.testgen.GenerativeSpringLoadedTest;
|
||||
|
||||
|
||||
/**
|
||||
* Captures the exception raised by executing a method: the exception the stdout and the stderr.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
* @since 1.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ResultException extends Exception implements IResult {
|
||||
|
||||
public final Throwable exception;
|
||||
|
||||
public final String stdout;
|
||||
|
||||
public final String stderr;
|
||||
|
||||
public ResultException(Throwable exception, String stdout, String stderr) {
|
||||
super(exception);
|
||||
this.exception = exception;
|
||||
if (stdout.endsWith("\n")) {
|
||||
stdout = stdout.substring(0, stdout.length() - 1);
|
||||
}
|
||||
if (stderr.endsWith("\n")) {
|
||||
stderr = stderr.substring(0, stderr.length() - 1);
|
||||
}
|
||||
this.stdout = stdout.trim();
|
||||
this.stderr = stderr.trim();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("============================\n");
|
||||
sb.append("Exception was '" + exception.getClass() + "'\n");
|
||||
sb.append("Exception msg '" + exception.getMessage() + "'\n");
|
||||
sb.append(getStackTraceAsString());
|
||||
sb.append("STDOUT was\n" + stdout + "\n");
|
||||
sb.append("STDERR was\n" + stderr + "\n");
|
||||
sb.append("============================\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getStackTraceAsString() {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
exception.printStackTrace(new PrintWriter(stringWriter));
|
||||
return stringWriter.toString();
|
||||
}
|
||||
|
||||
public Throwable getDeepestCause() {
|
||||
Throwable cause = exception;
|
||||
while (cause.getCause() != null) {
|
||||
cause = cause.getCause();
|
||||
}
|
||||
return cause;
|
||||
}
|
||||
|
||||
private int getNestingLevel() {
|
||||
int nestingLevel = 0;
|
||||
Throwable cause = exception;
|
||||
while (cause.getCause() != null) {
|
||||
nestingLevel++;
|
||||
cause = cause.getCause();
|
||||
}
|
||||
return nestingLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* This equals method is used by {@link GenerativeSpringLoadedTest}s to determine whether the behavior observed on
|
||||
* regular Java and SpringLoaded are accepted as sufficiently equivalent to pass the test.
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
//1) We are both ResultExpeptions!
|
||||
if (obj.getClass() != this.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ResultException other = (ResultException) obj;
|
||||
|
||||
//2) We are the same type of Exception
|
||||
if (other.exception.getClass() != this.exception.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//3) We have the same nesting depth
|
||||
if (other.getNestingLevel() != this.getNestingLevel()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//4) Deepest causes are of same type
|
||||
Throwable myCause = this.getDeepestCause();
|
||||
Throwable otherCause = other.getDeepestCause();
|
||||
if (myCause.getClass() != otherCause.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//5) Deepest causes have same message
|
||||
if (myCause.getMessage() == null) {
|
||||
return otherCause.getMessage() == null;
|
||||
}
|
||||
return myCause.getMessage().equals(otherCause.getMessage());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
Throwable e = getDeepestCause();
|
||||
int hash = e.getClass().hashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
public String getSummary() {
|
||||
Throwable cause = getDeepestCause();
|
||||
return cause.getClass().getSimpleName() + " " + cause.getMessage();
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.test.infra;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import org.springsource.loaded.testgen.GenerativeSpringLoadedTest;
|
||||
|
||||
|
||||
/**
|
||||
* Captures the exception raised by executing a method: the exception the stdout and the stderr.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
* @since 1.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ResultException extends Exception implements IResult {
|
||||
|
||||
public final Throwable exception;
|
||||
|
||||
public final String stdout;
|
||||
|
||||
public final String stderr;
|
||||
|
||||
public ResultException(Throwable exception, String stdout, String stderr) {
|
||||
super(exception);
|
||||
this.exception = exception;
|
||||
if (stdout.endsWith("\n")) {
|
||||
stdout = stdout.substring(0, stdout.length() - 1);
|
||||
}
|
||||
if (stderr.endsWith("\n")) {
|
||||
stderr = stderr.substring(0, stderr.length() - 1);
|
||||
}
|
||||
this.stdout = stdout.trim();
|
||||
this.stderr = stderr.trim();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("============================\n");
|
||||
sb.append("Exception was '" + exception.getClass() + "'\n");
|
||||
sb.append("Exception msg '" + exception.getMessage() + "'\n");
|
||||
sb.append(getStackTraceAsString());
|
||||
sb.append("STDOUT was\n" + stdout + "\n");
|
||||
sb.append("STDERR was\n" + stderr + "\n");
|
||||
sb.append("============================\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getStackTraceAsString() {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
exception.printStackTrace(new PrintWriter(stringWriter));
|
||||
return stringWriter.toString();
|
||||
}
|
||||
|
||||
public Throwable getDeepestCause() {
|
||||
Throwable cause = exception;
|
||||
while (cause.getCause() != null) {
|
||||
cause = cause.getCause();
|
||||
}
|
||||
return cause;
|
||||
}
|
||||
|
||||
private int getNestingLevel() {
|
||||
int nestingLevel = 0;
|
||||
Throwable cause = exception;
|
||||
while (cause.getCause() != null) {
|
||||
nestingLevel++;
|
||||
cause = cause.getCause();
|
||||
}
|
||||
return nestingLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* This equals method is used by {@link GenerativeSpringLoadedTest}s to determine whether the behavior observed on
|
||||
* regular Java and SpringLoaded are accepted as sufficiently equivalent to pass the test.
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
//1) We are both ResultExpeptions!
|
||||
if (obj.getClass() != this.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ResultException other = (ResultException) obj;
|
||||
|
||||
//2) We are the same type of Exception
|
||||
if (other.exception.getClass() != this.exception.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//3) We have the same nesting depth
|
||||
if (other.getNestingLevel() != this.getNestingLevel()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//4) Deepest causes are of same type
|
||||
Throwable myCause = this.getDeepestCause();
|
||||
Throwable otherCause = other.getDeepestCause();
|
||||
if (myCause.getClass() != otherCause.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//5) Deepest causes have same message
|
||||
if (myCause.getMessage() == null) {
|
||||
return otherCause.getMessage() == null;
|
||||
}
|
||||
return myCause.getMessage().equals(otherCause.getMessage());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
Throwable e = getDeepestCause();
|
||||
int hash = e.getClass().hashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
public String getSummary() {
|
||||
Throwable cause = getDeepestCause();
|
||||
return cause.getClass().getSimpleName() + " " + cause.getMessage();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,80 +1,80 @@
|
||||
/*
|
||||
* 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.test.infra;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Enumeration;
|
||||
|
||||
public class TestClassLoader extends URLClassLoader {
|
||||
|
||||
private static int idCt = 0;
|
||||
|
||||
private int id = idCt++;
|
||||
|
||||
public TestClassLoader(URL[] urls, ClassLoader parent) {
|
||||
super(urls, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized Class<?> loadClass(String arg0, boolean arg1) throws ClassNotFoundException {
|
||||
// System.out.println(this+" being asked to load class "+arg0+","+arg1);
|
||||
return super.loadClass(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getResource(String name) {
|
||||
// System.out.println(this+" being asked to getResource "+name);
|
||||
return super.getResource(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL findResource(String arg0) {
|
||||
// System.out.println(this+" being asked to find resource "+arg0);
|
||||
return super.findResource(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
// System.out.println(this+" being asked to find class "+name);
|
||||
return super.findClass(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||
// System.out.println(this + " loading "+name);
|
||||
return super.loadClass(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<URL> findResources(String name) throws IOException {
|
||||
// System.out.println(this+" being asked to find resources "+name);
|
||||
return super.findResources(name);
|
||||
}
|
||||
|
||||
public Class<?> defineTheClass(String name, byte[] bytes) {
|
||||
// System.out.println(this + " defining "+name);
|
||||
return super.defineClass(name, bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ClassLoader( " + id + " )";
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.test.infra;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Enumeration;
|
||||
|
||||
public class TestClassLoader extends URLClassLoader {
|
||||
|
||||
private static int idCt = 0;
|
||||
|
||||
private int id = idCt++;
|
||||
|
||||
public TestClassLoader(URL[] urls, ClassLoader parent) {
|
||||
super(urls, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized Class<?> loadClass(String arg0, boolean arg1) throws ClassNotFoundException {
|
||||
// System.out.println(this+" being asked to load class "+arg0+","+arg1);
|
||||
return super.loadClass(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getResource(String name) {
|
||||
// System.out.println(this+" being asked to getResource "+name);
|
||||
return super.getResource(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL findResource(String arg0) {
|
||||
// System.out.println(this+" being asked to find resource "+arg0);
|
||||
return super.findResource(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
// System.out.println(this+" being asked to find class "+name);
|
||||
return super.findClass(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||
// System.out.println(this + " loading "+name);
|
||||
return super.loadClass(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<URL> findResources(String name) throws IOException {
|
||||
// System.out.println(this+" being asked to find resources "+name);
|
||||
return super.findResources(name);
|
||||
}
|
||||
|
||||
public Class<?> defineTheClass(String name, byte[] bytes) {
|
||||
// System.out.println(this + " defining "+name);
|
||||
return super.defineClass(name, bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ClassLoader( " + id + " )";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user