/* Name: - RunTimeClassHierarchyAnalyser Description: - Requires: - Provides: - Part of: ProcessPuzzle Framework, Domain and Business Model Ready Architecture. Provides content, workflow and social networking functionality. http://www.processpuzzle.com ProcessPuzzle - Content and Workflow Management Integration Business Platform Author(s): - Zsolt Zsuffa Copyright: (C) 2011 This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /** * RTSI.java * * Created: Wed Jan 24 11:15:02 2001 * */ package com.processpuzzle.application.configuration.domain; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.net.JarURLConnection; import java.net.URL; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.zip.ZipEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.access.BootstrapException; import com.processpuzzle.application.security.domain.User; import com.processpuzzle.user_session.domain.UserRequestManager; /** * This utility class is looking for all the classes implementing or inheriting from a given interface or class. * (RunTime Subclass Identification) * * @author <a href="mailto:daniel@satlive.org">Daniel Le Berre</a> * @version 1.0 */ public class RunTimeClassHierarchyAnalyser { private static Logger log = LoggerFactory.getLogger( RunTimeClassHierarchyAnalyser.class ); private Set<Class<?>> subClasses = new HashSet<Class<?>>(); public RunTimeClassHierarchyAnalyser() { super(); } public boolean checkIfIsChildOf( Class<?> classToCheck, Class<?> parentClass ) { boolean result = false; if( classToCheck.equals( Object.class )) return false; if( classToCheck.getSuperclass().equals( parentClass )) return true; else if( !classToCheck.getSuperclass().equals( Object.class )) result = checkIfIsChildOf( classToCheck.getSuperclass(), parentClass ); return result; } public Set<Class<?>> findSubClasses( List<String> targetPackageNames, String parentClassName ) throws FileNotFoundException { try{ Class<?> parentClass = Class.forName( parentClassName ); for( String packageName : targetPackageNames ){ getClass().getClassLoader().getResource( packageName + ".*" ); } Package[] packages = Package.getPackages(); for( int i = 0; i < packages.length; i++ ){ String packageName = packages[i].getName(); for( String targetPackageName : targetPackageNames ){ if( packageName.startsWith( targetPackageName )) findSubClasses( packageName, parentClass ); } } }catch( ClassNotFoundException ex ){ log.error( "Class " + parentClassName + " not found!", ex ); }catch( Throwable ex ){ log.error( "Unknown exception was thrown, while analysing class " + parentClassName + ".", ex ); } return subClasses; } public void findSubClasses( String packageName, String parentClassName ) throws FileNotFoundException { try{ Class<?> parentClass = Class.forName( parentClassName ); findSubClasses( packageName, parentClass ); }catch( ClassNotFoundException ex ){ log.error( "Class " + parentClassName + " not found!", ex ); } } /** * Display all the classes inheriting or implementing a given class in a given package. * * @param packageName * the fully qualified name of the package * @param parentClass * the Class object to inherit from * @throws FileNotFoundException */ public void findSubClasses( String packageName, Class<?> parentClass ) throws FileNotFoundException { String path = translatePackageNameIntoPath( packageName ); URL url = getFileForPackage( path ); if( url == null ) throw new FileNotFoundException( path ); File directory = new File( url.getFile() ); if( directory.exists() ){ determineSubClassesFromClassFiles( packageName, parentClass, directory ); }else{ determineSubClassesFromJarFiles( parentClass, url ); } } public Set<Class<?>> getSubClasses() { return subClasses; } private URL getFileForPackage( String path ) { URL url = RunTimeClassHierarchyAnalyser.class.getResource( path ); // URL url = tosubclass.getResource(name); // URL url = ClassLoader.getSystemClassLoader().getResource(name); return url; } private void determineSubClassesFromClassFiles( String packageName, Class<?> parentClass, File directory ) { log.debug( "Looking for subclasses of:'" + parentClass.getName() + "' in package:'" + packageName + "'." ); String[] files = directory.list(); for( int i = 0; i < files.length; i++ ){ // we are only interested in .class files if( files[i].endsWith( ".class" ) ){ // removes the .class extension String className = files[i].substring( 0, files[i].length() - 6 ); try{ log.debug( "Investigating class: '" + className + "'." ); String fullClassName = packageName + "." + className; User currentUser = UserRequestManager.getInstance().currentUser(); Class<?> anyClass = Class.forName( fullClassName ); if( !UserRequestManager.getInstance().currentUser().equals( currentUser )) { System.out.println("Investigating class: '" + fullClassName + "' changed current user!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!."); throw new Exception(); } if( isInstanceOf( anyClass, parentClass )) subClasses.add( anyClass ); }catch( ClassNotFoundException cnfex ){ log.error( "Class: " + className + " not found.", cnfex ); }catch( ExceptionInInitializerError e ) { log.debug( "The class: " + packageName + "." + className + " could not be initialized.", e ); }catch( BootstrapException e ) { // The class can't be loaded }catch( NullPointerException e ) { // The class can't be loaded log.debug( "Strange to catch here NullPointerException for: " + packageName + "." + className, e ); }catch( Exception e ) { throw new SubClassIdentificationException( parentClass, e ); }catch( Throwable e ) { throw new SubClassIdentificationException( parentClass, e ); } } } } private void determineSubClassesFromJarFiles( Class<?> parentClass, URL url ) { log.debug( "Looking for subclasses of:'" + parentClass.getName() + "' in url:'" + url + "'." ); try{ // It does not work with the filesystem: we must be in the case of a package contained in a jar file. JarURLConnection conn = (JarURLConnection) url.openConnection(); String starts = conn.getEntryName(); JarFile jfile = conn.getJarFile(); Enumeration<JarEntry> e = jfile.entries(); while( e.hasMoreElements() ){ ZipEntry entry = (ZipEntry) e.nextElement(); String entryname = entry.getName(); if( entryname.startsWith( starts ) && (entryname.lastIndexOf( '/' ) <= starts.length()) && entryname.endsWith( ".class" ) ){ String className = entryname.substring( 0, entryname.length() - 6 ); if( className.startsWith( "/" ) ) className = className.substring( 1 ); className = className.replace( '/', '.' ); try{ log.debug( "Investigating class: '" + className + "'." ); Class<?> anyClass = Class.forName( className ); if( isInstanceOf( anyClass, parentClass )) subClasses.add( anyClass ); }catch( ClassNotFoundException cnfex ){ log.error( "Class: " + className + " not found." ); // }catch( InstantiationException iex ){ // // We try to instanciate an interface or an object that does not have a default constructor // }catch( IllegalAccessException iaex ){ // // The class is not public }catch( Exception ex ) { throw new SubClassIdentificationException( parentClass, ex ); }catch( Throwable ex ) { throw new SubClassIdentificationException( parentClass, ex ); } } } }catch( IOException ioex ){ log.error( "This error text should be defined!", ioex ); } } private String translatePackageNameIntoPath( String packageName ) { String path = new String( packageName ); if( !path.startsWith( "/" ) ){ path = "/" + path; } path = path.replace( '.', '/' ); return path; } private boolean isInstanceOf( Class<?> classToCheck, Class<?> parentClass ) { Class<?> superClass = null; try{ superClass = classToCheck.getSuperclass(); }catch( Exception e ){ System.out.println( RunTimeClassHierarchyAnalyser.class.getName() + ": Unexpected exception occured."); } if( superClass == null ) return false; if( superClass.equals( parentClass )) return true; else if( !( superClass.equals( Object.class ))) return isInstanceOf( superClass, parentClass ); else return false; } }