You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@river.apache.org by Peter Firmstone <ji...@zeus.net.au> on 2009/03/19 06:07:16 UTC
RIVER-272 Bantam ClassDepAnalyzer.java
Just thought I'd post the source of the Bantam ClassDepAnalyzer for
discussion:
The Bantam Class Dependency Analyser is much smaller that I expected,
considering the small size, were probably better rolling our own, this
is interesting though:
-Peter.
/**
*
* Copyright 2006 Skill Corporation
*
* 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 com.skillcorp.bantam.startup.annotation;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.rmi.RemoteException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.logging.Logger;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
public class ClassDepAnalyzer extends ClassLoader {
private static final Logger logger = Logger.getLogger("com.skillcorp.bantam.startup.annotation");
public ClassDepAnalyzer() {
super();
}
public static void main(String[] args) throws Exception {
Set<String> mainClasses = new HashSet<String>();
mainClasses.add("com.skillcorp.bantam.service.examples.admin.TestInterface");
mainClasses.add("com.skillcorp.bantam.service.examples.admin.TestAdminInterface");
Set<String> includedPackages = new HashSet<String>();
Set<String> excludedPackages = new HashSet<String>();
excludedPackages.addAll(Arrays.asList(new String[] { "java", "javax", "com.sun", "net.jini" }));
includedPackages.addAll(Arrays.asList(new String[] { "com.skillcorp.bantam.service", }));
URLClassLoader urlc = new URLClassLoader(new URL[] { new File(
"c:\\workspaces\\mayflowerClean\\bantam.dev.java.net\\bantam-examples\\build\\WEB-INF\\classes\\")
.toURL() }, ClassDepAnalyzer.class.getClass().getClassLoader());
new ClassDepAnalyzer().createClassDepJar("test.jar", includedPackages, excludedPackages, mainClasses, urlc);
}
public void test(final Class classToRemote) throws IOException {
String className = classToRemote.getName() + "Intfc";
String jarName = classToRemote.getSimpleName() + "-dl.jar";
createRemoteAndJar(className, classToRemote, jarName);
try {
File createdJarFile = new File(jarName);
URLClassLoader u = new URLClassLoader(new URL[] { createdJarFile.toURL() }, null);
u.loadClass(className);
}
catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public String createRemoteAndJar(Class classToRemote) throws ClassFormatError, FileNotFoundException, IOException {
return createRemoteAndJar(null, classToRemote, null);
}
public String createRemoteAndJar(String remoteName, final Class classToRemote, String jarName)
throws ClassFormatError, IOException, FileNotFoundException {
if (remoteName == null) {
remoteName = classToRemote.getName() + "Intfc";
}
if (jarName == null) {
jarName = classToRemote.getSimpleName() + "-dl.jar";
}
byte[] classBytes = createRemote(remoteName, classToRemote);
Class remoteClass = defineClass(remoteName, classBytes, 0, classBytes.length);
assert (java.rmi.Remote.class.isAssignableFrom(remoteClass));
Map<String, byte[]> createdResources = new HashMap<String, byte[]>();
createdResources.put(remoteName.replace('.', '/'), classBytes);
InputStream classStream = new ByteArrayInputStream(classBytes);
createClassDepJar(jarName, createdResources, classStream);
return jarName;
}
private void createClassDepJar(String jarName, Map<String, byte[]> createdResources, InputStream classStream)
throws IOException, FileNotFoundException {
Set<String> classes = analyzeDependencies(classStream, null, null, Thread.currentThread()
.getContextClassLoader());
logger.fine("jar contents: " + classes.toString());
createJar(jarName, classes, createdResources, Thread.currentThread().getContextClassLoader());
}
public void createClassDepJar(String jarName, Set<String> includePackages, Set<String> excludePackages,
Set<String> mainClasses, ClassLoader cl) throws IOException {
Set<String> allDependencies = new HashSet<String>();
for (String mainClass : mainClasses) {
logger.fine("analyzing main class " + mainClass);
String resourceName = mainClass.replace('.', '/') + ".class";
Set<String> dependencies = analyzeDependencies(cl.getResourceAsStream(resourceName), includePackages,
excludePackages, cl);
allDependencies.addAll(dependencies);
}
logger.fine("jar contents: " + allDependencies.toString());
Map<String, byte[]> empty = Collections.emptyMap();
createJar(jarName, allDependencies, empty, cl);
}
private byte[] createRemote(String remoteName, final Class classToRemote) {
ClassWriter cw = new ClassWriter(false);
String remoteResourceName = remoteName.replace('.', '/');
cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT + Opcodes.ACC_INTERFACE, remoteResourceName,
null, "java/lang/Object", new String[] { "java/rmi/Remote" });
Method[] methods = classToRemote.getDeclaredMethods();
for (Method method : methods) {
if (Modifier.isPublic(method.getModifiers())) {
Class[] exceptionTypes = method.getExceptionTypes();
String[] exceptions = new String[exceptionTypes.length];
boolean throwsRemote = false;
for (int j = 0; j < exceptionTypes.length; j++) {
Class ex = exceptionTypes[j];
if (ex.equals(RemoteException.class)) {
throwsRemote = true;
}
exceptions[j] = ex.getName().replace('.', '/');
}
if (!throwsRemote) {
String[] moreExceptions = new String[exceptions.length + 1];
System.arraycopy(exceptions, 0, moreExceptions, 0, exceptions.length);
moreExceptions[exceptions.length] = RemoteException.class.getName().replace('.', '/');
exceptions = moreExceptions;
}
cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT, method.getName(), Type
.getMethodDescriptor(method), null, exceptions);
}
}
cw.visitEnd();
return cw.toByteArray();
}
private Set<String> analyzeDependencies(InputStream classStream, Set<String> includedPackages,
Set<String> excludedPackages, ClassLoader cl) throws IOException {
DependencyVisitor v = new DependencyVisitor(includedPackages, excludedPackages);
new ClassReader(classStream).accept(v, false);
Set<String> dependentClasses = new HashSet<String>(v.getDependencies());
Set<String> newDependentClasses = new HashSet<String>(dependentClasses);
Set<String> evaluatedClasses = new HashSet<String>();
while (!newDependentClasses.isEmpty()) {
for (String dependency : dependentClasses) {
if (!evaluatedClasses.contains(dependency)) {
String dependentClass = dependency + ".class";
InputStream is = cl.getResourceAsStream(dependentClass);
if (is != null) {
new ClassReader(is).accept(v, false);
newDependentClasses.addAll(v.getDependencies());
}
evaluatedClasses.add(dependency);
}
}
newDependentClasses.removeAll(dependentClasses);
dependentClasses.addAll(newDependentClasses);
}
return v.getDependencies();
}
private void createJar(String jarName, Set<String> includedClasses, Map<String, byte[]> createdResources,
ClassLoader cl) throws IOException, FileNotFoundException {
JarOutputStream jos = new JarOutputStream(new FileOutputStream(jarName));
byte[] buf = new byte[1024];
for (String dependency : includedClasses) {
String resourceName = dependency + ".class";
InputStream is = cl.getResourceAsStream(resourceName);
int br = 0;
if (is != null) {
JarEntry entry = new JarEntry(resourceName);
jos.putNextEntry(entry);
while ((br = is.read(buf, 0, buf.length)) != -1) {
jos.write(buf, 0, br);
}
is.close();
}
else if (createdResources.containsKey(dependency)) {
JarEntry entry = new JarEntry(resourceName);
jos.putNextEntry(entry);
jos.write(createdResources.get(dependency));
}
jos.closeEntry();
}
jos.close();
}
}