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.classpath; 017 018import java.io.File; 019import java.io.FileNotFoundException; 020import java.io.IOException; 021import java.io.InputStream; 022import java.net.URL; 023import java.util.List; 024 025import javassist.bytecode.ClassFile; 026 027import org.apache.commons.lang3.builder.ToStringBuilder; 028import org.apache.commons.lang3.builder.ToStringStyle; 029import org.jadira.scanner.classfile.ClassFileResolver; 030import org.jadira.scanner.classpath.types.JClass; 031import org.jadira.scanner.classpath.types.JElement; 032import org.jadira.scanner.classpath.types.JInterface; 033import org.jadira.scanner.classpath.types.JPackage; 034import org.jadira.scanner.core.api.Allocator; 035import org.jadira.scanner.core.exception.ClasspathAccessException; 036import org.jadira.scanner.core.exception.FileAccessException; 037import org.jadira.scanner.core.helper.FileInputStreamOperation; 038import org.jadira.scanner.core.helper.FileUtils; 039import org.jadira.scanner.core.helper.JavassistClassFileHelper; 040import org.jadira.scanner.core.helper.filenamefilter.ClassFilenameFilter; 041import org.jadira.scanner.core.spi.AbstractFileResolver; 042import org.jadira.scanner.core.utils.reflection.ClassLoaderUtils; 043import org.jadira.scanner.file.locator.JdkBaseClasspathUrlLocator; 044 045import de.schlichtherle.io.FileInputStream; 046 047public class ClasspathResolver extends AbstractFileResolver<JElement> { 048 049 private static final List<URL> JDK_BASE_CLASSPATH_JARS = new JdkBaseClasspathUrlLocator().locate(); 050 051 private static final ClassFilenameFilter CLASSFILE_FILTER = new ClassFilenameFilter(); 052 053 private final ClasspathAssigner assigner = new ClasspathAssigner(); 054 055 private final ClassFileResolver classFileResolver; 056 057 private final ClassLoader[] classLoaders; 058 059 public ClasspathResolver() { 060 super(JDK_BASE_CLASSPATH_JARS); 061 this.classLoaders = ClassLoaderUtils.getClassLoaders(); 062 classFileResolver = new ClassFileResolver(classLoaders); 063 } 064 065 public ClasspathResolver(List<URL> classpaths) { 066 super(); 067 this.classLoaders = ClassLoaderUtils.getClassLoaders(); 068 classFileResolver = new ClassFileResolver(classpaths, this.classLoaders); 069 getDriverData().addAll(classpaths); 070 } 071 072 public ClasspathResolver(ClassLoader... classLoaders) { 073 super(JDK_BASE_CLASSPATH_JARS); 074 this.classLoaders = ClassLoaderUtils.getClassLoaders(classLoaders); 075 classFileResolver = new ClassFileResolver(this.classLoaders); 076 } 077 078 public ClasspathResolver(List<URL> classpaths, List<ClassLoader> classLoaders) { 079 super(); 080 this.classLoaders = ClassLoaderUtils.getClassLoaders((ClassLoader[])classLoaders.toArray()); 081 classFileResolver = new ClassFileResolver(classpaths, this.classLoaders); 082 getDriverData().addAll(classpaths); 083 } 084 085 public ClasspathResolver(List<URL> classpaths, ClassLoader... classLoaders) { 086 super(); 087 this.classLoaders = ClassLoaderUtils.getClassLoaders(classLoaders); 088 classFileResolver = new ClassFileResolver(classpaths, this.classLoaders); 089 getDriverData().addAll(classpaths); 090 } 091 092 @Override 093 public String toString() { 094 095 ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); 096 builder.append(getDriverData().toArray()); 097 098 return builder.toString(); 099 } 100 101 @Override 102 protected Allocator<JElement, File> getAssigner() { 103 return assigner; 104 } 105 106 private final class ClasspathAssigner implements Allocator<JElement, File> { 107 108 @Override 109 public JElement allocate(File e) { 110 111 JElement element; 112 if (e.isDirectory()) { 113 // Attempt to construct package 114 element = assignPackage(e); 115 } else { 116 try { 117 ClassFile f = JavassistClassFileHelper.constructClassFileForPath(e.getPath(), new FileInputStream(e)); 118 if (f.isInterface()) { 119 return JInterface.getJInterface(f, ClasspathResolver.this); 120 } else { 121 return JClass.getJClass(f, ClasspathResolver.this); 122 } 123 } catch (FileNotFoundException e1) { 124 throw new ClasspathAccessException("Couldnt find file", e1); 125 } catch (IOException e1) { 126 throw new ClasspathAccessException("Couldnt access file", e1); 127 } 128 } 129 return element; 130 } 131 132 private JPackage assignPackage(File e) { 133 134 JPackage retVal; 135 136 final File[] classes = e.listFiles(CLASSFILE_FILTER); 137 for (int i = 0; i < classes.length; i++) { 138 139 ClassFile classFile = FileUtils.doWithFile(classes[i], new FileInputStreamOperation<ClassFile>() { 140 141 @Override 142 public ClassFile execute(String path, InputStream fileInputStream) { 143 144 try { 145 return JavassistClassFileHelper.constructClassFile(path, fileInputStream); 146 } catch (IOException e) { 147 throw new FileAccessException("Cannot access class file: " + e.getMessage(), e); 148 } 149 } 150 }); 151 152 Package pkg = loadClass(classFile.getName()).getPackage(); 153 retVal = JPackage.getJPackage(pkg, ClasspathResolver.this); 154 155 if (retVal != null) { 156 return retVal; 157 } 158 } 159 return null; 160 } 161 } 162 163 public ClassFileResolver getClassFileResolver() { 164 return classFileResolver; 165 } 166 167 public Class<?> loadClass(String className) { 168 169 if ("java.lang.Class".equals(className)) { 170 return java.lang.Class.class; 171 } 172 173 for (int i = 0; i < classLoaders.length; i++) { 174 try { 175 return Class.forName(className, true, classLoaders[i]); 176 } catch (ClassNotFoundException e) { 177 // Ignore 178 } 179 } 180 throw new ClasspathAccessException("Could not find class: " + className); 181 } 182}