diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/ejb/packaging/NamedInputStream.java b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/packaging/NamedInputStream.java index 7141f55f98..733455bf7e 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/ejb/packaging/NamedInputStream.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/packaging/NamedInputStream.java @@ -26,10 +26,12 @@ package org.hibernate.ejb.packaging; import java.io.InputStream; /** - * @deprecated Use {@link org.hibernate.jpa.packaging.spi.NamedInputStream} instead + * @deprecated Doubly deprecated actually :) Moved to {@link org.hibernate.jpa.boot.spi.NamedInputStream} + * due to package renaming (org.hibernate.ejb -> org.hibernate.jpa). But also, the role fulfilled by this class + * was moved to the new {@link org.hibernate.jpa.boot.spi.InputStreamAccess} contract. */ @Deprecated -public class NamedInputStream extends org.hibernate.jpa.packaging.spi.NamedInputStream { +public class NamedInputStream extends org.hibernate.jpa.boot.spi.NamedInputStream { public NamedInputStream(String name, InputStream stream) { super( name, stream ); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/ejb/packaging/Scanner.java b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/packaging/Scanner.java index 3a2cd4e46a..a452e66495 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/ejb/packaging/Scanner.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/packaging/Scanner.java @@ -24,8 +24,8 @@ package org.hibernate.ejb.packaging; /** - * @deprecated Use {@link org.hibernate.jpa.packaging.spi.Scanner} instead + * @deprecated Use {@link org.hibernate.jpa.boot.scan.spi.Scanner} instead */ @Deprecated -public interface Scanner extends org.hibernate.jpa.packaging.spi.Scanner { +public interface Scanner extends org.hibernate.jpa.boot.scan.spi.Scanner { } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JarVisitorFactory.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/internal/ArchiveHelper.java similarity index 63% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JarVisitorFactory.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/internal/ArchiveHelper.java index 45ca7f8e1b..76caf4db9c 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JarVisitorFactory.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/internal/ArchiveHelper.java @@ -1,8 +1,10 @@ /* - * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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. + * distributed under license by Red Hat Inc. * * 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 @@ -19,29 +21,26 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.packaging.internal; +package org.hibernate.jpa.boot.archive.internal; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; -import java.net.URISyntaxException; import java.net.URL; import java.util.LinkedList; import java.util.List; -import org.hibernate.internal.util.StringHelper; -import org.hibernate.jpa.internal.EntityManagerMessageLogger; import org.jboss.logging.Logger; +import org.hibernate.jpa.boot.archive.spi.ArchiveException; + /** * @author Emmanuel Bernard - * @author Brett Meyer + * @author Steve Ebersole */ -public class JarVisitorFactory { - - private static final EntityManagerMessageLogger LOG = Logger.getMessageLogger(EntityManagerMessageLogger.class, - JarVisitorFactory.class.getName()); +public class ArchiveHelper { + private static final Logger log = Logger.getLogger( ArchiveHelper.class ); /** * Get the JAR URL of the JAR containing the given entry @@ -51,7 +50,6 @@ public class JarVisitorFactory { * @param entry file known to be in the JAR * @return the JAR URL * @throws IllegalArgumentException if none URL is found - * TODO move to a ScannerHelper service? */ public static URL getJarURLFromURLEntry(URL url, String entry) throws IllegalArgumentException { URL jarUrl; @@ -106,7 +104,7 @@ public class JarVisitorFactory { "Unable to determine JAR Url from " + url + ". Cause: " + e.getMessage() ); } - LOG.trace("JAR URL from URL Entry: " + url + " >> " + jarUrl); + log.trace("JAR URL from URL Entry: " + url + " >> " + jarUrl); return jarUrl; } @@ -114,7 +112,6 @@ public class JarVisitorFactory { * get the URL from a given path string * * @throws IllegalArgumentException is something goes wrong - * TODO move to a ScannerHelper service? */ public static URL getURLFromPath(String jarPath) { URL jarUrl; @@ -134,70 +131,40 @@ public class JarVisitorFactory { return jarUrl; } - /** - * Get a JarVisitor to the jar jarPath applying the given filters - * - * Method used in a non-managed environment - * - * @throws IllegalArgumentException if the jarPath is incorrect - */ - public static JarVisitor getVisitor(String jarPath, Filter[] filters) throws IllegalArgumentException { - File file = new File( jarPath ); - if ( file.isFile() ) { - return new InputStreamZippedJarVisitor( jarPath, filters ); + public static String unqualifiedJarFileName(URL jarUrl) { + // todo : weak algorithm subject to AOOBE + String fileName = jarUrl.getFile(); + int exclamation = fileName.lastIndexOf( "!" ); + if (exclamation != -1) { + fileName = fileName.substring( 0, exclamation ); } - else { - return new ExplodedJarVisitor( jarPath, filters ); + + int slash = fileName.lastIndexOf( "/" ); + if ( slash != -1 ) { + fileName = fileName.substring( + fileName.lastIndexOf( "/" ) + 1, + fileName.length() + ); + } + + if ( fileName.length() > 4 && fileName.endsWith( "ar" ) && fileName.charAt( fileName.length() - 4 ) == '.' ) { + fileName = fileName.substring( 0, fileName.length() - 4 ); + } + + return fileName; + } + + public static byte[] getBytesFromInputStreamSafely(InputStream inputStream) { + try { + return getBytesFromInputStream( inputStream ); + } + catch (IOException e) { + throw new ArchiveException( "Unable to extract bytes from InputStream", e ); } } - /** - * Build a JarVisitor on the given JAR URL applying the given filters - * - * @throws IllegalArgumentException if the URL is malformed - */ - public static JarVisitor getVisitor(URL jarUrl, Filter[] filters) throws IllegalArgumentException { - return getVisitor( jarUrl, filters, "" ); - } - - public static JarVisitor getVisitor(URL jarUrl, Filter[] filters, String entry) throws IllegalArgumentException { - String protocol = jarUrl.getProtocol(); - if ( "jar".equals( protocol ) ) { - return new JarProtocolVisitor( jarUrl, filters, entry ); - } - else if ( StringHelper.isEmpty( protocol ) || "file".equals( protocol ) || "vfszip".equals( protocol ) || "vfsfile".equals( protocol ) ) { - File file; - try { - final String filePart = jarUrl.getFile(); - if ( filePart != null && filePart.indexOf( ' ' ) != -1 ) { - //unescaped (from the container), keep as is - file = new File( jarUrl.getFile() ); - } - else { - file = new File( jarUrl.toURI().getSchemeSpecificPart() ); - } - } - catch (URISyntaxException e) { - throw new IllegalArgumentException( - "Unable to visit JAR " + jarUrl + ". Cause: " + e.getMessage(), e - ); - } - - if ( file.isDirectory() ) { - return new ExplodedJarVisitor( jarUrl, filters, entry ); - } - else { - return new FileZippedJarVisitor( jarUrl, filters, entry ); - } - } - else { - //let's assume the url can return the jar as a zip stream - return new InputStreamZippedJarVisitor( jarUrl, filters, entry ); - } - } - - // Optimized by HHH-7835 public static byte[] getBytesFromInputStream(InputStream inputStream) throws IOException { + // Optimized by HHH-7835 int size; List data = new LinkedList(); int bufferSize = 4096; diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/internal/ExplodedArchiveDescriptor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/internal/ExplodedArchiveDescriptor.java new file mode 100644 index 0000000000..50a64a1a2f --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/internal/ExplodedArchiveDescriptor.java @@ -0,0 +1,212 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.archive.internal; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Enumeration; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; + +import org.jboss.logging.Logger; + +import org.hibernate.jpa.boot.archive.spi.AbstractArchiveDescriptor; +import org.hibernate.jpa.boot.archive.spi.ArchiveContext; +import org.hibernate.jpa.boot.archive.spi.ArchiveDescriptorFactory; +import org.hibernate.jpa.boot.archive.spi.ArchiveEntry; +import org.hibernate.jpa.boot.archive.spi.ArchiveException; +import org.hibernate.jpa.boot.internal.FileInputStreamAccess; +import org.hibernate.jpa.boot.spi.InputStreamAccess; +import org.hibernate.jpa.internal.EntityManagerMessageLogger; + +/** + * @author Steve Ebersole + */ +public class ExplodedArchiveDescriptor extends AbstractArchiveDescriptor { + private static final EntityManagerMessageLogger LOG = Logger.getMessageLogger( + EntityManagerMessageLogger.class, + ExplodedArchiveDescriptor.class.getName() + ); + + public ExplodedArchiveDescriptor( + ArchiveDescriptorFactory archiveDescriptorFactory, + URL archiveUrl, + String entryBasePrefix) { + super( archiveDescriptorFactory, archiveUrl, entryBasePrefix ); + } + + @Override + public void visitArchive(ArchiveContext context) { + final File rootDirectory = resolveRootDirectory(); + if ( rootDirectory == null ) { + return; + } + + if ( rootDirectory.isDirectory() ) { + processDirectory( rootDirectory, null, context ); + } + else { + //assume zipped file + processZippedRoot( rootDirectory, context ); + } + } + + private File resolveRootDirectory() { + final File archiveUrlDirectory; + try { + final String filePart = getArchiveUrl().getFile(); + if ( filePart != null && filePart.indexOf( ' ' ) != -1 ) { + //unescaped (from the container), keep as is + archiveUrlDirectory = new File( filePart ); + } + else { + archiveUrlDirectory = new File( getArchiveUrl().toURI().getSchemeSpecificPart() ); + } + } + catch (URISyntaxException e) { + LOG.malformedUrl( getArchiveUrl(), e ); + return null; + } + + if ( !archiveUrlDirectory.exists() ) { + LOG.explodedJarDoesNotExist( getArchiveUrl() ); + return null; + } + if ( !archiveUrlDirectory.isDirectory() ) { + LOG.explodedJarNotDirectory( getArchiveUrl() ); + return null; + } + + final String entryBase = getEntryBasePrefix(); + if ( entryBase != null && entryBase.length() > 0 && ! "/".equals( entryBase ) ) { + return new File( archiveUrlDirectory, entryBase ); + } + else { + return archiveUrlDirectory; + } + } + + private void processDirectory( + File directory, + String path, + ArchiveContext context) { + if ( directory == null ) { + return; + } + + final File[] files = directory.listFiles(); + if ( files == null ) { + return; + } + + path = path == null ? "" : path + "/"; + for ( final File localFile : files ) { + if ( !localFile.exists() ) { + // should never happen conceptually, but... + continue; + } + + if ( localFile.isDirectory() ) { + processDirectory( localFile, path + localFile.getName(), context ); + continue; + } + + final String name = localFile.getAbsolutePath(); + final String relativeName = path + localFile.getName(); + final InputStreamAccess inputStreamAccess = new FileInputStreamAccess( name, localFile ); + + final ArchiveEntry entry = new ArchiveEntry() { + @Override + public String getName() { + return name; + } + + @Override + public String getNameWithinArchive() { + return relativeName; + } + + @Override + public InputStreamAccess getStreamAccess() { + return inputStreamAccess; + } + }; + + context.obtainArchiveEntryHandler( entry ).handleEntry( entry, context ); + } + } + + private void processZippedRoot(File rootFile, ArchiveContext context) { + try { + final JarFile jarFile = new JarFile(rootFile); + final Enumeration entries = jarFile.entries(); + while ( entries.hasMoreElements() ) { + final ZipEntry zipEntry = entries.nextElement(); + if ( zipEntry.isDirectory() ) { + continue; + } + + final String name = extractName( zipEntry ); + final String relativeName = extractRelativeName( zipEntry ); + final InputStreamAccess inputStreamAccess; + try { + inputStreamAccess = buildByteBasedInputStreamAccess( name, jarFile.getInputStream( zipEntry ) ); + } + catch (IOException e) { + throw new ArchiveException( + String.format( + "Unable to access stream from jar file [%s] for entry [%s]", + jarFile.getName(), + zipEntry.getName() + ) + ); + } + + final ArchiveEntry entry = new ArchiveEntry() { + @Override + public String getName() { + return name; + } + + @Override + public String getNameWithinArchive() { + return relativeName; + } + + @Override + public InputStreamAccess getStreamAccess() { + return inputStreamAccess; + } + }; + context.obtainArchiveEntryHandler( entry ).handleEntry( entry, context ); + } + } + catch (IOException e) { + throw new ArchiveException( "Error accessing jar file [" + rootFile.getAbsolutePath() + "]", e ); + } + } + +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/internal/JarFileBasedArchiveDescriptor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/internal/JarFileBasedArchiveDescriptor.java new file mode 100644 index 0000000000..d601d69ae2 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/internal/JarFileBasedArchiveDescriptor.java @@ -0,0 +1,193 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.archive.internal; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Enumeration; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.zip.ZipEntry; + +import org.jboss.logging.Logger; + +import org.hibernate.jpa.boot.archive.spi.AbstractArchiveDescriptor; +import org.hibernate.jpa.boot.archive.spi.ArchiveContext; +import org.hibernate.jpa.boot.archive.spi.ArchiveDescriptorFactory; +import org.hibernate.jpa.boot.archive.spi.ArchiveEntry; +import org.hibernate.jpa.boot.archive.spi.ArchiveEntryHandler; +import org.hibernate.jpa.boot.archive.spi.ArchiveException; +import org.hibernate.jpa.internal.EntityManagerMessageLogger; +import org.hibernate.jpa.boot.spi.InputStreamAccess; + +/** + * An ArchiveDescriptor implementation leveraging the {@link JarFile} API for processing. + * + * @author Steve Ebersole + */ +public class JarFileBasedArchiveDescriptor extends AbstractArchiveDescriptor { + private static final EntityManagerMessageLogger LOG = Logger.getMessageLogger( + EntityManagerMessageLogger.class, + JarFileBasedArchiveDescriptor.class.getName() + ); + + public JarFileBasedArchiveDescriptor( + ArchiveDescriptorFactory archiveDescriptorFactory, + URL archiveUrl, + String entry) { + super( archiveDescriptorFactory, archiveUrl, entry ); + } + + @Override + public void visitArchive(ArchiveContext context) { + final JarFile jarFile = resolveJarFileReference(); + if ( jarFile == null ) { + return; + } + + final Enumeration zipEntries = jarFile.entries(); + while ( zipEntries.hasMoreElements() ) { + final ZipEntry zipEntry = zipEntries.nextElement(); + final String entryName = extractName( zipEntry ); + + if ( getEntryBasePrefix() != null && ! entryName.startsWith( getEntryBasePrefix() ) ) { + continue; + } + if ( zipEntry.isDirectory() ) { + continue; + } + + if ( entryName.equals( getEntryBasePrefix() ) ) { + // exact match, might be a nested jar entry (ie from jar:file:..../foo.ear!/bar.jar) + // + // This algorithm assumes that the zipped file is only the URL root (including entry), not + // just any random entry + try { + InputStream is = new BufferedInputStream( jarFile.getInputStream( zipEntry ) ); + try { + final JarInputStream jarInputStream = new JarInputStream( is ); + ZipEntry subZipEntry = jarInputStream.getNextEntry(); + while ( subZipEntry != null ) { + if ( ! subZipEntry.isDirectory() ) { + + final String name = extractName( subZipEntry ); + final String relativeName = extractRelativeName( subZipEntry ); + final InputStreamAccess inputStreamAccess + = buildByteBasedInputStreamAccess( name, jarInputStream ); + + final ArchiveEntry entry = new ArchiveEntry() { + @Override + public String getName() { + return name; + } + + @Override + public String getNameWithinArchive() { + return relativeName; + } + + @Override + public InputStreamAccess getStreamAccess() { + return inputStreamAccess; + } + }; + + final ArchiveEntryHandler entryHandler = context.obtainArchiveEntryHandler( entry ); + entryHandler.handleEntry( entry, context ); + } + + subZipEntry = jarInputStream.getNextEntry(); + } + } + finally { + is.close(); + } + } + catch (Exception e) { + throw new ArchiveException( "Error accessing JarFile entry [" + zipEntry.getName() + "]", e ); + } + } + else { + final String name = extractName( zipEntry ); + final String relativeName = extractRelativeName( zipEntry ); + final InputStreamAccess inputStreamAccess; + try { + inputStreamAccess = buildByteBasedInputStreamAccess( name, jarFile.getInputStream( zipEntry ) ); + } + catch (IOException e) { + throw new ArchiveException( + String.format( + "Unable to access stream from jar file [%s] for entry [%s]", + jarFile.getName(), + zipEntry.getName() + ) + ); + } + + final ArchiveEntry entry = new ArchiveEntry() { + @Override + public String getName() { + return name; + } + + @Override + public String getNameWithinArchive() { + return relativeName; + } + + @Override + public InputStreamAccess getStreamAccess() { + return inputStreamAccess; + } + }; + + final ArchiveEntryHandler entryHandler = context.obtainArchiveEntryHandler( entry ); + entryHandler.handleEntry( entry, context ); + } + } + } + + private JarFile resolveJarFileReference() { + try { + String filePart = getArchiveUrl().getFile(); + if ( filePart != null && filePart.indexOf( ' ' ) != -1 ) { + // unescaped (from the container), keep as is + return new JarFile( getArchiveUrl().getFile() ); + } + else { + return new JarFile( getArchiveUrl().toURI().getSchemeSpecificPart() ); + } + } + catch (IOException e) { + LOG.unableToFindFile( getArchiveUrl(), e ); + } + catch (URISyntaxException e) { + LOG.malformedUrlWarning( getArchiveUrl(), e ); + } + return null; + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/internal/JarInputStreamBasedArchiveDescriptor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/internal/JarInputStreamBasedArchiveDescriptor.java new file mode 100644 index 0000000000..3b7b338535 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/internal/JarInputStreamBasedArchiveDescriptor.java @@ -0,0 +1,168 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.archive.internal; + +import java.io.IOException; +import java.net.URL; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.zip.ZipEntry; + +import org.jboss.logging.Logger; + +import org.hibernate.jpa.boot.archive.spi.AbstractArchiveDescriptor; +import org.hibernate.jpa.boot.archive.spi.ArchiveContext; +import org.hibernate.jpa.boot.archive.spi.ArchiveDescriptorFactory; +import org.hibernate.jpa.boot.archive.spi.ArchiveEntry; +import org.hibernate.jpa.boot.archive.spi.ArchiveException; +import org.hibernate.jpa.boot.spi.InputStreamAccess; +import org.hibernate.jpa.internal.EntityManagerMessageLogger; + +/** + * An ArchiveDescriptor implementation that works on archives accessible through a {@link java.util.jar.JarInputStream}. + * NOTE : This is less efficient implementation than {@link JarFileBasedArchiveDescriptor} + * + * @author Emmanuel Bernard + * @author Steve Ebersole + */ +public class JarInputStreamBasedArchiveDescriptor extends AbstractArchiveDescriptor { + private static final EntityManagerMessageLogger LOG = Logger.getMessageLogger( + EntityManagerMessageLogger.class, + JarInputStreamBasedArchiveDescriptor.class.getName() + ); + + public JarInputStreamBasedArchiveDescriptor( + ArchiveDescriptorFactory archiveDescriptorFactory, + URL url, + String entry) { + super( archiveDescriptorFactory, url, entry ); + } + + @Override + public void visitArchive(ArchiveContext context) { + final JarInputStream jarInputStream; + try { + jarInputStream = new JarInputStream( getArchiveUrl().openStream() ); + } + catch (Exception e) { + //really should catch IOException but Eclipse is buggy and raise NPE... + LOG.unableToFindFile( getArchiveUrl(), e ); + return; + } + + try { + JarEntry jarEntry; + while ( ( jarEntry = jarInputStream.getNextJarEntry() ) != null ) { + String jarEntryName = jarEntry.getName(); + if ( getEntryBasePrefix() != null && ! jarEntryName.startsWith( getEntryBasePrefix() ) ) { + continue; + } + + if ( jarEntry.isDirectory() ) { + continue; + } + + if ( jarEntryName.equals( getEntryBasePrefix() ) ) { + // exact match, might be a nested jar entry (ie from jar:file:..../foo.ear!/bar.jar) + // + // This algorithm assumes that the zipped file is only the URL root (including entry), not + // just any random entry + try { + final JarInputStream subJarInputStream = new JarInputStream( jarInputStream ); + try { + ZipEntry subZipEntry = jarInputStream.getNextEntry(); + while (subZipEntry != null) { + if ( ! subZipEntry.isDirectory() ) { + final String subName = extractName( subZipEntry ); + final InputStreamAccess inputStreamAccess + = buildByteBasedInputStreamAccess( subName, subJarInputStream ); + + final ArchiveEntry entry = new ArchiveEntry() { + @Override + public String getName() { + return subName; + } + + @Override + public String getNameWithinArchive() { + return subName; + } + + @Override + public InputStreamAccess getStreamAccess() { + return inputStreamAccess; + } + }; + + context.obtainArchiveEntryHandler( entry ).handleEntry( entry, context ); + } + subZipEntry = jarInputStream.getNextJarEntry(); + } + } + finally { + subJarInputStream.close(); + } + } + catch (Exception e) { + throw new ArchiveException( "Error accessing nested jar", e ); + } + } + else { + final String entryName = extractName( jarEntry ); + final InputStreamAccess inputStreamAccess + = buildByteBasedInputStreamAccess( entryName, jarInputStream ); + + final String relativeName = extractRelativeName( jarEntry ); + + final ArchiveEntry entry = new ArchiveEntry() { + @Override + public String getName() { + return entryName; + } + + @Override + public String getNameWithinArchive() { + return relativeName; + } + + @Override + public InputStreamAccess getStreamAccess() { + return inputStreamAccess; + } + }; + + context.obtainArchiveEntryHandler( entry ).handleEntry( entry, context ); + } + } + + jarInputStream.close(); + } + catch (IOException ioe) { + throw new ArchiveException( + String.format( "Error accessing JarInputStream [%s]", getArchiveUrl() ), + ioe + ); + } + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/internal/JarProtocolArchiveDescriptor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/internal/JarProtocolArchiveDescriptor.java new file mode 100644 index 0000000000..4eaa7d8748 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/internal/JarProtocolArchiveDescriptor.java @@ -0,0 +1,71 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.archive.internal; + +import java.net.URL; + +import org.hibernate.annotations.common.AssertionFailure; +import org.hibernate.jpa.boot.archive.spi.ArchiveContext; +import org.hibernate.jpa.boot.archive.spi.ArchiveDescriptor; +import org.hibernate.jpa.boot.archive.spi.ArchiveDescriptorFactory; + +/** + * An ArchiveDescriptor implementation for handling archives whose url reported a JAR protocol (i.e., jar://). + * + * @author Steve Ebersole + */ +public class JarProtocolArchiveDescriptor implements ArchiveDescriptor { + private final ArchiveDescriptor delegateDescriptor; + + public JarProtocolArchiveDescriptor( + ArchiveDescriptorFactory archiveDescriptorFactory, + URL url, + String incomingEntry) { + if ( incomingEntry != null && incomingEntry.length() > 0 ) { + throw new IllegalArgumentException( "jar:jar: not supported: " + url ); + } + + final String urlFile = url.getFile(); + final int subEntryIndex = urlFile.lastIndexOf( "!" ); + if ( subEntryIndex == -1 ) { + throw new AssertionFailure( "JAR URL does not contain '!/' :" + url ); + } + + final String subEntry; + if ( subEntryIndex + 1 >= urlFile.length() ) { + subEntry = ""; + } + else { + subEntry = urlFile.substring( subEntryIndex + 1 ); + } + + URL fileUrl = archiveDescriptorFactory.getJarURLFromURLEntry( url, subEntry ); + delegateDescriptor = archiveDescriptorFactory.buildArchiveDescriptor( fileUrl, subEntry ); + } + + @Override + public void visitArchive(ArchiveContext context) { + delegateDescriptor.visitArchive( context ); + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/internal/StandardArchiveDescriptorFactory.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/internal/StandardArchiveDescriptorFactory.java new file mode 100644 index 0000000000..e502570140 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/internal/StandardArchiveDescriptorFactory.java @@ -0,0 +1,105 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.archive.internal; + +import java.io.File; +import java.net.URISyntaxException; +import java.net.URL; + +import org.hibernate.internal.util.StringHelper; +import org.hibernate.jpa.boot.archive.spi.ArchiveDescriptor; +import org.hibernate.jpa.boot.archive.spi.ArchiveDescriptorFactory; + +/** + * @author Emmanuel Bernard + * @author Steve Ebersole + */ +public class StandardArchiveDescriptorFactory implements ArchiveDescriptorFactory { + public static final StandardArchiveDescriptorFactory INSTANCE = new StandardArchiveDescriptorFactory(); + + @Override + public ArchiveDescriptor buildArchiveDescriptor(URL url) { + return buildArchiveDescriptor( url, "" ); + } + + @Override + public ArchiveDescriptor buildArchiveDescriptor(URL url, String entry) { + final String protocol = url.getProtocol(); + if ( "jar".equals( protocol ) ) { + return new JarProtocolArchiveDescriptor( this, url, entry ); + } + else if ( StringHelper.isEmpty( protocol ) + || "file".equals( protocol ) + || "vfszip".equals( protocol ) + || "vfsfile".equals( protocol ) ) { + final File file; + try { + final String filePart = url.getFile(); + if ( filePart != null && filePart.indexOf( ' ' ) != -1 ) { + //unescaped (from the container), keep as is + file = new File( url.getFile() ); + } + else { + file = new File( url.toURI().getSchemeSpecificPart() ); + } + + if ( ! file.exists() ) { + throw new IllegalArgumentException( + String.format( + "File [%s] referenced by given URL [%s] does not exist", + filePart, + url.toExternalForm() + ) + ); + } + } + catch (URISyntaxException e) { + throw new IllegalArgumentException( + "Unable to visit JAR " + url + ". Cause: " + e.getMessage(), e + ); + } + + if ( file.isDirectory() ) { + return new ExplodedArchiveDescriptor( this, url, entry ); + } + else { + return new JarFileBasedArchiveDescriptor( this, url, entry ); + } + } + else { + //let's assume the url can return the jar as a zip stream + return new JarInputStreamBasedArchiveDescriptor( this, url, entry ); + } + } + + @Override + public URL getJarURLFromURLEntry(URL url, String entry) throws IllegalArgumentException { + return ArchiveHelper.getJarURLFromURLEntry( url, entry ); + } + + @Override + public URL getURLFromPath(String jarPath) { + return ArchiveHelper.getURLFromPath( jarPath ); + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/AbstractArchiveDescriptor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/AbstractArchiveDescriptor.java new file mode 100644 index 0000000000..00f92c6a0b --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/AbstractArchiveDescriptor.java @@ -0,0 +1,92 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.archive.spi; + +import java.io.InputStream; +import java.net.URL; +import java.util.zip.ZipEntry; + +import org.hibernate.internal.util.StringHelper; +import org.hibernate.jpa.boot.internal.ByteArrayInputStreamAccess; +import org.hibernate.jpa.boot.archive.internal.ArchiveHelper; +import org.hibernate.jpa.boot.spi.InputStreamAccess; + +/** + * @author Steve Ebersole + */ +public abstract class AbstractArchiveDescriptor implements ArchiveDescriptor { + private final ArchiveDescriptorFactory archiveDescriptorFactory; + private final URL archiveUrl; + private final String entryBasePrefix; + + protected AbstractArchiveDescriptor( + ArchiveDescriptorFactory archiveDescriptorFactory, + URL archiveUrl, + String entryBasePrefix) { + this.archiveDescriptorFactory = archiveDescriptorFactory; + this.archiveUrl = archiveUrl; + this.entryBasePrefix = normalizeEntryBasePrefix( entryBasePrefix ); + } + + private static String normalizeEntryBasePrefix(String entryBasePrefix) { + if ( StringHelper.isEmpty( entryBasePrefix ) || entryBasePrefix.length() == 1 ) { + return null; + } + + return entryBasePrefix.startsWith( "/" ) ? entryBasePrefix.substring( 1 ) : entryBasePrefix; + } + + protected ArchiveDescriptorFactory getArchiveDescriptorFactory() { + return archiveDescriptorFactory; + } + + protected URL getArchiveUrl() { + return archiveUrl; + } + + protected String getEntryBasePrefix() { + return entryBasePrefix; + } + + protected String extractRelativeName(ZipEntry zipEntry) { + final String entryName = extractName( zipEntry ); + return entryBasePrefix == null ? entryName : entryName.substring( entryBasePrefix.length() ); + } + + protected String extractName(ZipEntry zipEntry) { + return normalizePathName( zipEntry.getName() ); + } + + protected String normalizePathName(String pathName) { + return pathName.startsWith( "/" ) ? pathName.substring( 1 ) : pathName; + } + + protected InputStreamAccess buildByteBasedInputStreamAccess(final String name, InputStream inputStream) { + // because of how jar InputStreams work we need to extract the bytes immediately. However, we + // do delay the creation of the ByteArrayInputStreams until needed + final byte[] bytes = ArchiveHelper.getBytesFromInputStreamSafely( inputStream ); + return new ByteArrayInputStreamAccess( name, bytes ); + } + +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/ArchiveContext.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/ArchiveContext.java new file mode 100644 index 0000000000..8674cc6bb5 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/ArchiveContext.java @@ -0,0 +1,37 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.archive.spi; + +import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; + +/** +* @author Steve Ebersole +*/ +public interface ArchiveContext { + public PersistenceUnitDescriptor getPersistenceUnitDescriptor(); + + public boolean isRootUrl(); + + public ArchiveEntryHandler obtainArchiveEntryHandler(ArchiveEntry entry); +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/Filter.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/ArchiveDescriptor.java similarity index 67% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/Filter.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/ArchiveDescriptor.java index 0d16548108..b9d07dee0d 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/Filter.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/ArchiveDescriptor.java @@ -1,8 +1,10 @@ /* - * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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. + * distributed under license by Red Hat Inc. * * 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 @@ -19,22 +21,14 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.packaging.internal; - +package org.hibernate.jpa.boot.archive.spi; /** - * Filter used when searching elements in a JAR + * Contract for visiting an archive, which might be a jar, a zip, an exploded directory, etc. * + * @author Steve Ebersole * @author Emmanuel Bernard */ -public abstract class Filter { - private boolean retrieveStream; - - protected Filter(boolean retrieveStream) { - this.retrieveStream = retrieveStream; - } - - public boolean getStream() { - return retrieveStream; - } +public interface ArchiveDescriptor { + public void visitArchive(ArchiveContext archiveContext); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/ArchiveDescriptorFactory.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/ArchiveDescriptorFactory.java new file mode 100644 index 0000000000..441b531b25 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/ArchiveDescriptorFactory.java @@ -0,0 +1,39 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.archive.spi; + +import java.net.URL; + +/** + * Contract for building ArchiveDescriptor instances. + * + * @author Steve Ebersole + */ +public interface ArchiveDescriptorFactory { + public ArchiveDescriptor buildArchiveDescriptor(URL url); + public ArchiveDescriptor buildArchiveDescriptor(URL jarUrl, String entry); + + public URL getJarURLFromURLEntry(URL url, String entry) throws IllegalArgumentException; + public URL getURLFromPath(String jarPath); +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JarVisitor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/ArchiveEntry.java similarity index 55% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JarVisitor.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/ArchiveEntry.java index cbb829ee3e..7d7e9185e0 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JarVisitor.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/ArchiveEntry.java @@ -1,8 +1,10 @@ /* - * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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. + * distributed under license by Red Hat Inc. * * 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 @@ -19,28 +21,35 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.packaging.internal; -import java.io.IOException; -import java.util.Set; +package org.hibernate.jpa.boot.archive.spi; + +import org.hibernate.jpa.boot.spi.InputStreamAccess; /** - * @author Emmanuel Bernard + * Represent an entry in the archive. + * + * @author Steve Ebersole */ -public interface JarVisitor { +public interface ArchiveEntry { /** - * Get the unqualified Jar name (ie wo path and wo extension) + * Get the entry's name * - * @return the unqualified jar name. + * @return */ - String getUnqualifiedJarName(); - - Filter[] getFilters(); + public String getName(); /** - * Return the matching entries for each filter in the same order the filter where passed + * Get the relative name of the entry within the archive. Typically what we are looking for here is + * the ClassLoader resource lookup name. * - * @return array of Set of JarVisitor.Entry - * @throws java.io.IOException if something went wrong + * @return */ - Set[] getMatchingEntries() throws IOException; + public String getNameWithinArchive(); + + /** + * Get access to the stream for the entry + * + * @return + */ + public InputStreamAccess getStreamAccess(); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/FileFilter.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/ArchiveEntryHandler.java similarity index 60% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/FileFilter.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/ArchiveEntryHandler.java index 8c8575be3f..3fce3402c0 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/FileFilter.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/ArchiveEntryHandler.java @@ -1,8 +1,10 @@ /* - * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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. + * distributed under license by Red Hat Inc. * * 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 @@ -19,25 +21,13 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.packaging.internal; - +package org.hibernate.jpa.boot.archive.spi; /** - * Filter use to match a file by its name + * Handler for archive entries, based on the classified type of the entry * - * @author Emmanuel Bernard + * @author Steve Ebersole */ -public abstract class FileFilter extends Filter { - - /** - * @param retrieveStream Give back an open stream to the matching element or not - */ - public FileFilter(boolean retrieveStream) { - super( retrieveStream ); - } - - /** - * Return true if the fully qualified file name match - */ - public abstract boolean accept(String name); - } \ No newline at end of file +public interface ArchiveEntryHandler { + public void handleEntry(ArchiveEntry entry, ArchiveContext context); +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/ClassFilter.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/ArchiveException.java similarity index 63% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/ClassFilter.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/ArchiveException.java index 6ea6dc08bb..1257502318 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/ClassFilter.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/archive/spi/ArchiveException.java @@ -1,8 +1,10 @@ /* - * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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. + * distributed under license by Red Hat Inc. * * 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 @@ -19,20 +21,19 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.packaging.internal; +package org.hibernate.jpa.boot.archive.spi; +import org.hibernate.HibernateException; /** - * Filter on class elements - * - * @author Emmanuel Bernard - * @see JavaElementFilter + * @author Steve Ebersole */ -public abstract class ClassFilter extends JavaElementFilter { - /** - * @see JavaElementFilter#JavaElementFilter(boolean, Class[]) - */ - protected ClassFilter(boolean retrieveStream, Class[] annotations) { - super( retrieveStream, annotations ); +public class ArchiveException extends HibernateException { + public ArchiveException(String message) { + super( message ); } -} \ No newline at end of file + + public ArchiveException(String message, Throwable root) { + super( message, root ); + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/ByteArrayInputStreamAccess.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/ByteArrayInputStreamAccess.java new file mode 100644 index 0000000000..8b949206ce --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/ByteArrayInputStreamAccess.java @@ -0,0 +1,60 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.internal; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import org.hibernate.jpa.boot.spi.InputStreamAccess; +import org.hibernate.jpa.boot.spi.NamedInputStream; + +/** + * An InputStreamAccess implementation based on a byte array + * + * @author Steve Ebersole + */ +public class ByteArrayInputStreamAccess implements InputStreamAccess { + private final String name; + private final byte[] bytes; + + public ByteArrayInputStreamAccess(String name, byte[] bytes) { + this.name = name; + this.bytes = bytes; + } + + @Override + public String getStreamName() { + return name; + } + + @Override + public InputStream accessInputStream() { + return new ByteArrayInputStream( bytes ); + } + + @Override + public NamedInputStream asNamedInputStream() { + return new NamedInputStream( getStreamName(), accessInputStream() ); + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/Entry.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/ClassDescriptorImpl.java similarity index 53% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/Entry.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/ClassDescriptorImpl.java index 584bfcb15c..f8d15c1419 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/Entry.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/ClassDescriptorImpl.java @@ -1,8 +1,10 @@ /* - * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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. + * distributed under license by Red Hat Inc. * * 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 @@ -19,44 +21,48 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.packaging.internal; -import java.io.InputStream; +package org.hibernate.jpa.boot.internal; + +import org.hibernate.jpa.boot.spi.ClassDescriptor; +import org.hibernate.jpa.boot.spi.InputStreamAccess; /** - * Represent a JAR entry - * Contains a name and an optional Input stream to the entry - * - * @author Emmanuel Bernard + * @author Steve Ebersole */ -public class Entry { - private String name; - private InputStream is; +public class ClassDescriptorImpl implements ClassDescriptor { + private final String name; + private final InputStreamAccess streamAccess; - public Entry(String name, InputStream is) { + public ClassDescriptorImpl(String name, InputStreamAccess streamAccess) { this.name = name; - this.is = is; + this.streamAccess = streamAccess; } + @Override public String getName() { return name; } - public InputStream getInputStream() { - return is; + @Override + public InputStreamAccess getStreamAccess() { + return streamAccess; } + @Override public boolean equals(Object o) { - if ( this == o ) return true; - if ( o == null || getClass() != o.getClass() ) return false; + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } - final Entry entry = (Entry) o; - - if ( !name.equals( entry.name ) ) return false; - - return true; + ClassDescriptorImpl that = (ClassDescriptorImpl) o; + return name.equals( that.name ); } + @Override public int hashCode() { return name.hashCode(); } -} \ No newline at end of file +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java index 2098a2908e..dd47f695b0 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java @@ -23,12 +23,17 @@ */ package org.hibernate.jpa.boot.internal; +import javax.persistence.AttributeConverter; +import javax.persistence.EntityManagerFactory; +import javax.persistence.EntityNotFoundException; +import javax.persistence.PersistenceException; +import javax.persistence.spi.PersistenceUnitTransactionType; +import javax.sql.DataSource; import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; -import java.lang.annotation.Annotation; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; @@ -42,18 +47,17 @@ import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; -import javax.persistence.AttributeConverter; -import javax.persistence.Converter; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.EntityManagerFactory; -import javax.persistence.EntityNotFoundException; -import javax.persistence.MappedSuperclass; -import javax.persistence.PersistenceException; -import javax.persistence.spi.PersistenceUnitTransactionType; -import javax.sql.DataSource; +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.Index; +import org.jboss.jandex.IndexView; +import org.jboss.jandex.Indexer; + +import org.jboss.logging.Logger; import org.hibernate.Interceptor; +import org.hibernate.InvalidMappingException; import org.hibernate.MappingException; import org.hibernate.MappingNotFoundException; import org.hibernate.SessionFactory; @@ -81,14 +85,21 @@ import org.hibernate.jpa.boot.spi.EntityManagerFactoryBuilder; import org.hibernate.jpa.boot.spi.IntegratorProvider; import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; import org.hibernate.jpa.event.spi.JpaIntegrator; -import org.hibernate.jpa.internal.schemagen.JpaSchemaGenerator; import org.hibernate.jpa.internal.EntityManagerFactoryImpl; import org.hibernate.jpa.internal.EntityManagerMessageLogger; +import org.hibernate.jpa.internal.schemagen.JpaSchemaGenerator; import org.hibernate.jpa.internal.util.LogHelper; import org.hibernate.jpa.internal.util.PersistenceUnitTransactionTypeHelper; -import org.hibernate.jpa.packaging.internal.NativeScanner; -import org.hibernate.jpa.packaging.spi.NamedInputStream; -import org.hibernate.jpa.packaging.spi.Scanner; +import org.hibernate.jpa.boot.scan.internal.StandardScanOptions; +import org.hibernate.jpa.boot.scan.internal.StandardScanner; +import org.hibernate.jpa.boot.spi.ClassDescriptor; +import org.hibernate.jpa.boot.spi.InputStreamAccess; +import org.hibernate.jpa.boot.spi.MappingFileDescriptor; +import org.hibernate.jpa.boot.spi.NamedInputStream; +import org.hibernate.jpa.boot.spi.PackageDescriptor; +import org.hibernate.jpa.boot.scan.spi.ScanOptions; +import org.hibernate.jpa.boot.scan.spi.ScanResult; +import org.hibernate.jpa.boot.scan.spi.Scanner; import org.hibernate.jpa.spi.IdentifierGeneratorStrategyProvider; import org.hibernate.metamodel.source.annotations.JPADotNames; import org.hibernate.metamodel.source.annotations.JandexHelper; @@ -97,14 +108,6 @@ import org.hibernate.secure.internal.JACCConfiguration; import org.hibernate.service.ConfigLoader; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.spi.ServiceRegistryImplementor; -import org.jboss.jandex.AnnotationInstance; -import org.jboss.jandex.ClassInfo; -import org.jboss.jandex.CompositeIndex; -import org.jboss.jandex.DotName; -import org.jboss.jandex.Index; -import org.jboss.jandex.IndexView; -import org.jboss.jandex.Indexer; -import org.jboss.logging.Logger; /** * @author Steve Ebersole @@ -199,13 +202,14 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Next we do a preliminary pass at metadata processing, which involves: // 1) scanning - ScanResult scanResult = scan( bootstrapServiceRegistry ); + final ScanResult scanResult = scan( bootstrapServiceRegistry ); + final DeploymentResources deploymentResources = buildDeploymentResources( scanResult, bootstrapServiceRegistry ); // 2) building a Jandex index - Set collectedManagedClassNames = collectManagedClassNames( scanResult ); - IndexView jandexIndex = locateOrBuildJandexIndex( collectedManagedClassNames, scanResult.getPackageNames(), bootstrapServiceRegistry ); + final IndexView jandexIndex = locateOrBuildJandexIndex( deploymentResources ); // 3) building "metadata sources" to keep for later to use in building the SessionFactory - metadataSources = prepareMetadataSources( jandexIndex, collectedManagedClassNames, scanResult, bootstrapServiceRegistry ); + metadataSources = prepareMetadataSources( jandexIndex, deploymentResources, bootstrapServiceRegistry ); + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ withValidatorFactory( configurationValues.get( AvailableSettings.VALIDATION_FACTORY ) ); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -217,6 +221,122 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil } } + private static interface DeploymentResources { + public Iterable getClassDescriptors(); + public Iterable getPackageDescriptors(); + public Iterable getMappingFileDescriptors(); + } + + private DeploymentResources buildDeploymentResources( + ScanResult scanResult, + BootstrapServiceRegistry bootstrapServiceRegistry) { + + // mapping files ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + final ArrayList mappingFileDescriptors = new ArrayList(); + + final Set nonLocatedMappingFileNames = new HashSet(); + final List explicitMappingFileNames = persistenceUnit.getMappingFileNames(); + if ( explicitMappingFileNames != null ) { + nonLocatedMappingFileNames.addAll( explicitMappingFileNames ); + } + + for ( MappingFileDescriptor mappingFileDescriptor : scanResult.getLocatedMappingFiles() ) { + mappingFileDescriptors.add( mappingFileDescriptor ); + nonLocatedMappingFileNames.remove( mappingFileDescriptor.getName() ); + } + + for ( String name : nonLocatedMappingFileNames ) { + MappingFileDescriptor descriptor = buildMappingFileDescriptor( name, bootstrapServiceRegistry ); + mappingFileDescriptors.add( descriptor ); + } + + + // classes and packages ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + final HashMap classDescriptorMap = new HashMap(); + final HashMap packageDescriptorMap = new HashMap(); + + for ( ClassDescriptor classDescriptor : scanResult.getLocatedClasses() ) { + classDescriptorMap.put( classDescriptor.getName(), classDescriptor ); + } + + for ( PackageDescriptor packageDescriptor : scanResult.getLocatedPackages() ) { + packageDescriptorMap.put( packageDescriptor.getName(), packageDescriptor ); + } + + final List explicitClassNames = persistenceUnit.getManagedClassNames(); + if ( explicitClassNames != null ) { + for ( String explicitClassName : explicitClassNames ) { + // IMPL NOTE : explicitClassNames can contain class or package names!!! + if ( classDescriptorMap.containsKey( explicitClassName ) ) { + continue; + } + if ( packageDescriptorMap.containsKey( explicitClassName ) ) { + continue; + } + + // try it as a class name first... + final String classFileName = explicitClassName.replace( '.', '/' ) + ".class"; + final URL classFileUrl = bootstrapServiceRegistry.getService( ClassLoaderService.class ) + .locateResource( classFileName ); + if ( classFileUrl != null ) { + classDescriptorMap.put( + explicitClassName, + new ClassDescriptorImpl( explicitClassName, new UrlInputStreamAccess( classFileUrl ) ) + ); + continue; + } + + // otherwise, try it as a package name + final String packageInfoFileName = explicitClassName.replace( '.', '/' ) + "/package-info.class"; + final URL packageInfoFileUrl = bootstrapServiceRegistry.getService( ClassLoaderService.class ) + .locateResource( packageInfoFileName ); + if ( packageInfoFileUrl != null ) { + packageDescriptorMap.put( + explicitClassName, + new PackageDescriptorImpl( explicitClassName, new UrlInputStreamAccess( packageInfoFileUrl ) ) + ); + continue; + } + + LOG.debugf( + "Unable to resolve class [%s] named in persistence unit [%s]", + explicitClassName, + persistenceUnit.getName() + ); + } + } + + return new DeploymentResources() { + @Override + public Iterable getClassDescriptors() { + return classDescriptorMap.values(); + } + + @Override + public Iterable getPackageDescriptors() { + return packageDescriptorMap.values(); + } + + @Override + public Iterable getMappingFileDescriptors() { + return mappingFileDescriptors; + } + }; + } + + private MappingFileDescriptor buildMappingFileDescriptor( + String name, + BootstrapServiceRegistry bootstrapServiceRegistry) { + final URL url = bootstrapServiceRegistry.getService( ClassLoaderService.class ).locateResource( name ); + if ( url == null ) { + throw persistenceException( "Unable to resolve named mapping-file [" + name + "]" ); + } + + return new MappingFileDescriptorImpl( name, new UrlInputStreamAccess( url ) ); + } + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // temporary! @SuppressWarnings("unchecked") @@ -233,13 +353,13 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil @SuppressWarnings("unchecked") private MetadataSources prepareMetadataSources( IndexView jandexIndex, - Set collectedManagedClassNames, - ScanResult scanResult, + DeploymentResources deploymentResources, BootstrapServiceRegistry bootstrapServiceRegistry) { // todo : this needs to tie into the metamodel branch... MetadataSources metadataSources = new MetadataSources(); - for ( String className : collectedManagedClassNames ) { + for ( ClassDescriptor classDescriptor : deploymentResources.getClassDescriptors() ) { + final String className = classDescriptor.getName(); final ClassInfo classInfo = jandexIndex.getClassByName( DotName.createSimple( className ) ); if ( classInfo == null ) { // Not really sure what this means. Most likely it is explicitly listed in the persistence unit, @@ -266,15 +386,19 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil } } - metadataSources.packageNames.addAll( scanResult.getPackageNames() ); + for ( PackageDescriptor packageDescriptor : deploymentResources.getPackageDescriptors() ) { + metadataSources.packageNames.add( packageDescriptor.getName() ); + } - metadataSources.namedMappingFileInputStreams.addAll( scanResult.getHbmFiles() ); + for ( MappingFileDescriptor mappingFileDescriptor : deploymentResources.getMappingFileDescriptors() ) { + metadataSources.namedMappingFileInputStreams.add( mappingFileDescriptor.getStreamAccess().asNamedInputStream() ); + } - metadataSources.mappingFileResources.addAll( scanResult.getMappingFiles() ); final String explicitHbmXmls = (String) configurationValues.remove( AvailableSettings.HBXML_FILES ); if ( explicitHbmXmls != null ) { metadataSources.mappingFileResources.addAll( Arrays.asList( StringHelper.split( ", ", explicitHbmXmls ) ) ); } + final List explicitOrmXml = (List) configurationValues.remove( AvailableSettings.XML_FILE_NAMES ); if ( explicitOrmXml != null ) { metadataSources.mappingFileResources.addAll( explicitOrmXml ); @@ -283,41 +407,26 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil return metadataSources; } - private Set collectManagedClassNames(ScanResult scanResult) { - Set collectedNames = new HashSet(); - if ( persistenceUnit.getManagedClassNames() != null ) { - collectedNames.addAll( persistenceUnit.getManagedClassNames() ); - } - collectedNames.addAll( scanResult.getManagedClassNames() ); - return collectedNames; - } - - private IndexView locateOrBuildJandexIndex( - Set collectedManagedClassNames, - List packageNames, - BootstrapServiceRegistry bootstrapServiceRegistry) { + private IndexView locateOrBuildJandexIndex(DeploymentResources deploymentResources) { // for now create a whole new Index to work with, eventually we need to: // 1) accept an Index as an incoming config value // 2) pass that Index along to the metamodel code... - // - // (1) is mocked up here, but JBoss AS does not currently pass in any Index to use... IndexView jandexIndex = (IndexView) configurationValues.get( JANDEX_INDEX ); if ( jandexIndex == null ) { - jandexIndex = buildJandexIndex( collectedManagedClassNames, packageNames, bootstrapServiceRegistry ); + jandexIndex = buildJandexIndex( deploymentResources ); } return jandexIndex; } - private IndexView buildJandexIndex(Set classNamesSource, List packageNames, BootstrapServiceRegistry bootstrapServiceRegistry) { + private IndexView buildJandexIndex(DeploymentResources deploymentResources) { Indexer indexer = new Indexer(); - for ( String className : classNamesSource ) { - indexResource( className.replace( '.', '/' ) + ".class", indexer, bootstrapServiceRegistry ); + for ( ClassDescriptor classDescriptor : deploymentResources.getClassDescriptors() ) { + indexStream( indexer, classDescriptor.getStreamAccess() ); } - // add package-info from the configured packages - for ( String packageName : packageNames ) { - indexResource( packageName.replace( '.', '/' ) + "/package-info.class", indexer, bootstrapServiceRegistry ); + for ( PackageDescriptor packageDescriptor : deploymentResources.getPackageDescriptors() ) { + indexStream( indexer, packageDescriptor.getStreamAccess() ); } // for now we just skip entities defined in (1) orm.xml files and (2) hbm.xml files. this part really needs @@ -325,16 +434,25 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil // for now, we also need to wrap this in a CompositeIndex until Jandex is updated to use a common interface // between the 2... - return CompositeIndex.create( indexer.complete() ); + return indexer.complete(); } - private void indexResource(String resourceName, Indexer indexer, BootstrapServiceRegistry bootstrapServiceRegistry) { - InputStream stream = bootstrapServiceRegistry.getService( ClassLoaderService.class ).locateResourceStream( resourceName ); + private void indexStream(Indexer indexer, InputStreamAccess streamAccess) { try { - indexer.index( stream ); + InputStream stream = streamAccess.accessInputStream(); + try { + indexer.index( stream ); + } + finally { + try { + stream.close(); + } + catch (Exception ignore) { + } + } } catch ( IOException e ) { - throw persistenceException( "Unable to open input stream for resource " + resourceName, e ); + throw persistenceException( "Unable to index from stream " + streamAccess.getStreamName(), e ); } } @@ -586,37 +704,24 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil @SuppressWarnings("unchecked") private ScanResult scan(BootstrapServiceRegistry bootstrapServiceRegistry) { - Scanner scanner = locateOrBuildScanner( bootstrapServiceRegistry ); - ScanningContext scanningContext = new ScanningContext(); + final Scanner scanner = locateOrBuildScanner( bootstrapServiceRegistry ); + final ScanOptions scanOptions = determineScanOptions(); - final ScanResult scanResult = new ScanResult(); - if ( persistenceUnit.getMappingFileNames() != null ) { - scanResult.getMappingFiles().addAll( persistenceUnit.getMappingFileNames() ); - } + return scanner.scan( persistenceUnit, scanOptions ); + } - // dunno, but the old code did it... - scanningContext.setSearchOrm( ! scanResult.getMappingFiles().contains( META_INF_ORM_XML ) ); - - if ( persistenceUnit.getJarFileUrls() != null ) { - prepareAutoDetectionSettings( scanningContext, false ); - for ( URL jar : persistenceUnit.getJarFileUrls() ) { - scanningContext.setUrl( jar ); - scanInContext( scanner, scanningContext, scanResult ); - } - } - - prepareAutoDetectionSettings( scanningContext, persistenceUnit.isExcludeUnlistedClasses() ); - scanningContext.setUrl( persistenceUnit.getPersistenceUnitRootUrl() ); - scanInContext( scanner, scanningContext, scanResult ); - - return scanResult; + private ScanOptions determineScanOptions() { + return new StandardScanOptions( + (String) configurationValues.get( AvailableSettings.AUTODETECTION ), + persistenceUnit.isExcludeUnlistedClasses() + ); } @SuppressWarnings("unchecked") private Scanner locateOrBuildScanner(BootstrapServiceRegistry bootstrapServiceRegistry) { final Object value = configurationValues.remove( AvailableSettings.SCANNER ); if ( value == null ) { - return new NativeScanner(); + return new StandardScanner(); } if ( Scanner.class.isInstance( value ) ) { @@ -650,91 +755,6 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil } } - private void prepareAutoDetectionSettings(ScanningContext context, boolean excludeUnlistedClasses) { - final String detectionSetting = (String) configurationValues.get( AvailableSettings.AUTODETECTION ); - - if ( detectionSetting == null ) { - if ( excludeUnlistedClasses ) { - context.setDetectClasses( false ); - context.setDetectHbmFiles( false ); - } - else { - context.setDetectClasses( true ); - context.setDetectHbmFiles( true ); - } - } - else { - for ( String token : StringHelper.split( ", ", detectionSetting ) ) { - if ( "class".equalsIgnoreCase( token ) ) { - context.setDetectClasses( true ); - } - if ( "hbm".equalsIgnoreCase( token ) ) { - context.setDetectClasses( true ); - } - } - } - } - - private void scanInContext( - Scanner scanner, - ScanningContext scanningContext, - ScanResult scanResult) { - if ( scanningContext.getUrl() == null ) { - // not sure i like just ignoring this being null, but this is exactly what the old code does... - LOG.containerProvidingNullPersistenceUnitRootUrl(); - return; - } - if ( scanningContext.getUrl().getProtocol().equalsIgnoreCase( "bundle" ) ) { - // TODO: Is there a way to scan the root bundle URL in OSGi containers? - // Although the URL provides a stream handler that works for finding - // resources in a specific Bundle, the root one does not work. - return; - } - - try { - if ( scanningContext.isDetectClasses() ) { - Set matchingPackages = scanner.getPackagesInJar( scanningContext.url, new HashSet>(0) ); - for ( Package pkg : matchingPackages ) { - scanResult.getPackageNames().add( pkg.getName() ); - } - - Set> annotationsToLookFor = new HashSet>(); - annotationsToLookFor.add( Entity.class ); - annotationsToLookFor.add( MappedSuperclass.class ); - annotationsToLookFor.add( Embeddable.class ); - annotationsToLookFor.add( Converter.class ); - Set> matchingClasses = scanner.getClassesInJar( scanningContext.url, annotationsToLookFor ); - for ( Class clazz : matchingClasses ) { - scanResult.getManagedClassNames().add( clazz.getName() ); - } - } - - Set patterns = new HashSet(); - if ( scanningContext.isSearchOrm() ) { - patterns.add( META_INF_ORM_XML ); - } - if ( scanningContext.isDetectHbmFiles() ) { - patterns.add( "**/*.hbm.xml" ); - } - if ( ! scanResult.getMappingFiles().isEmpty() ) { - patterns.addAll( scanResult.getMappingFiles() ); - } - if ( patterns.size() != 0 ) { - Set files = scanner.getFilesInJar( scanningContext.getUrl(), patterns ); - for ( NamedInputStream file : files ) { - scanResult.getHbmFiles().add( file ); - scanResult.getMappingFiles().remove( file.getName() ); - } - } - } - catch (PersistenceException e ) { - throw e; - } - catch ( RuntimeException e ) { - throw persistenceException( "error trying to scan url: " + scanningContext.getUrl().toString(), e ); - } - } - @Override public EntityManagerFactoryBuilder withValidatorFactory(Object validatorFactory) { this.validatorFactory = validatorFactory; @@ -1094,14 +1114,28 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil //addInputStream has the responsibility to close the stream cfg.addInputStream( new BufferedInputStream( namedInputStream.getStream() ) ); } - catch (MappingException me) { - //try our best to give the file name - if ( StringHelper.isEmpty( namedInputStream.getName() ) ) { - throw me; + catch ( InvalidMappingException e ) { + // try our best to give the file name + if ( StringHelper.isNotEmpty( namedInputStream.getName() ) ) { + throw new InvalidMappingException( + "Error while parsing file: " + namedInputStream.getName(), + e.getType(), + e.getPath(), + e + ); } else { + throw e; + } + } + catch (MappingException me) { + // try our best to give the file name + if ( StringHelper.isNotEmpty( namedInputStream.getName() ) ) { throw new MappingException("Error while parsing file: " + namedInputStream.getName(), me ); } + else { + throw me; + } } } for ( String packageName : metadataSources.packageNames ) { @@ -1176,68 +1210,6 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil } } - public static class ScanningContext { - private URL url; - private boolean detectClasses; - private boolean detectHbmFiles; - private boolean searchOrm; - - public URL getUrl() { - return url; - } - - public void setUrl(URL url) { - this.url = url; - } - - public boolean isDetectClasses() { - return detectClasses; - } - - public void setDetectClasses(boolean detectClasses) { - this.detectClasses = detectClasses; - } - - public boolean isDetectHbmFiles() { - return detectHbmFiles; - } - - public void setDetectHbmFiles(boolean detectHbmFiles) { - this.detectHbmFiles = detectHbmFiles; - } - - public boolean isSearchOrm() { - return searchOrm; - } - - public void setSearchOrm(boolean searchOrm) { - this.searchOrm = searchOrm; - } - } - - private static class ScanResult { - private final List managedClassNames = new ArrayList(); - private final List packageNames = new ArrayList(); - private final List hbmFiles = new ArrayList(); - private final List mappingFiles = new ArrayList(); - - public List getManagedClassNames() { - return managedClassNames; - } - - public List getPackageNames() { - return packageNames; - } - - public List getHbmFiles() { - return hbmFiles; - } - - public List getMappingFiles() { - return mappingFiles; - } - } - public static class MetadataSources { private final List annotatedMappingClassNames = new ArrayList(); private final List converterDescriptors = new ArrayList(); diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/FileInputStreamAccess.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/FileInputStreamAccess.java new file mode 100644 index 0000000000..7d9294364a --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/FileInputStreamAccess.java @@ -0,0 +1,77 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.internal; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; + +import org.hibernate.HibernateException; +import org.hibernate.jpa.boot.archive.spi.ArchiveException; +import org.hibernate.jpa.boot.spi.InputStreamAccess; +import org.hibernate.jpa.boot.spi.NamedInputStream; + +/** + * An InputStreamAccess implementation based on a File reference + * + * @author Steve Ebersole + */ +public class FileInputStreamAccess implements InputStreamAccess { + private final String name; + private final File file; + + public FileInputStreamAccess(String name, File file) { + this.name = name; + this.file = file; + if ( ! file.exists() ) { + throw new HibernateException( "File must exist : " + file.getAbsolutePath() ); + } + } + + @Override + public String getStreamName() { + return name; + } + + @Override + public InputStream accessInputStream() { + try { + return new BufferedInputStream( new FileInputStream( file ) ); + } + catch (FileNotFoundException e) { + // should never ever ever happen, but... + throw new ArchiveException( + "File believed to exist based on File.exists threw error when passed to FileInputStream ctor", + e + ); + } + } + + @Override + public NamedInputStream asNamedInputStream() { + return new NamedInputStream( getStreamName(), accessInputStream() ); + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/MappingFileDescriptorImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/MappingFileDescriptorImpl.java new file mode 100644 index 0000000000..1a073c4002 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/MappingFileDescriptorImpl.java @@ -0,0 +1,73 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.internal; + +import org.hibernate.jpa.boot.spi.InputStreamAccess; +import org.hibernate.jpa.boot.spi.MappingFileDescriptor; + +/** + * @author Steve Ebersole + */ +public class MappingFileDescriptorImpl implements MappingFileDescriptor { + private final String name; + private final InputStreamAccess streamAccess; + + public MappingFileDescriptorImpl(String name, InputStreamAccess streamAccess) { + this.name = name; + this.streamAccess = streamAccess; + } + + @Override + public String getName() { + return name; + } + + @Override + public InputStreamAccess getStreamAccess() { + return streamAccess; + } + +// @Override +// public boolean equals(Object o) { +// if ( this == o ) { +// return true; +// } +// if ( o == null || getClass() != o.getClass() ) { +// return false; +// } +// +// MappingFileDescriptorImpl that = (MappingFileDescriptorImpl) o; +// +// return name.equals( that.name ) +// && streamAccess.getStreamName().equals( that.streamAccess.getStreamName() ); +// +// } +// +// @Override +// public int hashCode() { +// int result = name.hashCode(); +// result = 31 * result + streamAccess.getStreamName().hashCode(); +// return result; +// } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/PackageDescriptorImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/PackageDescriptorImpl.java new file mode 100644 index 0000000000..c40eb48786 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/PackageDescriptorImpl.java @@ -0,0 +1,68 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.internal; + +import org.hibernate.jpa.boot.spi.InputStreamAccess; +import org.hibernate.jpa.boot.spi.PackageDescriptor; + +/** + * @author Steve Ebersole + */ +public class PackageDescriptorImpl implements PackageDescriptor { + private final String name; + private final InputStreamAccess streamAccess; + + public PackageDescriptorImpl(String name, InputStreamAccess streamAccess) { + this.name = name; + this.streamAccess = streamAccess; + } + + @Override + public String getName() { + return name; + } + + @Override + public InputStreamAccess getStreamAccess() { + return streamAccess; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + PackageDescriptorImpl that = (PackageDescriptorImpl) o; + return name.equals( that.name ); + } + + @Override + public int hashCode() { + return name.hashCode(); + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/PersistenceXmlParser.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/PersistenceXmlParser.java index 311cc38079..e079c99c8c 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/PersistenceXmlParser.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/PersistenceXmlParser.java @@ -54,8 +54,8 @@ import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.hibernate.jpa.AvailableSettings; +import org.hibernate.jpa.boot.archive.internal.ArchiveHelper; import org.hibernate.jpa.internal.EntityManagerMessageLogger; -import org.hibernate.jpa.packaging.internal.JarVisitorFactory; import org.hibernate.jpa.internal.util.ConfigurationHelper; import org.hibernate.internal.util.StringHelper; import org.hibernate.metamodel.source.XsdException; @@ -121,7 +121,7 @@ public class PersistenceXmlParser { final Element element = (Element) children.item( i ); final String tag = element.getTagName(); if ( tag.equals( "persistence-unit" ) ) { - final URL puRootUrl = JarVisitorFactory.getJarURLFromURLEntry( xmlUrl, "/META-INF/persistence.xml" ); + final URL puRootUrl = ArchiveHelper.getJarURLFromURLEntry( xmlUrl, "/META-INF/persistence.xml" ); ParsedPersistenceXmlDescriptor persistenceUnit = new ParsedPersistenceXmlDescriptor( puRootUrl ); bindPersistenceUnit( persistenceUnit, element ); @@ -214,7 +214,7 @@ public class PersistenceXmlParser { persistenceUnit.addMappingFiles( extractContent( element ) ); } else if ( tag.equals( "jar-file" ) ) { - persistenceUnit.addJarFileUrl( JarVisitorFactory.getURLFromPath( extractContent( element ) ) ); + persistenceUnit.addJarFileUrl( ArchiveHelper.getURLFromPath( extractContent( element ) ) ); } else if ( tag.equals( "exclude-unlisted-classes" ) ) { persistenceUnit.setExcludeUnlistedClasses( true ); diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/UrlInputStreamAccess.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/UrlInputStreamAccess.java new file mode 100644 index 0000000000..4281201381 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/UrlInputStreamAccess.java @@ -0,0 +1,62 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.internal; + +import java.io.InputStream; +import java.net.URL; + +import org.hibernate.HibernateException; +import org.hibernate.jpa.boot.spi.InputStreamAccess; +import org.hibernate.jpa.boot.spi.NamedInputStream; + +/** + * @author Steve Ebersole + */ +public class UrlInputStreamAccess implements InputStreamAccess { + private final URL url; + + public UrlInputStreamAccess(URL url) { + this.url = url; + } + + @Override + public String getStreamName() { + return url.toExternalForm(); + } + + @Override + public InputStream accessInputStream() { + try { + return url.openStream(); + } + catch (Exception e) { + throw new HibernateException( "Could not open url stream : " + url.toExternalForm() ); + } + } + + @Override + public NamedInputStream asNamedInputStream() { + return new NamedInputStream( getStreamName(), accessInputStream() ); + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/internal/StandardScanOptions.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/internal/StandardScanOptions.java new file mode 100644 index 0000000000..e2dcf9e154 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/internal/StandardScanOptions.java @@ -0,0 +1,67 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.scan.internal; + +import org.hibernate.jpa.boot.scan.spi.ScanOptions; + +/** + * @author Steve Ebersole + */ +public class StandardScanOptions implements ScanOptions { + private final boolean detectClassesInRoot; + private final boolean detectClassesInNonRoot; + private final boolean detectHibernateMappingFiles; + + public StandardScanOptions() { + this( "hbm,class", false ); + } + + public StandardScanOptions(String explicitDetectionSetting, boolean persistenceUnitExcludeUnlistedClassesValue) { + if ( explicitDetectionSetting == null ) { + detectHibernateMappingFiles = true; + detectClassesInRoot = ! persistenceUnitExcludeUnlistedClassesValue; + detectClassesInNonRoot = true; + } + else { + detectHibernateMappingFiles = explicitDetectionSetting.contains( "hbm" ); + detectClassesInRoot = explicitDetectionSetting.contains( "class" ); + detectClassesInNonRoot = detectClassesInRoot; + } + } + + @Override + public boolean canDetectUnlistedClassesInRoot() { + return detectClassesInRoot; + } + + @Override + public boolean canDetectUnlistedClassesInNonRoot() { + return detectClassesInNonRoot; + } + + @Override + public boolean canDetectHibernateMappingFiles() { + return detectHibernateMappingFiles; + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/internal/StandardScanner.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/internal/StandardScanner.java new file mode 100644 index 0000000000..6db4050a0e --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/internal/StandardScanner.java @@ -0,0 +1,40 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.scan.internal; + +import org.hibernate.jpa.boot.archive.internal.StandardArchiveDescriptorFactory; +import org.hibernate.jpa.boot.scan.spi.AbstractScannerImpl; + +/** + * Standard implementation of the Scanner contract, supporting typical archive walking support where + * the urls we are processing can be treated using normal file handling. + * + * @author Steve Ebersole + * @author Emmanuel Bernard + */ +public class StandardScanner extends AbstractScannerImpl { + public StandardScanner() { + super( StandardArchiveDescriptorFactory.INSTANCE ); + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/AbstractScannerImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/AbstractScannerImpl.java new file mode 100644 index 0000000000..da5af94f25 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/AbstractScannerImpl.java @@ -0,0 +1,302 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.scan.spi; + +import java.net.URL; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.hibernate.jpa.boot.archive.spi.ArchiveContext; +import org.hibernate.jpa.boot.archive.spi.ArchiveDescriptor; +import org.hibernate.jpa.boot.archive.spi.ArchiveDescriptorFactory; +import org.hibernate.jpa.boot.archive.spi.ArchiveEntry; +import org.hibernate.jpa.boot.archive.spi.ArchiveEntryHandler; +import org.hibernate.jpa.boot.internal.ClassDescriptorImpl; +import org.hibernate.jpa.boot.internal.MappingFileDescriptorImpl; +import org.hibernate.jpa.boot.internal.PackageDescriptorImpl; +import org.hibernate.jpa.boot.spi.ClassDescriptor; +import org.hibernate.jpa.boot.spi.MappingFileDescriptor; +import org.hibernate.jpa.boot.spi.PackageDescriptor; +import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; + +/** + * @author Steve Ebersole + */ +public abstract class AbstractScannerImpl implements Scanner { + private final ArchiveDescriptorFactory archiveDescriptorFactory; + private final Map archiveDescriptorCache = new HashMap(); + + protected AbstractScannerImpl(ArchiveDescriptorFactory archiveDescriptorFactory) { + this.archiveDescriptorFactory = archiveDescriptorFactory; + } + + @Override + public ScanResult scan(PersistenceUnitDescriptor persistenceUnit, ScanOptions scanOptions) { + final ResultCollector resultCollector = new ResultCollector( scanOptions ); + + if ( persistenceUnit.getJarFileUrls() != null ) { + for ( URL url : persistenceUnit.getJarFileUrls() ) { + final ArchiveDescriptor descriptor = buildArchiveDescriptor( url, false, scanOptions ); + final ArchiveContext context = buildArchiveContext( persistenceUnit, false, resultCollector ); + descriptor.visitArchive( context ); + } + } + + if ( persistenceUnit.getPersistenceUnitRootUrl() != null ) { + final ArchiveDescriptor descriptor = buildArchiveDescriptor( persistenceUnit.getPersistenceUnitRootUrl(), true, scanOptions ); + final ArchiveContext context = buildArchiveContext( persistenceUnit, false, resultCollector ); + descriptor.visitArchive( context ); + } + + return ScanResultImpl.from( resultCollector ); + } + + private ArchiveContext buildArchiveContext( + PersistenceUnitDescriptor persistenceUnit, + boolean isRoot, + ArchiveEntryHandlers entryHandlers) { + return new ArchiveContextImpl( persistenceUnit, isRoot, entryHandlers ); + } + + protected static interface ArchiveEntryHandlers { + public ArchiveEntryHandler getClassFileHandler(); + public ArchiveEntryHandler getPackageInfoHandler(); + public ArchiveEntryHandler getFileHandler(); + } + + private ArchiveDescriptor buildArchiveDescriptor(URL url, boolean isRootUrl, ScanOptions scanOptions) { + final ArchiveDescriptor descriptor; + final ArchiveDescriptorInfo descriptorInfo = archiveDescriptorCache.get( url ); + if ( descriptorInfo == null ) { + descriptor = archiveDescriptorFactory.buildArchiveDescriptor( url ); + archiveDescriptorCache.put( + url, + new ArchiveDescriptorInfo( descriptor, isRootUrl, scanOptions ) + ); + } + else { + validateReuse( descriptorInfo, isRootUrl, scanOptions ); + descriptor = descriptorInfo.archiveDescriptor; + } + return descriptor; + } + + public static class ResultCollector + implements ArchiveEntryHandlers, + PackageInfoArchiveEntryHandler.Callback, + ClassFileArchiveEntryHandler.Callback, + NonClassFileArchiveEntryHandler.Callback { + private final ClassFileArchiveEntryHandler classFileHandler; + private final PackageInfoArchiveEntryHandler packageInfoHandler; + private final NonClassFileArchiveEntryHandler fileHandler; + + private final Set packageDescriptorSet = new HashSet(); + private final Set classDescriptorSet = new HashSet(); + private final Set mappingFileSet = new HashSet(); + + public ResultCollector(ScanOptions scanOptions) { + this.classFileHandler = new ClassFileArchiveEntryHandler( scanOptions, this ); + this.packageInfoHandler = new PackageInfoArchiveEntryHandler( scanOptions, this ); + this.fileHandler = new NonClassFileArchiveEntryHandler( scanOptions, this ); + } + + @Override + public ArchiveEntryHandler getClassFileHandler() { + return classFileHandler; + } + + @Override + public ArchiveEntryHandler getPackageInfoHandler() { + return packageInfoHandler; + } + + @Override + public ArchiveEntryHandler getFileHandler() { + return fileHandler; + } + + @Override + public void locatedPackage(PackageDescriptor packageDescriptor) { + if ( PackageDescriptorImpl.class.isInstance( packageDescriptor ) ) { + packageDescriptorSet.add( packageDescriptor ); + } + else { + // to make sure we have proper equals/hashcode + packageDescriptorSet.add( + new PackageDescriptorImpl( + packageDescriptor.getName(), + packageDescriptor.getStreamAccess() + ) + ); + } + } + + @Override + public void locatedClass(ClassDescriptor classDescriptor) { + if ( ClassDescriptorImpl.class.isInstance( classDescriptor ) ) { + classDescriptorSet.add( classDescriptor ); + } + else { + // to make sure we have proper equals/hashcode + classDescriptorSet.add( + new ClassDescriptorImpl( + classDescriptor.getName(), + classDescriptor.getStreamAccess() + ) + ); + } + } + + @Override + public void locatedMappingFile(MappingFileDescriptor mappingFileDescriptor) { + if ( MappingFileDescriptorImpl.class.isInstance( mappingFileDescriptor ) ) { + mappingFileSet.add( mappingFileDescriptor ); + } + else { + // to make sure we have proper equals/hashcode + mappingFileSet.add( + new MappingFileDescriptorImpl( + mappingFileDescriptor.getName(), + mappingFileDescriptor.getStreamAccess() + ) + ); + } + } + + public Set getPackageDescriptorSet() { + return packageDescriptorSet; + } + + public Set getClassDescriptorSet() { + return classDescriptorSet; + } + + public Set getMappingFileSet() { + return mappingFileSet; + } + } + + private static class ArchiveDescriptorInfo { + private final ArchiveDescriptor archiveDescriptor; + private final boolean isRoot; + private final ScanOptions scanOptions; + + private ArchiveDescriptorInfo( + ArchiveDescriptor archiveDescriptor, + boolean isRoot, + ScanOptions scanOptions) { + this.archiveDescriptor = archiveDescriptor; + this.isRoot = isRoot; + this.scanOptions = scanOptions; + } + } + + protected void validateReuse(ArchiveDescriptorInfo descriptor, boolean root, ScanOptions options) { + // is it really reasonable that a single url be processed multiple times? + // for now, throw an exception, mainly because I am interested in situations where this might happen + throw new IllegalStateException( "ArchiveDescriptor reused; can URLs be processed multiple times?" ); + } + + public static class ArchiveContextImpl implements ArchiveContext { + private final PersistenceUnitDescriptor persistenceUnitDescriptor; + private final boolean isRootUrl; + private final ArchiveEntryHandlers entryHandlers; + + public ArchiveContextImpl( + PersistenceUnitDescriptor persistenceUnitDescriptor, + boolean isRootUrl, + ArchiveEntryHandlers entryHandlers) { + this.persistenceUnitDescriptor = persistenceUnitDescriptor; + this.isRootUrl = isRootUrl; + this.entryHandlers = entryHandlers; + } + + @Override + public PersistenceUnitDescriptor getPersistenceUnitDescriptor() { + return persistenceUnitDescriptor; + } + + @Override + public boolean isRootUrl() { + return isRootUrl; + } + + @Override + public ArchiveEntryHandler obtainArchiveEntryHandler(ArchiveEntry entry) { + final String nameWithinArchive = entry.getNameWithinArchive(); + + if ( nameWithinArchive.endsWith( "package-info.class" ) ) { + return entryHandlers.getPackageInfoHandler(); + } + else if ( nameWithinArchive.endsWith( ".class" ) ) { + return entryHandlers.getClassFileHandler(); + } + else { + return entryHandlers.getFileHandler(); + } + } + } + + private static class ScanResultImpl implements ScanResult { + private final Set packageDescriptorSet; + private final Set classDescriptorSet; + private final Set mappingFileSet; + + private ScanResultImpl( + Set packageDescriptorSet, + Set classDescriptorSet, + Set mappingFileSet) { + this.packageDescriptorSet = packageDescriptorSet; + this.classDescriptorSet = classDescriptorSet; + this.mappingFileSet = mappingFileSet; + } + + private static ScanResult from(ResultCollector resultCollector) { + return new ScanResultImpl( + Collections.unmodifiableSet( resultCollector.packageDescriptorSet ), + Collections.unmodifiableSet( resultCollector.classDescriptorSet ), + Collections.unmodifiableSet( resultCollector.mappingFileSet ) + ); + } + + @Override + public Set getLocatedPackages() { + return packageDescriptorSet; + } + + @Override + public Set getLocatedClasses() { + return classDescriptorSet; + } + + @Override + public Set getLocatedMappingFiles() { + return mappingFileSet; + } + } + +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/ClassFileArchiveEntryHandler.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/ClassFileArchiveEntryHandler.java new file mode 100644 index 0000000000..0bfcad0fc4 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/ClassFileArchiveEntryHandler.java @@ -0,0 +1,132 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.scan.spi; + +import javax.persistence.Converter; +import javax.persistence.Embeddable; +import javax.persistence.Entity; +import javax.persistence.MappedSuperclass; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; + +import javassist.bytecode.AnnotationsAttribute; +import javassist.bytecode.ClassFile; + +import org.hibernate.jpa.boot.archive.spi.ArchiveContext; +import org.hibernate.jpa.boot.archive.spi.ArchiveEntry; +import org.hibernate.jpa.boot.archive.spi.ArchiveEntryHandler; +import org.hibernate.jpa.boot.archive.spi.ArchiveException; +import org.hibernate.jpa.boot.internal.ClassDescriptorImpl; +import org.hibernate.jpa.boot.scan.spi.ScanOptions; +import org.hibernate.jpa.boot.spi.ClassDescriptor; + +/** +* @author Steve Ebersole +*/ +public class ClassFileArchiveEntryHandler implements ArchiveEntryHandler { + private final ScanOptions scanOptions; + private final Callback callback; + + public static interface Callback { + public void locatedClass(ClassDescriptor classDescriptor); + } + + public ClassFileArchiveEntryHandler(ScanOptions scanOptions, Callback callback) { + this.scanOptions = scanOptions; + this.callback = callback; + } + + @Override + public void handleEntry(ArchiveEntry entry, ArchiveContext context) { + final ClassFile classFile = toClassFile( entry ); + final ClassDescriptor classDescriptor = toClassDescriptor( classFile, entry ); + + if ( ! context.getPersistenceUnitDescriptor().getManagedClassNames().contains( classDescriptor.getName() ) ) { + if ( context.isRootUrl() ) { + if ( ! scanOptions.canDetectUnlistedClassesInRoot() ) { + return; + } + } + else { + if ( ! scanOptions.canDetectUnlistedClassesInNonRoot() ) { + return; + } + } + } + + // we are only interested in classes with certain annotations, so see if the ClassDescriptor + // represents a class which contains any of those annotations + if ( ! containsClassAnnotationsOfInterest( classFile ) ) { + return; + } + + notifyMatchedClass( classDescriptor ); + } + + private ClassFile toClassFile(ArchiveEntry entry) { + final InputStream inputStream = entry.getStreamAccess().accessInputStream(); + final DataInputStream dataInputStream = new DataInputStream( inputStream ); + try { + return new ClassFile( dataInputStream ); + } + catch (IOException e) { + throw new ArchiveException( "Could not build ClassFile" ); + } + finally { + try { + dataInputStream.close(); + } + catch (Exception ignore) { + } + + try { + inputStream.close(); + } + catch (IOException ignore) { + } + } + } + + @SuppressWarnings("SimplifiableIfStatement") + private boolean containsClassAnnotationsOfInterest(ClassFile cf) { + final AnnotationsAttribute visibleAnnotations = (AnnotationsAttribute) cf.getAttribute( AnnotationsAttribute.visibleTag ); + if ( visibleAnnotations == null ) { + return false; + } + + return visibleAnnotations.getAnnotation( Entity.class.getName() ) != null + || visibleAnnotations.getAnnotation( MappedSuperclass.class.getName() ) != null + || visibleAnnotations.getAnnotation( Embeddable.class.getName() ) != null + || visibleAnnotations.getAnnotation( Converter.class.getName() ) != null; + } + + protected ClassDescriptor toClassDescriptor(ClassFile classFile, ArchiveEntry entry) { + return new ClassDescriptorImpl( classFile.getName(), entry.getStreamAccess() ); + } + + protected final void notifyMatchedClass(ClassDescriptor classDescriptor) { + callback.locatedClass( classDescriptor ); + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/NonClassFileArchiveEntryHandler.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/NonClassFileArchiveEntryHandler.java new file mode 100644 index 0000000000..a3aa33a437 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/NonClassFileArchiveEntryHandler.java @@ -0,0 +1,82 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.scan.spi; + +import org.hibernate.jpa.boot.archive.spi.ArchiveContext; +import org.hibernate.jpa.boot.archive.spi.ArchiveEntry; +import org.hibernate.jpa.boot.archive.spi.ArchiveEntryHandler; +import org.hibernate.jpa.boot.internal.MappingFileDescriptorImpl; +import org.hibernate.jpa.boot.spi.MappingFileDescriptor; + +/** +* @author Steve Ebersole +*/ +public class NonClassFileArchiveEntryHandler implements ArchiveEntryHandler { + private final ScanOptions scanOptions; + private final Callback callback; + + public static interface Callback { + public void locatedMappingFile(MappingFileDescriptor mappingFileDescriptor); + } + + public NonClassFileArchiveEntryHandler(ScanOptions scanOptions, Callback callback) { + this.scanOptions = scanOptions; + this.callback = callback; + } + + @Override + public void handleEntry(ArchiveEntry entry, ArchiveContext context) { + if ( acceptAsMappingFile( entry, context) ) { + notifyMatchedMappingFile( entry ); + } + } + + @SuppressWarnings("SimplifiableIfStatement") + private boolean acceptAsMappingFile(ArchiveEntry entry, ArchiveContext context) { + if ( entry.getName().endsWith( "hbm.xml" ) ) { + return scanOptions.canDetectHibernateMappingFiles(); + } + + // todo : should really do this case-insensitively + if ( entry.getName().endsWith( "META-INF/orm.xml" ) ) { + if ( context.getPersistenceUnitDescriptor().getMappingFileNames().contains( "META-INF/orm.xml" ) ) { + // if the user explicitly listed META-INF/orm.xml, only except the root one + // + // not sure why exactly, but this is what the old code does + return context.isRootUrl(); + } + return true; + } + + return context.getPersistenceUnitDescriptor().getMappingFileNames().contains( entry.getNameWithinArchive() ); + } + + protected final void notifyMatchedMappingFile(ArchiveEntry entry) { + callback.locatedMappingFile( toMappingFileDescriptor( entry ) ); + } + + protected MappingFileDescriptor toMappingFileDescriptor(ArchiveEntry entry) { + return new MappingFileDescriptorImpl( entry.getNameWithinArchive(), entry.getStreamAccess() ); + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/PackageInfoArchiveEntryHandler.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/PackageInfoArchiveEntryHandler.java new file mode 100644 index 0000000000..bacd9d3f1b --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/PackageInfoArchiveEntryHandler.java @@ -0,0 +1,71 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.scan.spi; + +import org.hibernate.jpa.boot.archive.spi.ArchiveContext; +import org.hibernate.jpa.boot.archive.spi.ArchiveEntry; +import org.hibernate.jpa.boot.archive.spi.ArchiveEntryHandler; +import org.hibernate.jpa.boot.internal.PackageDescriptorImpl; +import org.hibernate.jpa.boot.spi.PackageDescriptor; + +import static java.io.File.separatorChar; + +/** + * @author Steve Ebersole + */ +public class PackageInfoArchiveEntryHandler implements ArchiveEntryHandler { + @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) + private final ScanOptions scanOptions; + private final Callback callback; + + public static interface Callback { + public void locatedPackage(PackageDescriptor packageDescriptor); + } + + public PackageInfoArchiveEntryHandler(ScanOptions scanOptions, Callback callback) { + this.scanOptions = scanOptions; + this.callback = callback; + } + + @Override + public void handleEntry(ArchiveEntry entry, ArchiveContext context) { + if ( entry.getNameWithinArchive().equals( "package-info.class" ) ) { + // the old code skipped package-info in the root package/dir... + return; + } + notifyMatchedPackage( toPackageDescriptor( entry ) ); + } + + protected PackageDescriptor toPackageDescriptor(ArchiveEntry entry) { + final String packageInfoFilePath = entry.getNameWithinArchive(); + final String packageName = packageInfoFilePath.substring( 0, packageInfoFilePath.lastIndexOf( '/' ) ) + .replace( separatorChar, '.' ); + + return new PackageDescriptorImpl( packageName, entry.getStreamAccess() ); + } + + protected final void notifyMatchedPackage(PackageDescriptor packageDescriptor) { + callback.locatedPackage( packageDescriptor ); + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/PackageFilter.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/ScanOptions.java similarity index 63% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/PackageFilter.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/ScanOptions.java index d8ded44656..a5ec564a8d 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/PackageFilter.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/ScanOptions.java @@ -1,8 +1,10 @@ /* - * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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. + * distributed under license by Red Hat Inc. * * 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 @@ -19,20 +21,14 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.packaging.internal; - +package org.hibernate.jpa.boot.scan.spi; /** - * Filter on pachage element - * - * @author Emmanuel Bernard - * @see JavaElementFilter + * @author Steve Ebersole */ -public abstract class PackageFilter extends JavaElementFilter { - /** - * @see JavaElementFilter#JavaElementFilter(boolean, Class[]) - */ - protected PackageFilter(boolean retrieveStream, Class[] annotations) { - super( retrieveStream, annotations ); - } -} \ No newline at end of file +public interface ScanOptions { + public boolean canDetectUnlistedClassesInRoot(); + public boolean canDetectUnlistedClassesInNonRoot(); + + public boolean canDetectHibernateMappingFiles(); +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/ScanResult.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/ScanResult.java new file mode 100644 index 0000000000..13e0fa02f9 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/ScanResult.java @@ -0,0 +1,41 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.scan.spi; + +import java.util.Set; + +import org.hibernate.jpa.boot.spi.ClassDescriptor; +import org.hibernate.jpa.boot.spi.MappingFileDescriptor; +import org.hibernate.jpa.boot.spi.PackageDescriptor; + +/** + * Defines the result of scanning + * + * @author Steve Ebersole + */ +public interface ScanResult { + public Set getLocatedPackages(); + public Set getLocatedClasses(); + public Set getLocatedMappingFiles(); +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/Scanner.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/Scanner.java new file mode 100644 index 0000000000..d57b54cb76 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/scan/spi/Scanner.java @@ -0,0 +1,46 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.scan.spi; + +import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; + +/** + * Defines the contract for Hibernate to be able to scan for classes, packages and resources inside a + * persistence unit. + * + * @author Emmanuel Bernard + * @author Steve Ebersole + */ +public interface Scanner { + /** + * Perform the scanning against the described persistence unit using the defined options, and return the scan + * results. + * + * @param persistenceUnit THe description of the persistence unit. + * @param options The scan options + * + * @return The scan results. + */ + public ScanResult scan(PersistenceUnitDescriptor persistenceUnit, ScanOptions options); +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/ClassDescriptor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/ClassDescriptor.java new file mode 100644 index 0000000000..c04e57d9c0 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/ClassDescriptor.java @@ -0,0 +1,34 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.spi; + +/** + * Defines the result of scanning a persistence unit for classes. + * + * @author Steve Ebersole + */ +public interface ClassDescriptor { + public String getName(); + public InputStreamAccess getStreamAccess(); +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/InputStreamAccess.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/InputStreamAccess.java new file mode 100644 index 0000000000..cb51b1ada5 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/InputStreamAccess.java @@ -0,0 +1,52 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.spi; + +import java.io.InputStream; + +/** + * Contract for building InputStreams, especially in on-demand situations + * + * @author Steve Ebersole + */ +public interface InputStreamAccess { + /** + * Get the name of the resource backing the stream + * + * @return The backing resource name + */ + public String getStreamName(); + + /** + * Get access to the stream. Can be called multiple times, a different stream instance should be returned each time. + * + * @return The stream + */ + public InputStream accessInputStream(); + + /** + * @deprecated Needed until we can remove NamedInputStream + */ + public NamedInputStream asNamedInputStream(); +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/MappingFileDescriptor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/MappingFileDescriptor.java new file mode 100644 index 0000000000..5dfec7b9cb --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/MappingFileDescriptor.java @@ -0,0 +1,32 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.spi; + +/** + * @author Steve Ebersole + */ +public interface MappingFileDescriptor { + public String getName(); + public InputStreamAccess getStreamAccess(); +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/spi/NamedInputStream.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/NamedInputStream.java similarity index 80% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/spi/NamedInputStream.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/NamedInputStream.java index b0dfe020ac..fd6abed28f 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/spi/NamedInputStream.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/NamedInputStream.java @@ -21,34 +21,33 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.packaging.spi; +package org.hibernate.jpa.boot.spi; + import java.io.InputStream; /** + * Bundles together a stream and the name that was used to locate it. The name is often useful for logging. + * + * @deprecated Use {@link org.hibernate.jpa.boot.spi.InputStreamAccess} instead. + * * @author Emmanuel Bernard + * @author Steve Ebersole */ +@Deprecated public class NamedInputStream { + private final String name; + private final InputStream stream; + public NamedInputStream(String name, InputStream stream) { this.name = name; this.stream = stream; } - private String name; - private InputStream stream; - public InputStream getStream() { return stream; } - public void setStream(InputStream stream) { - this.stream = stream; - } - public String getName() { return name; } - - public void setName(String name) { - this.name = name; - } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/PackageDescriptor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/PackageDescriptor.java new file mode 100644 index 0000000000..101e516575 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/PackageDescriptor.java @@ -0,0 +1,34 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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.jpa.boot.spi; + +/** + * Defines the result of scanning a persistence unit for packages. + * + * @author Steve Ebersole + */ +public interface PackageDescriptor { + public String getName(); + public InputStreamAccess getStreamAccess(); +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/AbstractJarVisitor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/AbstractJarVisitor.java deleted file mode 100644 index b857f9d237..0000000000 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/AbstractJarVisitor.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * 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.jpa.packaging.internal; - -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javassist.bytecode.AnnotationsAttribute; -import javassist.bytecode.ClassFile; -import org.jboss.logging.Logger; - -import org.hibernate.jpa.internal.EntityManagerMessageLogger; - -/** - * Parse a JAR of any form (zip file, exploded directory, ...) - * apply a set of filters (File filter, Class filter, Package filter) - * and return the appropriate matching sets of elements - * - * @author Emmanuel Bernard - */ -public abstract class AbstractJarVisitor implements JarVisitor { - - //TODO shortcut when filters are null or empty - - private static final EntityManagerMessageLogger LOG = Logger.getMessageLogger(EntityManagerMessageLogger.class, - AbstractJarVisitor.class.getName()); - - protected String unqualifiedJarName; - protected URL jarUrl; - protected boolean done = false; - private List filters = new ArrayList(); - private Set fileFilters = new HashSet(); - private Set classFilters = new HashSet(); - private Set packageFilters = new HashSet(); - private Set[] entries; - - - - /** - * Build a jar visitor from its jar string path - */ - private AbstractJarVisitor(String jarPath) { - this.jarUrl = JarVisitorFactory.getURLFromPath( jarPath ); - unqualify(); - } - - protected AbstractJarVisitor(String fileName, Filter[] filters) { - this( fileName ); - initFilters( filters ); - } - - private void initFilters(Filter[] filters) { - for ( Filter filter : filters ) { - if ( filter instanceof FileFilter ) { - fileFilters.add( (FileFilter) filter ); - } - else if ( filter instanceof ClassFilter ) { - classFilters.add( (ClassFilter) filter ); - } - else if ( filter instanceof PackageFilter ) { - packageFilters.add( (PackageFilter) filter ); - } - else { - throw new AssertionError( "Unknown filter type: " + filter.getClass().getName() ); - } - this.filters.add( filter ); - } - int size = this.filters.size(); - this.entries = new Set[ size ]; - for ( int index = 0; index < size ; index++ ) { - this.entries[index] = new HashSet(); - } - } - - protected AbstractJarVisitor(URL url, Filter[] filters) { - this( url ); - initFilters( filters ); - } - - private AbstractJarVisitor(URL url) { - jarUrl = url; - unqualify(); - } - - protected void unqualify() { - //FIXME weak algorithm subject to AOOBE - String fileName = jarUrl.getFile(); - int exclamation = fileName.lastIndexOf( "!" ); - if (exclamation != -1) fileName = fileName.substring( 0, exclamation ); - int slash = fileName.lastIndexOf( "/" ); - if ( slash != -1 ) { - fileName = fileName.substring( - fileName.lastIndexOf( "/" ) + 1, - fileName.length() - ); - } - if ( fileName.length() > 4 && fileName.endsWith( "ar" ) && fileName.charAt( fileName.length() - 4 ) == '.' ) { - fileName = fileName.substring( 0, fileName.length() - 4 ); - } - unqualifiedJarName = fileName; - LOG.debugf("Searching mapped entities in jar/par: %s", jarUrl); - } - - /** - * Get the unqualified Jar name (ie wo path and wo extension) - */ - public String getUnqualifiedJarName() { - return unqualifiedJarName; - } - - public Filter[] getFilters() { - return filters.toArray( new Filter[ filters.size() ] ); - } - - /** - * Return the matching entries for each filter in the same order the filter where passed - * - * @return array of Set of JarVisitor.Entry - * @throws IOException if something went wrong - */ - public Set[] getMatchingEntries() throws IOException { - if ( !done ) { - //avoid url access and so on - if ( filters.size() > 0 ) doProcessElements(); - done = true; - } - return entries; - } - - protected abstract void doProcessElements() throws IOException; - - //TODO avoid 2 input stream when not needed - protected final void addElement(String entryName, InputStream is, InputStream secondIs) throws IOException { - int entryNameLength = entryName.length(); - if ( entryName.endsWith( "package-info.class" ) ) { - String name; - if ( entryNameLength == "package-info.class".length() ) { - name = ""; - } - else { - name = entryName.substring( 0, entryNameLength - ".package-info.class".length() ).replace( '/', '.' ); - } - executeJavaElementFilter( name, packageFilters, is, secondIs ); - } - else if ( entryName.endsWith( ".class" ) ) { - String name = entryName.substring( 0, entryNameLength - ".class".length() ).replace( '/', '.' ); - LOG.debugf("Filtering: %s", name); - executeJavaElementFilter( name, classFilters, is, secondIs ); - } - else { - String name = entryName; - boolean accepted = false; - for ( FileFilter filter : fileFilters ) { - if ( filter.accept( name ) ) { - accepted = true; - InputStream localIs; - if ( filter.getStream() ) { - localIs = secondIs; - } - else { - localIs = null; - secondIs.close(); - } - is.close(); - LOG.debugf("File Filter matched for %s", name); - Entry entry = new Entry( name, localIs ); - int index = this.filters.indexOf( filter ); - this.entries[index].add( entry ); - } - } - if (!accepted) { - //not accepted free resources - is.close(); - secondIs.close(); - } - } - } - - private void executeJavaElementFilter( - String name, Set filters, InputStream is, InputStream secondIs - ) throws IOException { - boolean accepted = false; - for ( JavaElementFilter filter : filters ) { - if ( filter.accept( name ) ) { - //FIXME cannot currently have a class filtered twice but matching once - // need to copy the is - boolean match = checkAnnotationMatching( is, filter ); - if ( match ) { - accepted = true; - InputStream localIs; - if ( filter.getStream() ) { - localIs = secondIs; - } - else { - localIs = null; - secondIs.close(); - } - LOG.debugf("Java element filter matched for %s", name); - Entry entry = new Entry( name, localIs ); - int index = this.filters.indexOf( filter ); - this.entries[index].add( entry ); - break; //we matched - } - } - } - if (!accepted) { - is.close(); - secondIs.close(); - } - } - - private boolean checkAnnotationMatching(InputStream is, JavaElementFilter filter) throws IOException { - if ( filter.getAnnotations().length == 0 ) { - is.close(); - return true; - } - DataInputStream dstream = new DataInputStream( is ); - ClassFile cf = null; - - try { - cf = new ClassFile( dstream ); - } - finally { - dstream.close(); - is.close(); - } - boolean match = false; - AnnotationsAttribute visible = (AnnotationsAttribute) cf.getAttribute( AnnotationsAttribute.visibleTag ); - if ( visible != null ) { - for ( Class annotation : filter.getAnnotations() ) { - match = visible.getAnnotation( annotation.getName() ) != null; - if ( match ) break; - } - } - return match; - } -} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/ExplodedJarVisitor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/ExplodedJarVisitor.java deleted file mode 100644 index cef0a7d9aa..0000000000 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/ExplodedJarVisitor.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * 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.jpa.packaging.internal; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.Enumeration; -import java.util.jar.JarFile; -import java.util.zip.ZipEntry; - -import org.jboss.logging.Logger; - -import org.hibernate.jpa.internal.EntityManagerMessageLogger; - - -/** - * @author Emmanuel Bernard - */ -public class ExplodedJarVisitor extends AbstractJarVisitor { - - private static final EntityManagerMessageLogger LOG = Logger.getMessageLogger(EntityManagerMessageLogger.class, - ExplodedJarVisitor.class.getName()); - - private String entry; - - public ExplodedJarVisitor(URL url, Filter[] filters, String entry) { - super( url, filters ); - this.entry = entry; - } - - public ExplodedJarVisitor(String fileName, Filter[] filters) { - super( fileName, filters ); - } - - @Override - protected void doProcessElements() throws IOException { - File jarFile; - try { - String filePart = jarUrl.getFile(); - if ( filePart != null && filePart.indexOf( ' ' ) != -1 ) { - //unescaped (from the container), keep as is - jarFile = new File( jarUrl.getFile() ); - } - else { - jarFile = new File( jarUrl.toURI().getSchemeSpecificPart() ); - } - } - catch (URISyntaxException e) { - LOG.malformedUrl(jarUrl, e); - return; - } - - if ( !jarFile.exists() ) { - LOG.explodedJarDoesNotExist(jarUrl); - return; - } - if ( !jarFile.isDirectory() ) { - LOG.explodedJarNotDirectory(jarUrl); - return; - } - File rootFile; - if (entry != null && entry.length() > 0 && ! "/".equals( entry ) ) { - rootFile = new File(jarFile, entry); - } - else { - rootFile = jarFile; - } - if ( rootFile.isDirectory() ) { - getClassNamesInTree( rootFile, null ); - } - else { - //assume zipped file - processZippedRoot(rootFile); - } - } - - //FIXME shameful copy of FileZippedJarVisitor.doProcess() - //TODO long term fix is to introduce a process interface (closure like) to addElements and then share the code - private void processZippedRoot(File rootFile) throws IOException { - JarFile jarFile = new JarFile(rootFile); - Enumeration entries = jarFile.entries(); - while ( entries.hasMoreElements() ) { - ZipEntry zipEntry = entries.nextElement(); - String name = zipEntry.getName(); - if ( !zipEntry.isDirectory() ) { - //build relative name - if ( name.startsWith( "/" ) ) name = name.substring( 1 ); - addElement( - name, - new BufferedInputStream( jarFile.getInputStream( zipEntry ) ), - new BufferedInputStream( jarFile.getInputStream( zipEntry ) ) - ); - } - } - } - - private void getClassNamesInTree(File jarFile, String header) throws IOException { - File[] files = jarFile.listFiles(); - header = header == null ? "" : header + "/"; - for ( File localFile : files ) { - if ( !localFile.isDirectory() ) { - String entryName = localFile.getName(); - addElement( - header + entryName, - new BufferedInputStream( new FileInputStream( localFile ) ), - new BufferedInputStream( new FileInputStream( localFile ) ) - ); - - } - else { - getClassNamesInTree( localFile, header + localFile.getName() ); - } - } - } -} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/FileZippedJarVisitor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/FileZippedJarVisitor.java deleted file mode 100644 index 8dd003ce59..0000000000 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/FileZippedJarVisitor.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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.jpa.packaging.internal; - -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.Enumeration; -import java.util.jar.JarFile; -import java.util.jar.JarInputStream; -import java.util.zip.ZipEntry; - -import org.jboss.logging.Logger; - -import org.hibernate.jpa.internal.EntityManagerMessageLogger; - -/** - * Work on a JAR that can be accessed through a File - * - * @author Emmanuel Bernard - */ -public class FileZippedJarVisitor extends AbstractJarVisitor { - - private static final EntityManagerMessageLogger LOG = Logger.getMessageLogger(EntityManagerMessageLogger.class, - FileZippedJarVisitor.class.getName()); - - private String entry; - - public FileZippedJarVisitor(String fileName, Filter[] filters) { - super( fileName, filters ); - } - - public FileZippedJarVisitor(URL url, Filter[] filters, String entry) { - super( url, filters ); - this.entry = entry; - } - - @Override - protected void doProcessElements() throws IOException { - JarFile jarFile; - try { - String filePart = jarUrl.getFile(); - if ( filePart != null && filePart.indexOf( ' ' ) != -1 ) { - //unescaped (from the container), keep as is - jarFile = new JarFile( jarUrl.getFile() ); - } - else { - jarFile = new JarFile( jarUrl.toURI().getSchemeSpecificPart() ); - } - } - catch (IOException ze) { - LOG.unableToFindFile(jarUrl, ze); - return; - } - catch (URISyntaxException e) { - LOG.malformedUrlWarning(jarUrl, e); - return; - } - - if ( entry != null && entry.length() == 1 ) entry = null; //no entry - if ( entry != null && entry.startsWith( "/" ) ) entry = entry.substring( 1 ); //remove '/' header - - Enumeration entries = jarFile.entries(); - while ( entries.hasMoreElements() ) { - ZipEntry zipEntry = entries.nextElement(); - String name = zipEntry.getName(); - if ( entry != null && ! name.startsWith( entry ) ) continue; //filter it out - if ( !zipEntry.isDirectory() ) { - if ( name.equals( entry ) ) { - //exact match, might be a nested jar entry (ie from jar:file:..../foo.ear!/bar.jar) - /* - * This algorithm assumes that the zipped file is only the URL root (including entry), not just any random entry - */ - InputStream is = null; - try { - is = new BufferedInputStream( jarFile.getInputStream( zipEntry ) ); - JarInputStream jis = new JarInputStream( is ); - ZipEntry subZipEntry = jis.getNextEntry(); - while (subZipEntry != null) { - if ( ! subZipEntry.isDirectory() ) { - //FIXME copy sucks - byte[] entryBytes = JarVisitorFactory.getBytesFromInputStream( jis ); - String subname = subZipEntry.getName(); - if ( subname.startsWith( "/" ) ) subname = subname.substring( 1 ); - addElement( - subname, - new ByteArrayInputStream(entryBytes), - new ByteArrayInputStream(entryBytes) - ); - } - subZipEntry = jis.getNextEntry(); - } - } - finally { - if ( is != null) is.close(); - } - } - else { - //build relative name - if (entry != null) name = name.substring( entry.length() ); - if ( name.startsWith( "/" ) ) name = name.substring( 1 ); - addElement( - name, - new BufferedInputStream( jarFile.getInputStream( zipEntry ) ), - new BufferedInputStream( jarFile.getInputStream( zipEntry ) ) - ); - } - } - } - } -} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/InputStreamZippedJarVisitor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/InputStreamZippedJarVisitor.java deleted file mode 100644 index 27163d29ae..0000000000 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/InputStreamZippedJarVisitor.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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.jpa.packaging.internal; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.net.URL; -import java.util.jar.JarEntry; -import java.util.jar.JarInputStream; -import java.util.zip.ZipEntry; - -import org.jboss.logging.Logger; - -import org.hibernate.jpa.internal.EntityManagerMessageLogger; - - -/** - * Work on a JAR that can only be accessed through a inputstream - * This is less efficient than the {@link FileZippedJarVisitor} - * - * @author Emmanuel Bernard - */ -public class InputStreamZippedJarVisitor extends AbstractJarVisitor { - - private static final EntityManagerMessageLogger LOG = Logger.getMessageLogger(EntityManagerMessageLogger.class, - InputStreamZippedJarVisitor.class.getName()); - - private String entry; - - public InputStreamZippedJarVisitor(URL url, Filter[] filters, String entry) { - super( url, filters ); - this.entry = entry; - } - - public InputStreamZippedJarVisitor(String fileName, Filter[] filters) { - super( fileName, filters ); - } - - @Override - protected void doProcessElements() throws IOException { - JarInputStream jis; - try { - jis = new JarInputStream( jarUrl.openStream() ); - } - catch (Exception ze) { - //really should catch IOException but Eclipse is buggy and raise NPE... - LOG.unableToFindFile(jarUrl, ze); - return; - } - if ( entry != null && entry.length() == 1 ) entry = null; //no entry - if ( entry != null && entry.startsWith( "/" ) ) entry = entry.substring( 1 ); //remove '/' header - - JarEntry jarEntry; - while ( ( jarEntry = jis.getNextJarEntry() ) != null ) { - String name = jarEntry.getName(); - if ( entry != null && ! name.startsWith( entry ) ) continue; //filter it out - if ( !jarEntry.isDirectory() ) { - if ( name.equals( entry ) ) { - //exact match, might be a nested jar entry (ie from jar:file:..../foo.ear!/bar.jar) - /* - * This algorithm assumes that the zipped file is only the URL root (including entry), not just any random entry - */ - JarInputStream subJis = null; - try { - subJis = new JarInputStream( jis ); - ZipEntry subZipEntry = jis.getNextEntry(); - while (subZipEntry != null) { - if ( ! subZipEntry.isDirectory() ) { - //FIXME copy sucks - byte[] entryBytes = JarVisitorFactory.getBytesFromInputStream( jis ); - String subname = subZipEntry.getName(); - if ( subname.startsWith( "/" ) ) subname = subname.substring( 1 ); - addElement( - subname, - new ByteArrayInputStream(entryBytes), - new ByteArrayInputStream(entryBytes) - ); - } - subZipEntry = jis.getNextJarEntry(); - } - } - finally { - if (subJis != null) subJis.close(); - } - } - else { - byte[] entryBytes = JarVisitorFactory.getBytesFromInputStream( jis ); - //build relative name - if (entry != null) name = name.substring( entry.length() ); - if ( name.startsWith( "/" ) ) name = name.substring( 1 ); - //this is bad cause we actually read everything instead of walking it lazily - addElement( - name, - new ByteArrayInputStream( entryBytes ), - new ByteArrayInputStream( entryBytes ) - ); - } - } - } - jis.close(); - } -} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JarProtocolVisitor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JarProtocolVisitor.java deleted file mode 100644 index c1bd24a169..0000000000 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JarProtocolVisitor.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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.jpa.packaging.internal; - -import java.io.IOException; -import java.net.URL; -import java.util.Set; - -import org.hibernate.annotations.common.AssertionFailure; - -/** - * @author Emmanuel Bernard - */ -public class JarProtocolVisitor implements JarVisitor { - private JarVisitor delegate; - private URL jarUrl; - private Filter[] filters; - - public JarProtocolVisitor(URL url, Filter[] filters, String entry) { - this.jarUrl = url; - this.filters = filters; - if (entry != null && entry.length() > 0) throw new IllegalArgumentException( "jar:jar: not supported: " + jarUrl ); - init(); - } - - private void init() { - String file = jarUrl.getFile(); - String entry; - int subEntryIndex = file.lastIndexOf( "!" ); - if (subEntryIndex == -1) throw new AssertionFailure("JAR URL does not contain '!/' :" + jarUrl); - if ( subEntryIndex + 1 >= file.length() ) { - entry = ""; - } - else { - entry = file.substring( subEntryIndex + 1 ); - } - URL fileUrl = JarVisitorFactory.getJarURLFromURLEntry( jarUrl, entry ); - delegate = JarVisitorFactory.getVisitor( fileUrl, filters, entry ); - - } - - public String getUnqualifiedJarName() { - return delegate.getUnqualifiedJarName(); - } - - public Filter[] getFilters() { - return delegate.getFilters(); - } - - public Set[] getMatchingEntries() throws IOException { - return delegate.getMatchingEntries(); - } - -} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JavaElementFilter.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JavaElementFilter.java deleted file mode 100644 index ab7e42fc42..0000000000 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JavaElementFilter.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.jpa.packaging.internal; - - -/** - * Filter a Java element (class or package per fully qualified name and annotation existence) - * At least 1 annotation has to annotate the element and the accept method must match - * If none annotations are passed, only the accept method must pass. - * - * @author Emmanuel Bernard - */ -public abstract class JavaElementFilter extends Filter { - private Class[] annotations; - - /** - * @param retrieveStream Give back an open stream to the matching element or not - * @param annotations Array of annotations that must be present to match (1 of them should annotate the element - */ - protected JavaElementFilter(boolean retrieveStream, Class[] annotations) { - super( retrieveStream ); - this.annotations = annotations == null ? new Class[]{} : annotations; - } - - public Class[] getAnnotations() { - return annotations; - } - - /** - * Return true if the fully qualified name match - */ - public abstract boolean accept(String javaElementName); -} \ No newline at end of file diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/NativeScanner.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/NativeScanner.java deleted file mode 100644 index 6a568a981d..0000000000 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/NativeScanner.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * 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.jpa.packaging.internal; - -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.Converter; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.MappedSuperclass; - -import org.hibernate.AssertionFailure; -import org.hibernate.internal.util.ReflectHelper; -import org.hibernate.jpa.packaging.spi.NamedInputStream; -import org.hibernate.jpa.packaging.spi.Scanner; - -/** - * @author Emmanuel Bernard - */ -public class NativeScanner implements Scanner { - - private static final String META_INF_ORM_XML = "META-INF/orm.xml"; - - private Map visitors = new HashMap(); - 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 getPackagesInJar(URL jarToScan, Set> annotationsToLookFor) { - if ( annotationsToLookFor.size() > 0 ) { - throw new AssertionFailure( "Improper use of NativeScanner: must not filter packages" ); - } - - JarVisitor jarVisitor = getVisitor( jarToScan ); - final Set packageEntries; - try { - packageEntries = ( Set ) jarVisitor.getMatchingEntries()[PACKAGE_FILTER_INDEX]; - } - catch ( IOException e ) { - throw new RuntimeException( "Error while reading " + jarToScan.toString(), e ); - } - Set packages = new HashSet( 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> getClassesInJar(URL jarToScan, Set> 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 classesEntry; - try { - classesEntry = ( Set ) jarVisitor.getMatchingEntries()[CLASS_FILTER_INDEX]; - } - catch ( IOException e ) { - throw new RuntimeException( "Error while reading " + jarToScan.toString(), e ); - } - Set> classes = new HashSet>( 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> annotationsToLookFor) { - return annotationsToLookFor.size() != 4 - || !annotationsToLookFor.contains( Entity.class ) - || !annotationsToLookFor.contains( MappedSuperclass.class ) - || !annotationsToLookFor.contains( Embeddable.class ) - || !annotationsToLookFor.contains( Converter.class ); - } - - /** - * support for patterns is primitive: - * - **\/*.hbm.xml - * Other patterns will not be found - */ - public Set getFilesInJar(URL jarToScan, Set 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 endWiths = new HashSet(); - Set exacts = new HashSet(); - 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 fileEntries; - try { - fileEntries = ( Set ) jarVisitor.getMatchingEntries()[FILE_FILTER_INDEX]; - } - catch ( IOException e ) { - throw new RuntimeException( "Error while reading " + jarToScan.toString(), e ); - } - Set files = new HashSet( fileEntries.size() ); - Set leftOver = new HashSet( 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 getFilesInClasspath(Set 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; - } -} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/spi/Scanner.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/spi/Scanner.java deleted file mode 100644 index 241d233d61..0000000000 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/spi/Scanner.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2012, Red Hat Inc. 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 Inc. - * - * 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.jpa.packaging.spi; -import java.lang.annotation.Annotation; -import java.net.URL; -import java.util.Set; - -/** - * @author Emmanuel Bernard - */ -public interface Scanner { - /** - * return all packages in the jar matching one of these annotations - * if annotationsToLookFor is empty, return all packages - */ - Set getPackagesInJar(URL jartoScan, Set> annotationsToLookFor); - - /** - * return all classes in the jar matching one of these annotations - * if annotationsToLookFor is empty, return all classes - */ - Set> getClassesInJar(URL jartoScan, Set> annotationsToLookFor); - - /** - * return all files in the jar matching one of these file names - * if filePatterns is empty, return all files - * eg **\/*.hbm.xml, META-INF/orm.xml - */ - Set getFilesInJar(URL jartoScan, Set filePatterns); - - - /** - * Return all files in the classpath (ie PU visibility) matching one of these file names - * if filePatterns is empty, return all files - * the use case is really exact file name. - * - * NOT USED by HEM at the moment. We use exact file search via getResourceAsStream for now. - */ - Set getFilesInClasspath(Set filePatterns); - - /** - * return the unqualified JAR name ie customer-model.jar or store.war - */ - String getUnqualifiedJarName(URL jarUrl); - -} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/TestHelper.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/TestHelper.java new file mode 100644 index 0000000000..90ef4d9ce8 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/TestHelper.java @@ -0,0 +1,67 @@ +package org.hibernate.jpa.test; + +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. 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 Inc. + * + * 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 + */ + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +import static java.io.File.separatorChar; + +/** + * @author Steve Ebersole + */ +public class TestHelper { + private static URL RESOLVED_TEST_ROOT_URL; + + public static URL determineTestRootUrl() { + if ( RESOLVED_TEST_ROOT_URL == null ) { + RESOLVED_TEST_ROOT_URL = resolveRootUrl( TestHelper.class ); + } + return RESOLVED_TEST_ROOT_URL; + } + + public static URL resolveRootUrl(Class knownClass) { + final String knownClassFileName = '/' + knownClass.getName().replace( '.', separatorChar ) + ".class"; + final URL knownClassFileUrl = TestHelper.class.getResource( knownClassFileName ); + final String knownClassFileUrlString = knownClassFileUrl.toExternalForm(); + + // to start, strip off the class file name + String rootUrlString = knownClassFileUrlString.substring( 0, knownClassFileUrlString.lastIndexOf( separatorChar ) ); + + // then strip off each package dir + final String packageName = knownClass.getPackage().getName(); + for ( String packageNamePart : packageName.split( "\\." ) ) { + rootUrlString = rootUrlString.substring( 0, rootUrlString.lastIndexOf( separatorChar ) ); + } + + try { + return new URL( rootUrlString ); + } + catch (MalformedURLException e) { + throw new RuntimeException( "Could not convert class base url as string to URL ref", e ); + } + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/CustomScanner.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/CustomScanner.java index c1ec0fdf84..71a6d5e72b 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/CustomScanner.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/CustomScanner.java @@ -1,19 +1,17 @@ package org.hibernate.jpa.test.packaging; -import java.lang.annotation.Annotation; -import java.net.URL; -import java.util.Set; - -import org.hibernate.jpa.packaging.internal.NativeScanner; -import org.hibernate.jpa.packaging.spi.NamedInputStream; -import org.hibernate.jpa.packaging.spi.Scanner; +import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; +import org.hibernate.jpa.boot.scan.internal.StandardScanner; +import org.hibernate.jpa.boot.scan.spi.ScanOptions; +import org.hibernate.jpa.boot.scan.spi.ScanResult; +import org.hibernate.jpa.boot.scan.spi.Scanner; /** * @author Emmanuel Bernard */ public class CustomScanner implements Scanner { public static boolean isUsed = false; - private Scanner scanner = new NativeScanner(); + private Scanner delegate = new StandardScanner(); public static boolean isUsed() { return isUsed; @@ -23,28 +21,9 @@ public class CustomScanner implements Scanner { isUsed = false; } - public Set getPackagesInJar(URL jartoScan, Set> annotationsToLookFor) { + @Override + public ScanResult scan(PersistenceUnitDescriptor persistenceUnit, ScanOptions options) { isUsed = true; - return scanner.getPackagesInJar( jartoScan, annotationsToLookFor ); - } - - public Set> getClassesInJar(URL jartoScan, Set> annotationsToLookFor) { - isUsed = true; - return scanner.getClassesInJar( jartoScan, annotationsToLookFor ); - } - - public Set getFilesInJar(URL jartoScan, Set filePatterns) { - isUsed = true; - return scanner.getFilesInJar( jartoScan, filePatterns ); - } - - public Set getFilesInClasspath(Set filePatterns) { - isUsed = true; - return scanner.getFilesInClasspath( filePatterns ); - } - - public String getUnqualifiedJarName(URL jarUrl) { - isUsed = true; - return scanner.getUnqualifiedJarName( jarUrl ); + return delegate.scan( persistenceUnit, options ); } } diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/JarVisitorTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/JarVisitorTest.java index b1fb148cb1..e4ed5d1ef4 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/JarVisitorTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/JarVisitorTest.java @@ -23,12 +23,6 @@ */ package org.hibernate.jpa.test.packaging; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; @@ -38,29 +32,32 @@ import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; import java.net.URLStreamHandlerFactory; -import java.util.Set; - -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.MappedSuperclass; import org.hibernate.dialect.H2Dialect; -import org.hibernate.jpa.packaging.internal.ClassFilter; -import org.hibernate.jpa.packaging.internal.Entry; -import org.hibernate.jpa.packaging.internal.ExplodedJarVisitor; -import org.hibernate.jpa.packaging.internal.FileFilter; -import org.hibernate.jpa.packaging.internal.FileZippedJarVisitor; -import org.hibernate.jpa.packaging.internal.Filter; -import org.hibernate.jpa.packaging.internal.InputStreamZippedJarVisitor; -import org.hibernate.jpa.packaging.internal.JarProtocolVisitor; -import org.hibernate.jpa.packaging.internal.JarVisitor; -import org.hibernate.jpa.packaging.internal.JarVisitorFactory; -import org.hibernate.jpa.packaging.internal.PackageFilter; +import org.hibernate.jpa.boot.archive.internal.ArchiveHelper; +import org.hibernate.jpa.boot.archive.internal.ExplodedArchiveDescriptor; +import org.hibernate.jpa.boot.archive.internal.JarFileBasedArchiveDescriptor; +import org.hibernate.jpa.boot.archive.internal.JarInputStreamBasedArchiveDescriptor; +import org.hibernate.jpa.boot.archive.internal.JarProtocolArchiveDescriptor; +import org.hibernate.jpa.boot.archive.internal.StandardArchiveDescriptorFactory; +import org.hibernate.jpa.boot.archive.spi.ArchiveDescriptor; +import org.hibernate.jpa.boot.internal.ClassDescriptorImpl; +import org.hibernate.jpa.boot.scan.internal.StandardScanOptions; +import org.hibernate.jpa.boot.scan.spi.AbstractScannerImpl; +import org.hibernate.jpa.boot.spi.MappingFileDescriptor; +import org.hibernate.jpa.test.PersistenceUnitDescriptorAdapter; import org.hibernate.jpa.test.pack.defaultpar.Version; import org.hibernate.jpa.test.pack.explodedpar.Carpet; + +import org.junit.Test; + import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.TestForIssue; -import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; /** * @author Emmanuel Bernard @@ -72,7 +69,7 @@ import org.junit.Test; public class JarVisitorTest extends PackagingTestCase { @Test public void testHttp() throws Exception { - URL url = JarVisitorFactory.getJarURLFromURLEntry( + URL url = ArchiveHelper.getJarURLFromURLEntry( new URL( "jar:http://www.ibiblio.org/maven/hibernate/jars/hibernate-annotations-3.0beta1.jar!/META-INF/persistence.xml" ), @@ -86,10 +83,18 @@ public class JarVisitorTest extends PackagingTestCase { //fail silently return; } - JarVisitor visitor = JarVisitorFactory.getVisitor( url, getFilters() ); - assertEquals( 0, visitor.getMatchingEntries()[0].size() ); - assertEquals( 0, visitor.getMatchingEntries()[1].size() ); - assertEquals( 0, visitor.getMatchingEntries()[2].size() ); + ArchiveDescriptor archiveDescriptor = StandardArchiveDescriptorFactory.INSTANCE.buildArchiveDescriptor( url ); + AbstractScannerImpl.ResultCollector resultCollector = new AbstractScannerImpl.ResultCollector( new StandardScanOptions() ); + archiveDescriptor.visitArchive( + new AbstractScannerImpl.ArchiveContextImpl( + new PersistenceUnitDescriptorAdapter(), + true, + resultCollector + ) + ); + assertEquals( 0, resultCollector.getClassDescriptorSet().size() ); + assertEquals( 0, resultCollector.getPackageDescriptorSet().size() ); + assertEquals( 0, resultCollector.getMappingFileSet().size() ); } @Test @@ -97,20 +102,40 @@ public class JarVisitorTest extends PackagingTestCase { File defaultPar = buildDefaultPar(); addPackageToClasspath( defaultPar ); - Filter[] filters = getFilters(); - JarVisitor jarVisitor = new InputStreamZippedJarVisitor( defaultPar.toURL(), filters, "" ); - assertEquals( "defaultpar", jarVisitor.getUnqualifiedJarName() ); - Set entries = jarVisitor.getMatchingEntries()[1]; - assertEquals( 3, entries.size() ); - Entry entry = new Entry( org.hibernate.jpa.test.pack.defaultpar.ApplicationServer.class.getName(), null ); - assertTrue( entries.contains( entry ) ); - entry = new Entry( Version.class.getName(), null ); - assertTrue( entries.contains( entry ) ); - assertNull( ( ( Entry ) entries.iterator().next() ).getInputStream() ); - assertEquals( 2, jarVisitor.getMatchingEntries()[2].size() ); - for ( Entry localEntry : ( Set ) jarVisitor.getMatchingEntries()[2] ) { - assertNotNull( localEntry.getInputStream() ); - localEntry.getInputStream().close(); + ArchiveDescriptor archiveDescriptor = new JarInputStreamBasedArchiveDescriptor( + StandardArchiveDescriptorFactory.INSTANCE, + defaultPar.toURL(), + "" + ); + + AbstractScannerImpl.ResultCollector resultCollector = new AbstractScannerImpl.ResultCollector( new StandardScanOptions() ); + archiveDescriptor.visitArchive( + new AbstractScannerImpl.ArchiveContextImpl( + new PersistenceUnitDescriptorAdapter(), + true, + resultCollector + ) + ); + + validateResults( resultCollector, org.hibernate.jpa.test.pack.defaultpar.ApplicationServer.class, Version.class ); + } + + private void validateResults(AbstractScannerImpl.ResultCollector resultCollector, Class... expectedClasses) throws IOException { + assertEquals( 3, resultCollector.getClassDescriptorSet().size() ); + for ( Class expectedClass : expectedClasses ) { + assertTrue( + resultCollector.getClassDescriptorSet().contains( + new ClassDescriptorImpl( expectedClass.getName(), null ) + ) + ); + } + + assertEquals( 2, resultCollector.getMappingFileSet().size() ); + for ( MappingFileDescriptor mappingFileDescriptor : resultCollector.getMappingFileSet() ) { + assertNotNull( mappingFileDescriptor.getStreamAccess() ); + final InputStream stream = mappingFileDescriptor.getStreamAccess().accessInputStream(); + assertNotNull( stream ); + stream.close(); } } @@ -122,41 +147,39 @@ public class JarVisitorTest extends PackagingTestCase { addPackageToClasspath( nestedEar ); String jarFileName = nestedEar.toURL().toExternalForm() + "!/defaultpar.par"; - Filter[] filters = getFilters(); - JarVisitor jarVisitor = new JarProtocolVisitor( new URL( jarFileName ), filters, "" ); - //TODO should we fix the name here to reach defaultpar rather than nestedjar ?? - //assertEquals( "defaultpar", jarVisitor.getUnqualifiedJarName() ); - Set entries = jarVisitor.getMatchingEntries()[1]; - assertEquals( 3, entries.size() ); - Entry entry = new Entry( org.hibernate.jpa.test.pack.defaultpar.ApplicationServer.class.getName(), null ); - assertTrue( entries.contains( entry ) ); - entry = new Entry( Version.class.getName(), null ); - assertTrue( entries.contains( entry ) ); - assertNull( ( ( Entry ) entries.iterator().next() ).getInputStream() ); - assertEquals( 2, jarVisitor.getMatchingEntries()[2].size() ); - for ( Entry localEntry : ( Set ) jarVisitor.getMatchingEntries()[2] ) { - assertNotNull( localEntry.getInputStream() ); - localEntry.getInputStream().close(); - } + + JarProtocolArchiveDescriptor archiveDescriptor = new JarProtocolArchiveDescriptor( + StandardArchiveDescriptorFactory.INSTANCE, + new URL( jarFileName ), + "" + ); + AbstractScannerImpl.ResultCollector resultCollector = new AbstractScannerImpl.ResultCollector( new StandardScanOptions() ); + archiveDescriptor.visitArchive( + new AbstractScannerImpl.ArchiveContextImpl( + new PersistenceUnitDescriptorAdapter(), + true, + resultCollector + ) + ); + + validateResults( resultCollector, org.hibernate.jpa.test.pack.defaultpar.ApplicationServer.class, Version.class ); jarFileName = nestedEarDir.toURL().toExternalForm() + "!/defaultpar.par"; - //JarVisitor jarVisitor = new ZippedJarVisitor( jarFileName, true, true ); - filters = getFilters(); - jarVisitor = new JarProtocolVisitor( new URL( jarFileName ), filters, "" ); - //TODO should we fix the name here to reach defaultpar rather than nestedjar ?? - //assertEquals( "defaultpar", jarVisitor.getUnqualifiedJarName() ); - entries = jarVisitor.getMatchingEntries()[1]; - assertEquals( 3, entries.size() ); - entry = new Entry( org.hibernate.jpa.test.pack.defaultpar.ApplicationServer.class.getName(), null ); - assertTrue( entries.contains( entry ) ); - entry = new Entry( Version.class.getName(), null ); - assertTrue( entries.contains( entry ) ); - assertNull( ( ( Entry ) entries.iterator().next() ).getInputStream() ); - assertEquals( 2, jarVisitor.getMatchingEntries()[2].size() ); - for ( Entry localEntry : ( Set ) jarVisitor.getMatchingEntries()[2] ) { - assertNotNull( localEntry.getInputStream() ); - localEntry.getInputStream().close(); - } + archiveDescriptor = new JarProtocolArchiveDescriptor( + StandardArchiveDescriptorFactory.INSTANCE, + new URL( jarFileName ), + "" + ); + resultCollector = new AbstractScannerImpl.ResultCollector( new StandardScanOptions() ); + archiveDescriptor.visitArchive( + new AbstractScannerImpl.ArchiveContextImpl( + new PersistenceUnitDescriptorAdapter(), + true, + resultCollector + ) + ); + + validateResults( resultCollector, org.hibernate.jpa.test.pack.defaultpar.ApplicationServer.class, Version.class ); } @Test @@ -165,21 +188,26 @@ public class JarVisitorTest extends PackagingTestCase { addPackageToClasspath( war ); String jarFileName = war.toURL().toExternalForm() + "!/WEB-INF/classes"; - Filter[] filters = getFilters(); - JarVisitor jarVisitor = new JarProtocolVisitor( new URL( jarFileName ), filters, "" ); - assertEquals( "war", jarVisitor.getUnqualifiedJarName() ); - Set entries = jarVisitor.getMatchingEntries()[1]; - assertEquals( 3, entries.size() ); - Entry entry = new Entry( org.hibernate.jpa.test.pack.war.ApplicationServer.class.getName(), null ); - assertTrue( entries.contains( entry ) ); - entry = new Entry( org.hibernate.jpa.test.pack.war.Version.class.getName(), null ); - assertTrue( entries.contains( entry ) ); - assertNull( ( ( Entry ) entries.iterator().next() ).getInputStream() ); - assertEquals( 2, jarVisitor.getMatchingEntries()[2].size() ); - for ( Entry localEntry : ( Set ) jarVisitor.getMatchingEntries()[2] ) { - assertNotNull( localEntry.getInputStream() ); - localEntry.getInputStream().close(); - } + JarProtocolArchiveDescriptor archiveDescriptor = new JarProtocolArchiveDescriptor( + StandardArchiveDescriptorFactory.INSTANCE, + new URL( jarFileName ), + "" + ); + + AbstractScannerImpl.ResultCollector resultCollector = new AbstractScannerImpl.ResultCollector( new StandardScanOptions() ); + archiveDescriptor.visitArchive( + new AbstractScannerImpl.ArchiveContextImpl( + new PersistenceUnitDescriptorAdapter(), + true, + resultCollector + ) + ); + + validateResults( + resultCollector, + org.hibernate.jpa.test.pack.war.ApplicationServer.class, + org.hibernate.jpa.test.pack.war.Version.class + ); } @Test @@ -187,21 +215,21 @@ public class JarVisitorTest extends PackagingTestCase { File defaultPar = buildDefaultPar(); addPackageToClasspath( defaultPar ); - Filter[] filters = getFilters(); - JarVisitor jarVisitor = new FileZippedJarVisitor( defaultPar.toURL(), filters, "" ); - assertEquals( "defaultpar", jarVisitor.getUnqualifiedJarName() ); - Set entries = jarVisitor.getMatchingEntries()[1]; - assertEquals( 3, entries.size() ); - Entry entry = new Entry( org.hibernate.jpa.test.pack.defaultpar.ApplicationServer.class.getName(), null ); - assertTrue( entries.contains( entry ) ); - entry = new Entry( Version.class.getName(), null ); - assertTrue( entries.contains( entry ) ); - assertNull( ( ( Entry ) entries.iterator().next() ).getInputStream() ); - assertEquals( 2, jarVisitor.getMatchingEntries()[2].size() ); - for ( Entry localEntry : ( Set ) jarVisitor.getMatchingEntries()[2] ) { - assertNotNull( localEntry.getInputStream() ); - localEntry.getInputStream().close(); - } + JarFileBasedArchiveDescriptor archiveDescriptor = new JarFileBasedArchiveDescriptor( + StandardArchiveDescriptorFactory.INSTANCE, + defaultPar.toURL(), + "" + ); + AbstractScannerImpl.ResultCollector resultCollector = new AbstractScannerImpl.ResultCollector( new StandardScanOptions() ); + archiveDescriptor.visitArchive( + new AbstractScannerImpl.ArchiveContextImpl( + new PersistenceUnitDescriptorAdapter(), + true, + resultCollector + ) + ); + + validateResults( resultCollector, org.hibernate.jpa.test.pack.defaultpar.ApplicationServer.class, Version.class ); } @Test @@ -209,32 +237,50 @@ public class JarVisitorTest extends PackagingTestCase { File explodedPar = buildExplodedPar(); addPackageToClasspath( explodedPar ); - Filter[] filters = getFilters(); String dirPath = explodedPar.toURL().toExternalForm(); // TODO - shouldn't ExplodedJarVisitor take care of a trailing slash? if ( dirPath.endsWith( "/" ) ) { dirPath = dirPath.substring( 0, dirPath.length() - 1 ); } - JarVisitor jarVisitor = new ExplodedJarVisitor( dirPath, filters ); - assertEquals( "explodedpar", jarVisitor.getUnqualifiedJarName() ); - Set[] entries = jarVisitor.getMatchingEntries(); - assertEquals( 1, entries[1].size() ); - assertEquals( 1, entries[0].size() ); - assertEquals( 1, entries[2].size() ); - Entry entry = new Entry( Carpet.class.getName(), null ); - assertTrue( entries[1].contains( entry ) ); - for ( Entry localEntry : ( Set ) jarVisitor.getMatchingEntries()[2] ) { - assertNotNull( localEntry.getInputStream() ); - localEntry.getInputStream().close(); + ExplodedArchiveDescriptor archiveDescriptor = new ExplodedArchiveDescriptor( + StandardArchiveDescriptorFactory.INSTANCE, + ArchiveHelper.getURLFromPath( dirPath ), + "" + ); + AbstractScannerImpl.ResultCollector resultCollector = new AbstractScannerImpl.ResultCollector( new StandardScanOptions() ); + archiveDescriptor.visitArchive( + new AbstractScannerImpl.ArchiveContextImpl( + new PersistenceUnitDescriptorAdapter(), + true, + resultCollector + ) + ); + + assertEquals( 1, resultCollector.getClassDescriptorSet().size() ); + assertEquals( 1, resultCollector.getPackageDescriptorSet().size() ); + assertEquals( 1, resultCollector.getMappingFileSet().size() ); + + assertTrue( + resultCollector.getClassDescriptorSet().contains( + new ClassDescriptorImpl( Carpet.class.getName(), null ) + ) + ); + + for ( MappingFileDescriptor mappingFileDescriptor : resultCollector.getMappingFileSet() ) { + assertNotNull( mappingFileDescriptor.getStreamAccess() ); + final InputStream stream = mappingFileDescriptor.getStreamAccess().accessInputStream(); + assertNotNull( stream ); + stream.close(); } } @Test @TestForIssue(jiraKey = "HHH-6806") - public void testJarVisitorFactory() throws Exception{ - - addPackageToClasspath( buildExplodedPar(), buildDefaultPar() ); + public void testJarVisitorFactory() throws Exception { + final File explodedPar = buildExplodedPar(); + final File defaultPar = buildDefaultPar(); + addPackageToClasspath( explodedPar, defaultPar ); //setting URL to accept vfs based protocol URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() { @@ -250,21 +296,21 @@ public class JarVisitorTest extends PackagingTestCase { } }); - URL jarUrl = new URL ("file:./target/packages/defaultpar.par"); - JarVisitor jarVisitor = JarVisitorFactory.getVisitor(jarUrl, getFilters(), null); - assertEquals(FileZippedJarVisitor.class.getName(), jarVisitor.getClass().getName()); - - jarUrl = new URL ("file:./target/packages/explodedpar"); - jarVisitor = JarVisitorFactory.getVisitor(jarUrl, getFilters(), null); - assertEquals(ExplodedJarVisitor.class.getName(), jarVisitor.getClass().getName()); - - jarUrl = new URL ("vfszip:./target/packages/defaultpar.par"); - jarVisitor = JarVisitorFactory.getVisitor(jarUrl, getFilters(), null); - assertEquals(FileZippedJarVisitor.class.getName(), jarVisitor.getClass().getName()); - - jarUrl = new URL ("vfsfile:./target/packages/explodedpar"); - jarVisitor = JarVisitorFactory.getVisitor(jarUrl, getFilters(), null); - assertEquals(ExplodedJarVisitor.class.getName(), jarVisitor.getClass().getName()); + URL jarUrl = defaultPar.toURL(); + ArchiveDescriptor descriptor = StandardArchiveDescriptorFactory.INSTANCE.buildArchiveDescriptor( jarUrl ); + assertEquals( JarFileBasedArchiveDescriptor.class.getName(), descriptor.getClass().getName() ); + + jarUrl = explodedPar.toURL(); + descriptor = StandardArchiveDescriptorFactory.INSTANCE.buildArchiveDescriptor( jarUrl ); + assertEquals( ExplodedArchiveDescriptor.class.getName(), descriptor.getClass().getName() ); + + jarUrl = new URL( defaultPar.toURL().toExternalForm().replace( "file:", "vfszip:" ) ); + descriptor = StandardArchiveDescriptorFactory.INSTANCE.buildArchiveDescriptor( jarUrl ); + assertEquals( JarFileBasedArchiveDescriptor.class.getName(), descriptor.getClass().getName()); + + jarUrl = new URL( explodedPar.toURL().toExternalForm().replace( "file:", "vfsfile:" ) ); + descriptor = StandardArchiveDescriptorFactory.INSTANCE.buildArchiveDescriptor( jarUrl ); + assertEquals( ExplodedArchiveDescriptor.class.getName(), descriptor.getClass().getName() ); } @Test @@ -315,36 +361,29 @@ public class JarVisitorTest extends PackagingTestCase { @Test @TestForIssue(jiraKey = "HHH-7835") - public void testGetBytesFromInputStream() { - try { - File file = buildLargeJar(); + public void testGetBytesFromInputStream() throws Exception { + File file = buildLargeJar(); - long start = System.currentTimeMillis(); - InputStream stream = new BufferedInputStream( - new FileInputStream( file ) ); - int oldLength = getBytesFromInputStream( stream ).length; - stream.close(); - long oldTime = System.currentTimeMillis() - start; + long start = System.currentTimeMillis(); + InputStream stream = new BufferedInputStream( + new FileInputStream( file ) ); + int oldLength = getBytesFromInputStream( stream ).length; + stream.close(); + long oldTime = System.currentTimeMillis() - start; - start = System.currentTimeMillis(); - stream = new BufferedInputStream( new FileInputStream( file ) ); - int newLength = JarVisitorFactory.getBytesFromInputStream( - stream ).length; - stream.close(); - long newTime = System.currentTimeMillis() - start; + start = System.currentTimeMillis(); + stream = new BufferedInputStream( new FileInputStream( file ) ); + int newLength = ArchiveHelper.getBytesFromInputStream( stream ).length; + stream.close(); + long newTime = System.currentTimeMillis() - start; - assertEquals( oldLength, newLength ); - assertTrue( oldTime > newTime ); - } - catch ( Exception e ) { - fail( e.getMessage() ); - } + assertEquals( oldLength, newLength ); + assertTrue( oldTime > newTime ); } // This is the old getBytesFromInputStream from JarVisitorFactory before // it was changed by HHH-7835. Use it as a regression test. - private byte[] getBytesFromInputStream( - InputStream inputStream) throws IOException { + private byte[] getBytesFromInputStream(InputStream inputStream) throws IOException { int size; byte[] entryBytes = new byte[0]; @@ -363,46 +402,16 @@ public class JarVisitorTest extends PackagingTestCase { @Test @TestForIssue(jiraKey = "HHH-7835") - public void testGetBytesFromZeroInputStream() { - try { - // Ensure that JarVisitorFactory#getBytesFromInputStream - // can handle 0 length streams gracefully. - InputStream emptyStream = new BufferedInputStream( - new FileInputStream( new File( - "src/test/resources/org/hibernate/jpa/test/packaging/empty.txt" ) ) ); - int length = JarVisitorFactory.getBytesFromInputStream( - emptyStream ).length; - assertEquals( length, 0 ); - emptyStream.close(); + public void testGetBytesFromZeroInputStream() throws Exception { + // Ensure that JarVisitorFactory#getBytesFromInputStream + // can handle 0 length streams gracefully. + URL emptyTxtUrl = getClass().getResource( "/org/hibernate/jpa/test/packaging/empty.txt" ); + if ( emptyTxtUrl == null ) { + throw new RuntimeException( "Bah!" ); } - catch ( Exception e ) { - fail( e.getMessage() ); - } - } - - private Filter[] getFilters() { - return new Filter[] { - new PackageFilter( false, null ) { - public boolean accept(String javaElementName) { - return true; - } - }, - new ClassFilter( - false, new Class[] { - Entity.class, - MappedSuperclass.class, - Embeddable.class - } - ) { - public boolean accept(String javaElementName) { - return true; - } - }, - new FileFilter( true ) { - public boolean accept(String javaElementName) { - return javaElementName.endsWith( "hbm.xml" ) || javaElementName.endsWith( "META-INF/orm.xml" ); - } - } - }; + InputStream emptyStream = new BufferedInputStream( emptyTxtUrl.openStream() ); + int length = ArchiveHelper.getBytesFromInputStream( emptyStream ).length; + assertEquals( length, 0 ); + emptyStream.close(); } } diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/PackagingTestCase.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/PackagingTestCase.java index 30582808f6..af86c8aeae 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/PackagingTestCase.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/PackagingTestCase.java @@ -45,6 +45,7 @@ import org.hibernate.jpa.test.Cat; import org.hibernate.jpa.test.Distributor; import org.hibernate.jpa.test.Item; import org.hibernate.jpa.test.Kitten; +import org.hibernate.jpa.test.TestHelper; import org.hibernate.jpa.test.pack.cfgxmlpar.Morito; import org.hibernate.jpa.test.pack.defaultpar.ApplicationServer; import org.hibernate.jpa.test.pack.defaultpar.IncrementListener; @@ -204,6 +205,10 @@ public abstract class PackagingTestCase extends BaseCoreFunctionalTestCase { } protected File buildExplicitPar() { + // explicitpar/persistence.xml references externaljar.jar so build that from here. + // this is the reason for tests failing after clean at least on my (Steve) local system + buildExternalJar(); + String fileName = "explicitpar.par"; JavaArchive archive = ShrinkWrap.create( JavaArchive.class, fileName ); archive.addClasses( @@ -342,8 +347,10 @@ public abstract class PackagingTestCase extends BaseCoreFunctionalTestCase { // Build a large jar by adding a lorem ipsum file repeatedly. for ( int i = 0; i < 100; i++ ) { ArchivePath path = ArchivePaths.create( "META-INF/file" + i ); - archive.addAsResource( new File( "src/test/resources/org/hibernate/jpa/test/packaging/loremipsum.txt" ), - path ); + archive.addAsResource( + "org/hibernate/jpa/test/packaging/loremipsum.txt", + path + ); } File testPackage = new File( packageTargetDir, fileName ); diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/ScannerTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/ScannerTest.java index ed2bc44952..28ed10ab8e 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/ScannerTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/ScannerTest.java @@ -23,31 +23,32 @@ */ package org.hibernate.jpa.test.packaging; -import java.io.File; -import java.lang.annotation.Annotation; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; -import javax.persistence.Converter; -import javax.persistence.Embeddable; -import javax.persistence.Entity; import javax.persistence.EntityManagerFactory; -import javax.persistence.MappedSuperclass; import javax.persistence.Persistence; - -import org.junit.Test; +import java.io.File; +import java.io.InputStream; +import java.util.HashMap; import org.hibernate.jpa.AvailableSettings; -import org.hibernate.jpa.packaging.internal.NativeScanner; - +import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor; +import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; +import org.hibernate.jpa.boot.scan.internal.StandardScanOptions; +import org.hibernate.jpa.boot.scan.internal.StandardScanner; +import org.hibernate.jpa.boot.spi.ClassDescriptor; +import org.hibernate.jpa.boot.spi.MappingFileDescriptor; +import org.hibernate.jpa.boot.spi.NamedInputStream; +import org.hibernate.jpa.boot.scan.spi.ScanOptions; +import org.hibernate.jpa.boot.scan.spi.ScanResult; +import org.hibernate.jpa.boot.scan.spi.Scanner; import org.hibernate.jpa.test.pack.defaultpar.ApplicationServer; -import org.hibernate.jpa.packaging.spi.NamedInputStream; -import org.hibernate.jpa.packaging.spi.Scanner; import org.hibernate.jpa.test.pack.defaultpar.Version; +import org.junit.Test; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; /** * @author Emmanuel Bernard @@ -59,32 +60,39 @@ public class ScannerTest extends PackagingTestCase { File defaultPar = buildDefaultPar(); addPackageToClasspath( defaultPar ); - Scanner scanner = new NativeScanner(); - assertEquals( "defaultpar", scanner.getUnqualifiedJarName( defaultPar.toURL() ) ); + PersistenceUnitDescriptor descriptor = new ParsedPersistenceXmlDescriptor( defaultPar.toURL() ); + ScanOptions options = new StandardScanOptions( "hbm,class", descriptor.isExcludeUnlistedClasses() ); + Scanner scanner = new StandardScanner(); + ScanResult scanResult = scanner.scan( descriptor, options ); - Set> annotationsToLookFor = new HashSet>( 3 ); - annotationsToLookFor.add( Entity.class ); - annotationsToLookFor.add( MappedSuperclass.class ); - annotationsToLookFor.add( Embeddable.class ); - annotationsToLookFor.add( Converter.class ); - final Set> classes = scanner.getClassesInJar( defaultPar.toURL(), annotationsToLookFor ); + assertEquals( 3, scanResult.getLocatedClasses().size() ); + assertClassesContained( scanResult, ApplicationServer.class ); + assertClassesContained( scanResult, Version.class ); - assertEquals( 3, classes.size() ); - assertTrue( classes.contains( ApplicationServer.class ) ); - assertTrue( classes.contains( Version.class ) ); - - Set filePatterns = new HashSet( 2 ); - filePatterns.add( "**/*.hbm.xml" ); - filePatterns.add( "META-INF/orm.xml" ); - final Set files = scanner.getFilesInJar( defaultPar.toURL(), filePatterns ); - - assertEquals( 2, files.size() ); - for ( NamedInputStream file : files ) { - assertNotNull( file.getStream() ); - file.getStream().close(); + assertEquals( 2, scanResult.getLocatedMappingFiles().size() ); + for ( MappingFileDescriptor mappingFileDescriptor : scanResult.getLocatedMappingFiles() ) { + assertNotNull( mappingFileDescriptor.getName() ); + assertNotNull( mappingFileDescriptor.getStreamAccess() ); + InputStream stream = mappingFileDescriptor.getStreamAccess().accessInputStream(); + assertNotNull( stream ); + stream.close(); + NamedInputStream namedInputStream = mappingFileDescriptor.getStreamAccess().asNamedInputStream(); + assertNotNull( namedInputStream ); + stream = namedInputStream.getStream(); + assertNotNull( stream ); + stream.close(); } } + private void assertClassesContained(ScanResult scanResult, Class classToCheckFor) { + for ( ClassDescriptor classDescriptor : scanResult.getLocatedClasses() ) { + if ( classDescriptor.getName().equals( classToCheckFor.getName() ) ) { + return; + } + } + fail( "ScanResult did not contain expected Class : " + classToCheckFor.getName() ); + } + @Test public void testCustomScanner() throws Exception { File defaultPar = buildDefaultPar();