001/* 002 * Copyright 2012 Chris Pheby 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.jadira.scanner.classfile; 017 018import java.io.BufferedInputStream; 019import java.io.DataInputStream; 020import java.io.File; 021import java.io.FileNotFoundException; 022import java.io.IOException; 023import java.io.InputStream; 024import java.net.URL; 025import java.util.List; 026import java.util.WeakHashMap; 027 028import javassist.bytecode.ClassFile; 029 030import org.apache.commons.lang3.builder.ToStringBuilder; 031import org.apache.commons.lang3.builder.ToStringStyle; 032import org.jadira.scanner.classfile.filter.ClassFileFilter; 033import org.jadira.scanner.classfile.filter.NameFilter; 034import org.jadira.scanner.classfile.filter.PackageFileFilter; 035import org.jadira.scanner.classpath.projector.ClasspathProjector; 036import org.jadira.scanner.core.api.Allocator; 037import org.jadira.scanner.core.api.Projector; 038import org.jadira.scanner.core.helper.JavassistClassFileHelper; 039import org.jadira.scanner.core.spi.AbstractFileResolver; 040import org.jadira.scanner.core.utils.reflection.ClassLoaderUtils; 041import org.jadira.scanner.file.locator.JdkBaseClasspathUrlLocator; 042 043import de.schlichtherle.io.FileInputStream; 044 045public class ClassFileResolver extends AbstractFileResolver<ClassFile> { 046 047 private static final WeakHashMap<File, ClassFile> CACHED_CLASSFILES = new WeakHashMap<File, ClassFile>(); 048 049 private static final Projector<File> CLASSPATH_PROJECTOR = ClasspathProjector.SINGLETON; 050 051 private static final List<URL> JDK_BASE_CLASSPATH_JARS = new JdkBaseClasspathUrlLocator().locate(); 052 053 private final ClassFileAssigner assigner = new ClassFileAssigner(); 054 055 private final ClassLoader[] classLoaders; 056 057 public ClassFileResolver() { 058 this.classLoaders = ClassLoaderUtils.getClassLoaders(); 059 } 060 061 public ClassFileResolver(ClassLoader... classLoaders) { 062 super(JDK_BASE_CLASSPATH_JARS); 063 this.classLoaders = ClassLoaderUtils.getClassLoaders(classLoaders); 064 } 065 066 public ClassFileResolver(List<URL> classpaths, ClassLoader... classLoaders) { 067 super(JDK_BASE_CLASSPATH_JARS); 068 getDriverData().addAll(classpaths); 069 this.classLoaders = ClassLoaderUtils.getClassLoaders(classLoaders); 070 } 071 072 @Override 073 public String toString() { 074 075 ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); 076 builder.append(getDriverData().toArray()); 077 078 return builder.toString(); 079 } 080 081 @Override 082 protected Allocator<ClassFile, File> getAssigner() { 083 return assigner; 084 } 085 086 private final class ClassFileAssigner implements Allocator<ClassFile, File> { 087 088 @Override 089 public ClassFile allocate(File e) { 090 091 ClassFile res = CACHED_CLASSFILES.get(e); 092 if (res != null) { 093 return res; 094 } 095 096 FileInputStream tis = null; 097 098 try { 099 tis = new FileInputStream(e); 100 res = JavassistClassFileHelper.constructClassFileForPath(e.getPath(), tis); 101 CACHED_CLASSFILES.put(e, res); 102 return res; 103 } catch (FileNotFoundException e1) { 104 throw new IllegalArgumentException(e + " is not a valid File", e1); 105 } catch (IOException e1) { 106 throw new IllegalArgumentException("Could not load ClassFile for " + e, e1); 107 } finally { 108 if (tis != null) { 109 try { 110 tis.close(); 111 } catch (IOException e1) { 112 // Ignore 113 } 114 } 115 } 116 } 117 } 118 119 public ClassFile resolveClassFile(String name) { 120 121 ClassFile cf = null; 122 123 String className = name.replace('.', '/').concat(".class"); 124 125 for (ClassLoader classLoader : classLoaders) { 126 127 if (classLoader != null) { 128 129 InputStream is = classLoader.getResourceAsStream(className); 130 if (is == null) { 131 continue; 132 } 133 BufferedInputStream fin = new BufferedInputStream(is); 134 135 try { 136 cf = new ClassFile(new DataInputStream(fin)); 137 if (cf != null) { 138 return cf; 139 } 140 } catch (IOException e) { 141 // Ignore 142 } 143 } 144 } 145 146 cf = resolveFirst(null, CLASSPATH_PROJECTOR, new PackageFileFilter(name, true), new NameFilter(name)); 147 if (cf == null) { 148 cf = resolveFirst(null, CLASSPATH_PROJECTOR, new ClassFileFilter(name)); 149 } 150 return cf; 151 } 152}