/* * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Middleware LLC. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * 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 Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.hibernate.ejb.packaging; import java.io.IOException; import java.lang.annotation.Annotation; import java.net.URL; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.persistence.Embeddable; import javax.persistence.Entity; import javax.persistence.MappedSuperclass; import org.hibernate.AssertionFailure; import org.hibernate.internal.util.ReflectHelper; /** * @author Emmanuel Bernard */ public class NativeScanner implements Scanner { private static final String META_INF_ORM_XML = "META-INF/orm.xml"; private Map<URL, StateJarVisitor> visitors = new HashMap<URL, StateJarVisitor>(); private static final int PACKAGE_FILTER_INDEX = 0; private static final int CLASS_FILTER_INDEX = 1; private static final int FILE_FILTER_INDEX = 2; /** * This implementation does not honor the list of annotations and return everything. * Must strictly be used by HEM */ public Set<Package> getPackagesInJar(URL jarToScan, Set<Class<? extends Annotation>> annotationsToLookFor) { if ( annotationsToLookFor.size() > 0 ) { throw new AssertionFailure( "Improper use of NativeScanner: must not filter packages" ); } JarVisitor jarVisitor = getVisitor( jarToScan ); final Set<Entry> packageEntries; try { packageEntries = ( Set<Entry> ) jarVisitor.getMatchingEntries()[PACKAGE_FILTER_INDEX]; } catch ( IOException e ) { throw new RuntimeException( "Error while reading " + jarToScan.toString(), e ); } Set<Package> packages = new HashSet<Package>( packageEntries.size() ); for ( Entry entry : packageEntries ) { try { packages.add( ReflectHelper.classForName( entry.getName() + ".package-info" ).getPackage() ); } catch ( ClassNotFoundException e ) { //should never happen, if it happens, simply ignore the flawed package } } return packages; } /** * Build a JarVisitor with some assumptions wrt the scanning * This helps do one scan instead of several */ private JarVisitor getVisitor(URL jar) { StateJarVisitor stateJarVisitor = visitors.get( jar ); if ( stateJarVisitor == null ) { Filter[] filters = new Filter[3]; filters[PACKAGE_FILTER_INDEX] = new PackageFilter( false, null ) { public boolean accept(String javaElementName) { return true; } }; filters[CLASS_FILTER_INDEX] = new ClassFilter( false, new Class[] { Entity.class, MappedSuperclass.class, Embeddable.class } ) { public boolean accept(String javaElementName) { return true; } }; filters[FILE_FILTER_INDEX] = new FileFilter( true ) { public boolean accept(String javaElementName) { return javaElementName.endsWith( "hbm.xml" ) || javaElementName.endsWith( META_INF_ORM_XML ); } }; stateJarVisitor = new StateJarVisitor( JarVisitorFactory.getVisitor( jar, filters ) ); visitors.put( jar, stateJarVisitor ); } return stateJarVisitor.visitor; } public Set<Class<?>> getClassesInJar(URL jarToScan, Set<Class<? extends Annotation>> annotationsToLookFor) { if ( isValidForClasses( annotationsToLookFor ) ) { throw new AssertionFailure( "Improper use of NativeScanner: " + "must not filter classes by other annotations than Entity, MappedSuperclass, embeddable" ); } JarVisitor jarVisitor = getVisitor( jarToScan ); final Set<Entry> classesEntry; try { classesEntry = ( Set<Entry> ) jarVisitor.getMatchingEntries()[CLASS_FILTER_INDEX]; } catch ( IOException e ) { throw new RuntimeException( "Error while reading " + jarToScan.toString(), e ); } Set<Class<?>> classes = new HashSet<Class<?>>( classesEntry.size() ); for ( Entry entry : classesEntry ) { try { classes.add( ReflectHelper.classForName( entry.getName() ) ); } catch ( ClassNotFoundException e ) { //should never happen, if it happens, simply ignore the flawed package } } return classes; } private boolean isValidForClasses(Set<Class<? extends Annotation>> annotationsToLookFor) { return annotationsToLookFor.size() != 3 || !annotationsToLookFor.contains( Entity.class ) || !annotationsToLookFor.contains( MappedSuperclass.class ) || !annotationsToLookFor.contains( Embeddable.class ); } /** * support for patterns is primitive: * - **\/*.hbm.xml * Other patterns will not be found */ public Set<NamedInputStream> getFilesInJar(URL jarToScan, Set<String> filePatterns) { StringBuilder sb = new StringBuilder("URL: ").append( jarToScan ) .append( "\n" ); for (String pattern : filePatterns) { sb.append( " " ).append( pattern ).append( "\n" ); } JarVisitor jarVisitor = getVisitor( jarToScan ); //state visitor available final StateJarVisitor stateVisitor = visitors.get( jarToScan ); if ( stateVisitor.hasReadFiles ) { throw new AssertionFailure( "Cannot read files twice on NativeScanner" ); } stateVisitor.hasReadFiles = true; Set<String> endWiths = new HashSet<String>(); Set<String> exacts = new HashSet<String>(); for ( String pattern : filePatterns ) { if ( pattern.startsWith( "**/*" ) ) { final String patternTail = pattern.substring( 4, pattern.length() ); if ( !patternTail.equals( ".hbm.xml" ) ) { throw new AssertionFailure( "Improper use of NativeScanner: " + "must not filter files via pattern other than .hbm.xml" ); } endWiths.add( patternTail ); } else { exacts.add( pattern ); } } final Set<Entry> fileEntries; try { fileEntries = ( Set<Entry> ) jarVisitor.getMatchingEntries()[FILE_FILTER_INDEX]; } catch ( IOException e ) { throw new RuntimeException( "Error while reading " + jarToScan.toString(), e ); } Set<NamedInputStream> files = new HashSet<NamedInputStream>( fileEntries.size() ); Set<Entry> leftOver = new HashSet<Entry>( fileEntries ); for ( Entry entry : fileEntries ) { boolean done = false; for ( String exact : exacts ) { if ( entry.getName().equals( exact ) ) { files.add( new NamedInputStream( entry.getName(), entry.getInputStream() ) ); leftOver.remove( entry ); done = true; } } if (done) continue; for ( String endWithPattern : endWiths ) { if ( entry.getName().endsWith( endWithPattern ) ) { files.add( new NamedInputStream( entry.getName(), entry.getInputStream() ) ); leftOver.remove( entry ); } } } for ( Entry entry : leftOver ) { try { entry.getInputStream().close(); } catch ( IOException e ) { //swallow as we don't care about these files } } return files; } public Set<NamedInputStream> getFilesInClasspath(Set<String> filePatterns) { throw new AssertionFailure( "Not implemented" ); } public String getUnqualifiedJarName(URL jarToScan) { JarVisitor jarVisitor = getVisitor( jarToScan ); return jarVisitor.getUnqualifiedJarName(); } private static class StateJarVisitor { StateJarVisitor(JarVisitor visitor) { this.visitor = visitor; } JarVisitor visitor; boolean hasReadFiles = false; } }