/*******************************************************************************
* 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.apache.kato.tck.harness;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import javax.tools.diagnostics.FactoryRegistry;
import javax.tools.diagnostics.image.CorruptData;
import javax.tools.diagnostics.image.CorruptDataException;
import javax.tools.diagnostics.runtime.java.JavaClass;
import javax.tools.diagnostics.runtime.java.JavaClassLoader;
import javax.tools.diagnostics.runtime.java.JavaField;
import javax.tools.diagnostics.runtime.java.JavaHeap;
import javax.tools.diagnostics.runtime.java.JavaObject;
import javax.tools.diagnostics.runtime.java.JavaRuntime;
import org.apache.kato.tck.api.IJavaRuntimeCreator;
public abstract class TCKJavaRuntimeTestcase extends AbstractTCKTestcase {
/**
*
*/
private static final String DUMPPATH_PROPERTY = "org.apache.kato.dumppath";
private static JavaRuntime runtime=null;
private static IJavaRuntimeCreator creator=null;
/**
* Convert an address to a 0x hex number
* @param address
* @return A string representing the address
*/
public static String format(long address) {
return "0x"+Long.toHexString(address);
}
/**
* Retrieves a field from a JavaClass by name.
*
* @param javaClass JavaClass to get field from
* @param fieldName name of field to retrieve.
* @return JavaField found, or null if not found.
* @throws CorruptDataException
*/
public static JavaField getJavaField(JavaClass javaClass, String fieldName)
throws CorruptDataException {
JavaField foundField = null;
Iterator fields = javaClass.getDeclaredFields().iterator();
while (fields.hasNext()) {
JavaField nextField = (JavaField) fields.next();
if (nextField.getName().equals(fieldName)) {
foundField = nextField;
break;
}
}
return foundField;
}
/**
* Retrieve a JavaField by name from a JavaObject.
* You must use the JavaField on the passed JavaObject
* to get the actual field contents though.
*
* @param javaObject JavaObject to retrieve the JavaField from.
* @param fieldName The name of the JavaField to retrieve
* @return JavaField or null if not found.
* @throws CorruptDataException
*/
public static JavaField getJavaField(JavaObject javaObject, String fieldName)
throws CorruptDataException {
return getJavaField(javaObject.getJavaClass(), fieldName);
}
public JavaRuntime getJavaRuntime()
{
if(runtime==null) {
String dumpFileNamePath=System.getProperty(DUMPPATH_PROPERTY);
if(dumpFileNamePath==null) report("Missing dump path. Set System Property "+DUMPPATH_PROPERTY+" to full path to the dump");
try {
File dump=new File(dumpFileNamePath);
runtime=FactoryRegistry.getDefaultRegistry().getJavaRuntime(dump);
} catch (IOException e) {
report(e,"unable to create Java runtime image from dump "+dumpFileNamePath);
}
if(runtime==null) report("unable to create Java runtime image from dump "+dumpFileNamePath);
}
return runtime;
}
/**
* Returns the system classloader for runtime.
* @return the JavaClassLoader object representing the system classloader.
*/
protected JavaClassLoader getBootClassLoader() {
JavaRuntime run = getJavaRuntime();
// Find the bootstrap loader using the idea that it it the only loader to have loaded itself
JavaClassLoader bootLoader = null;
HashMap loaders = new HashMap();
for (Iterator i = run.getJavaClassLoaders().iterator(); i.hasNext();) {
Object next = i.next();
if (next instanceof CorruptData) {
continue;
}
JavaClassLoader jcl = (JavaClassLoader)next;
try {
JavaObject loaderObject = jcl.getObject();
// Remember the class loader
loaders.put(loaderObject, jcl);
if (loaderObject == null) {
// Potential boot loader
System.out.println("Found class loader with null Java object "+jcl);
bootLoader = jcl;
break;
} else if (loaderObject.getJavaClass().getName().equals("*System*")) {
System.out.println("Found class loader of type *System* "+jcl);
bootLoader = jcl;
break;
} else {
JavaClass loadObjectClass = loaderObject.getJavaClass();
System.out.println("Found class loader "+loadObjectClass.getName()+" at "+format(loaderObject.getID().getAddress()));
JavaClassLoader jcl2 = getClassLoader(loadObjectClass);
if (jcl.equals(jcl2)) {
System.out.println("Found boot class loader "+loadObjectClass.getName()+" at "+format(loaderObject.getID().getAddress()));
bootLoader = jcl;
break;
}
}
} catch (CorruptDataException e) {
}
}
return bootLoader;
}
/**
* Basic class loader finder - copes with arrays not having a loader, use component type loader instead
* @param j2
* @param listener TODO
*
* @throws CorruptDataException
*/
private JavaClassLoader getClassLoader1(JavaClass j2) throws CorruptDataException {
JavaClassLoader load;
// Fix up possible problem with arrays not having a class loader
// Use the loader of the component type instead
for (JavaClass j3 = j2; ; j3 = j3.getComponentType()) {
load = j3.getClassLoader();
if (load != null) break;
}
return load;
}
/**
* General class loader finder
* @param j1
* @param listener TODO
*
* @throws CorruptDataException
*/
private JavaClassLoader getClassLoader(JavaClass j1) throws CorruptDataException {
try {
return getClassLoader1(j1);
} catch (CorruptDataException e) {
JavaClassLoader load = getClassLoader2(j1);
if (load != null) return load;
throw e;
}
}
/**
* @param j1
*
* @throws CorruptDataException
*/
private JavaClassLoader getClassLoader2(JavaClass j1) throws CorruptDataException {
JavaClassLoader load = null;
JavaRuntime run = getJavaRuntime();
for (Iterator i = run.getJavaClassLoaders().iterator(); i.hasNext();) {
Object next = i.next();
if (next instanceof CorruptData) {
continue;
}
JavaClassLoader jcl = (JavaClassLoader)next;
for (Iterator j = jcl.getDefinedClasses().iterator();j.hasNext();) {
Object next2 = j.next();
if (next2 instanceof CorruptData) {
continue;
}
JavaClass j2 = (JavaClass)next2;
if (j2.equals(j1)) {
return jcl;
}
}
}
return load;
}
private JavaObject thisJavaObject = null;
/**
* Finds the JavaObject in the Dump that corresponds with this
* testcase.
*
* @return The dead version of the test, or null.
*/
public JavaObject getScenerioReference() {
String packageName=this.getClass().getPackage().getName();
String thisClassName=simpleName(this.getClass());
String testClassName="Setup"+thisClassName.substring(4);
String scenerioReference=packageName+"."+testClassName;
scenerioReference=scenerioReference.replace('.', '/');
if (thisJavaObject == null) {
return thisJavaObject = getJavaObjectByClassname(scenerioReference);
}
return null;
}
/**
* Dumplicates the functionality of JavaClass.getSimpleName().
* That method isn't available pre-1.4
*
* @param clazz
* @return The short name of the class, after the last $ and .
*/
private static String simpleName(Class clazz) {
String className = clazz.getName();
int dotPos = className.lastIndexOf('.');
int dolPos = className.lastIndexOf('$');
if (dotPos == -1 && dolPos == -1) {
return className;
}
int cutoff = Math.max(dotPos, dolPos);
return className.substring(cutoff+1);
}
public JavaObject getJavaObjectByClassname(String className) {
JavaRuntime run = getJavaRuntime();
for(Iterator heaps = run.getHeaps().iterator(); heaps.hasNext();) {
Object nextHeap = heaps.next();
if (nextHeap instanceof CorruptData) {
continue;
}
JavaHeap heap = (JavaHeap) nextHeap;
for(Iterator objects = heap.getObjects().iterator(); objects.hasNext();) {
Object nextObject = objects.next();
if (nextObject instanceof CorruptData) {
continue;
}
JavaObject object = (JavaObject) nextObject;
try {
if (className.equals(object.getJavaClass().getName())) {
return object;
}
} catch (CorruptDataException e) {
// Ignore, it might be a following object.
}
}
}
return null;
}
}