/* * Copyright 2013, Arondor * * 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.arondor.common.reflection.parser.java; import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.apache.log4j.Logger; import com.arondor.common.reflection.api.catalog.AccessibleClassCatalog; import com.arondor.common.reflection.api.parser.AccessibleClassParser; import com.arondor.common.reflection.api.parser.AccessibleClassProvider; import com.arondor.common.reflection.model.java.AccessibleClass; /** * Abstract stuff for java-based AccessibleClass provider * * @author Francois Barre * */ public abstract class AbstractJavaAccessibleClassProvider implements AccessibleClassProvider { private static final Logger LOG = Logger.getLogger(AbstractJavaAccessibleClassProvider.class); private ClassLoader classLoader = this.getClass().getClassLoader(); protected ClassLoader getEffectiveClassLoader() { return classLoader; } protected void setEffectiveClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } private AccessibleClassParser accessibleClassParser = new JavaAccessibleClassParser(); public AccessibleClassParser getAccessibleClassParser() { return accessibleClassParser; } public void setAccessibleClassParser(AccessibleClassParser accessibleClassParser) { this.accessibleClassParser = accessibleClassParser; } private boolean allowClassWithNoEmptyConstructor = true; private boolean allowInterfaces = true; private List<String> packagePrefixes; public void setPackagePrefixes(List<String> packagePrefixes) { this.packagePrefixes = packagePrefixes; } public List<String> getPackagePrefixes() { return packagePrefixes; } protected boolean isClassInPackagePrefixes(String clazz) { for (String prefix : getPackagePrefixes()) { if (clazz.startsWith(prefix)) { return true; } } return false; } protected boolean isValidClass(Class<?> clazz) { String className = clazz.getName(); Class<?> emptyConstructorSignature[] = new Class<?>[0]; try { LOG.debug("Class '" + className + "', modifiers : " + clazz.getModifiers()); if (clazz.isAnonymousClass()) { LOG.debug("Skipping anonymous class " + clazz.getName()); return false; } int modifiers = clazz.getModifiers(); if (Modifier.isAbstract(modifiers)) { LOG.debug("Abstract class : " + clazz.getName()); return false; } if (!isAllowInterfaces() && Modifier.isInterface(modifiers)) { LOG.debug("Interface class : " + clazz.getName()); return false; } if (!isAllowClassWithNoEmptyConstructor()) { Constructor<?> emptyConstructor = null; try { emptyConstructor = clazz.getConstructor(emptyConstructorSignature); } catch (SecurityException e) { } catch (NoSuchMethodException e) { } if (emptyConstructor == null) { LOG.debug("No empty constructor : " + clazz.getName()); return false; } } return true; } catch (NoClassDefFoundError e) { LOG.warn("Could not fetch class '" + className + "'"); } catch (UnsupportedOperationException e) { LOG.debug("Could not fetch class '" + className + "'"); } catch (UnsatisfiedLinkError e) { LOG.debug("Could not fetch class '" + className + "'"); } catch (ExceptionInInitializerError e) { LOG.debug("Could not fetch class '" + className + "'"); } catch (RuntimeException e) { LOG.debug("Could not fetch class '" + className + "'"); } return false; } protected void scanDirectory(AccessibleClassCatalog catalog, File pathFile) { LOG.debug("Scanning directory : " + pathFile); if (!pathFile.exists()) { LOG.warn("Skipping directory " + pathFile.getAbsolutePath() + ", does not exist"); return; } scanDirectory(catalog, pathFile, null); } private void scanDirectory(AccessibleClassCatalog catalog, File pathFile, String root) { File children[] = pathFile.listFiles(); String subRootPrefix = (root != null ? (root + ".") : ""); for (int idx = 0; idx < children.length; idx++) { File entry = children[idx]; if (!entry.exists()) { LOG.warn("Child " + entry.getAbsolutePath() + " does not exist !"); } if (entry.isDirectory()) { String subRoot = subRootPrefix + entry.getName(); scanDirectory(catalog, entry, subRoot); } else if (entry.getName().endsWith(".class")) { String clz = subRootPrefix + entry.getName().substring(0, entry.getName().length() - ".class".length()); if (isClassInPackagePrefixes(clz)) { addClass(catalog, clz); } } else if (entry.getName().endsWith(".jar")) { scanJar(catalog, entry); } } } protected void scanJar(AccessibleClassCatalog catalog, File pathFile) { doScanJar(catalog, pathFile); } protected void doScanJar(AccessibleClassCatalog catalog, File pathFile) { LOG.debug("Openning jar '" + pathFile.getAbsolutePath() + "'"); JarFile jarFile = null; try { jarFile = new JarFile(pathFile); Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); LOG.debug("Opening entry : " + entry.getName()); if (entry.getName().endsWith(".class") && !entry.getName().contains("$")) { String clz = entry.getName().substring(0, entry.getName().length() - ".class".length()); /** * Regardless on which platform we are on, convert both \\ * and / to a dot. */ clz = clz.replace('\\', '.').replace('/', '.'); if (isClassInPackagePrefixes(clz)) { addClass(catalog, clz); } } } } catch (Throwable t) { LOG.error("Could not scan jar : " + pathFile.getAbsolutePath(), t); } finally { if (jarFile != null) { try { jarFile.close(); } catch (IOException e) { LOG.error("Could not close jar : " + pathFile.getAbsolutePath()); } } } } protected void addClass(AccessibleClassCatalog catalog, String className) { try { Class<?> clazz = Class.forName(className, false, getEffectiveClassLoader()); addClass(catalog, clazz); } catch (ClassNotFoundException e) { LOG.error("Could not get class for name : " + className); } catch (NoClassDefFoundError e) { LOG.error("Could not get class for name : " + className); } } protected void addClass(AccessibleClassCatalog catalog, Class<?> clazz) { AccessibleClass accessibleClass = accessibleClassParser.parseAccessibleClass(clazz); if (accessibleClass == null) { LOG.error("Could not parse class :" + clazz.getName()); return; } catalog.addAccessibleClass(accessibleClass); } public boolean isAllowClassWithNoEmptyConstructor() { return allowClassWithNoEmptyConstructor; } public void setAllowClassWithNoEmptyConstructor(boolean allowClassWithNoEmptyConstructor) { this.allowClassWithNoEmptyConstructor = allowClassWithNoEmptyConstructor; } public boolean isAllowInterfaces() { return allowInterfaces; } public void setAllowInterfaces(boolean allowInterfaces) { this.allowInterfaces = allowInterfaces; } }