HHH-9143 - Build baseline Jandex as part of scanning

This commit is contained in:
Steve Ebersole 2014-04-28 17:00:33 -05:00
parent 9db6596a15
commit 4b443069fd
22 changed files with 939 additions and 594 deletions

View File

@ -454,12 +454,29 @@ public interface AvailableSettings {
String FLUSH_MODE = "org.hibernate.flushMode";
/**
* Pass an implementation of {@link org.hibernate.ejb.packaging.Scanner}:
* - preferably an actual instance
* - or a class name with a no-arg constructor
* Pass an implementation of {@link org.hibernate.jpa.boot.scan.spi.Scanner}
* (or the deprecated {@link org.hibernate.ejb.packaging.Scanner}). Accepts
* either:<ul>
* <li>an actual instance</li>
* <li>a reference to a Class that implements Scanner</li>
* <li>a fully qualified name of a Class that implements Scanner</li>
* </ul>
*/
String SCANNER = "hibernate.ejb.resource_scanner";
/**
* Pass {@link org.hibernate.jpa.boot.archive.spi.ArchiveDescriptorFactory} to use
* in the scanning process. Accepts either:<ul>
* <li>an ArchiveDescriptorFactory instance</li>
* <li>a reference to a Class that implements ArchiveDescriptorFactory</li>
* <li>a fully qualified name of a Class that implements ArchiveDescriptorFactory</li>
* </ul>
*
* @see #SCANNER
* @see org.hibernate.jpa.boot.scan.spi.Scanner
*/
String SCANNER_ARCHIVE_DELEGATE = "hibernate.jpa.scanner_archive_delegate";
/**
* List of classes names
* Internal use only

View File

@ -64,8 +64,11 @@
import org.hibernate.internal.util.ValueHolder;
import org.hibernate.jaxb.spi.cfg.JaxbHibernateConfiguration;
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.boot.archive.spi.ArchiveDescriptorFactory;
import org.hibernate.jpa.boot.scan.internal.StandardJpaScanEnvironmentImpl;
import org.hibernate.jpa.boot.scan.internal.StandardScanOptions;
import org.hibernate.jpa.boot.scan.internal.StandardScanner;
import org.hibernate.jpa.boot.scan.spi.ScanEnvironment;
import org.hibernate.jpa.boot.scan.spi.ScanOptions;
import org.hibernate.jpa.boot.scan.spi.ScanResult;
import org.hibernate.jpa.boot.scan.spi.Scanner;
@ -691,28 +694,37 @@ private void configure(StandardServiceRegistry ssr, MergedSettings mergedSetting
@SuppressWarnings("unchecked")
private ScanResult scanDeployment(BootstrapServiceRegistry bootstrapServiceRegistry) {
IndexView jandexIndex = (IndexView) configurationValues.remove( JANDEX_INDEX );
Indexer indexer = null;
if ( jandexIndex != null ) {
indexer = new Indexer();
Indexer jandexIndexer = null;
if ( jandexIndex == null ) {
// If we don't have a Jandex index passed to us, we need to build one.
jandexIndexer = new Indexer();
}
final Scanner scanner = locateOrBuildScanner( bootstrapServiceRegistry );
final ScanOptions scanOptions = determineScanOptions( indexer );
final ScanEnvironment environment = new StandardJpaScanEnvironmentImpl( persistenceUnit );
final ScanOptions options = determineScanOptions( jandexIndexer, jandexIndex );
final ScanResult result = scanner.scan( environment, options );
ScanResult scanResult = scanner.scan( persistenceUnit, scanOptions );
if ( indexer != null ) {
jandexIndex = indexer.complete();
configurationValues.put( JANDEX_INDEX, jandexIndex );
if ( jandexIndexer != null ) {
jandexIndex = jandexIndexer.complete();
// if the indexer indexed anything use that index
//
// IMPL NOTE : (unfortunately?) Jandex has no way to see if an Indexer
// actually indexed anything. But, it also manages package-info.class
// as any other class, so this check should be fine
if ( !jandexIndex.getKnownClasses().isEmpty() ) {
configurationValues.put( JANDEX_INDEX, jandexIndex );
}
}
return scanResult;
return result;
}
@SuppressWarnings("unchecked")
private Scanner locateOrBuildScanner(BootstrapServiceRegistry bootstrapServiceRegistry) {
final Object value = configurationValues.remove( AvailableSettings.SCANNER );
if ( value == null ) {
return new StandardScanner();
return createStandardScanner( bootstrapServiceRegistry );
}
if ( Scanner.class.isInstance( value ) ) {
@ -746,11 +758,56 @@ private Scanner locateOrBuildScanner(BootstrapServiceRegistry bootstrapServiceRe
}
}
private ScanOptions determineScanOptions(Indexer indexer) {
@SuppressWarnings("unchecked")
private Scanner createStandardScanner(BootstrapServiceRegistry bootstrapServiceRegistry) {
final Object value = configurationValues.remove( AvailableSettings.SCANNER_ARCHIVE_DELEGATE );
if ( value == null ) {
return new StandardScanner();
}
if ( ArchiveDescriptorFactory.class.isInstance( value ) ) {
return new StandardScanner( (ArchiveDescriptorFactory) value );
}
final Class<? extends ArchiveDescriptorFactory> delegateClass;
if ( Class.class.isInstance( value ) ) {
try {
delegateClass = (Class<? extends ArchiveDescriptorFactory>) value;
}
catch ( ClassCastException e ) {
throw persistenceException(
"Expecting ArchiveDescriptorFactory implementation, but found " + ((Class) value).getName()
);
}
}
else {
final String delegateClassName = value.toString();
try {
delegateClass = bootstrapServiceRegistry.getService( ClassLoaderService.class ).classForName( delegateClassName );
}
catch ( ClassCastException e ) {
throw persistenceException(
"Expecting ArchiveDescriptorFactory implementation, but found " + delegateClassName
);
}
}
try {
final ArchiveDescriptorFactory delegate = delegateClass.newInstance();
return new StandardScanner( delegate );
}
catch (Exception e) {
throw persistenceException( "Unable to instantiate ArchiveDescriptorFactory class: " + delegateClass, e );
}
}
private ScanOptions determineScanOptions(Indexer indexer, IndexView jandexIndex) {
return new StandardScanOptions(
indexer,
jandexIndex,
(String) configurationValues.get( AvailableSettings.AUTODETECTION ),
persistenceUnit.isExcludeUnlistedClasses(),
indexer
persistenceUnit.isExcludeUnlistedClasses()
);
}

View File

@ -0,0 +1,60 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2014, 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.spi.ArchiveEntryHandler;
import org.hibernate.jpa.boot.scan.spi.ArchiveEntryHandlers;
import org.hibernate.jpa.boot.scan.spi.ClassFileArchiveEntryHandler;
import org.hibernate.jpa.boot.scan.spi.NonClassFileArchiveEntryHandler;
import org.hibernate.jpa.boot.scan.spi.PackageInfoArchiveEntryHandler;
/**
* @author Steve Ebersole
*/
public class ResultCoordinator implements ArchiveEntryHandlers {
private final ClassFileArchiveEntryHandler classEntryHandler;
private final PackageInfoArchiveEntryHandler packageEntryHandler;
private final ArchiveEntryHandler fileEntryHandler;
public ResultCoordinator(ScanResultCollector resultCollector) {
this.classEntryHandler = new ClassFileArchiveEntryHandler( resultCollector );
this.packageEntryHandler = new PackageInfoArchiveEntryHandler( resultCollector );
this.fileEntryHandler = new NonClassFileArchiveEntryHandler( resultCollector );
}
@Override
public ArchiveEntryHandler getClassFileHandler() {
return classEntryHandler;
}
@Override
public ArchiveEntryHandler getPackageInfoHandler() {
return packageEntryHandler;
}
@Override
public ArchiveEntryHandler getFileHandler() {
return fileEntryHandler;
}
}

View File

@ -0,0 +1,229 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2014, 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 java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.PersistenceException;
import org.hibernate.jpa.boot.scan.spi.ScanEnvironment;
import org.hibernate.jpa.boot.scan.spi.ScanOptions;
import org.hibernate.jpa.boot.scan.spi.ScanResult;
import org.hibernate.jpa.boot.spi.ClassDescriptor;
import org.hibernate.jpa.boot.spi.MappingFileDescriptor;
import org.hibernate.jpa.boot.spi.PackageDescriptor;
import org.hibernate.metamodel.source.internal.annotations.util.JPADotNames;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.logging.Logger;
/**
* @author Steve Ebersole
*/
public class ScanResultCollector {
private static final Logger log = Logger.getLogger( ScanResultCollector.class );
private final ScanEnvironment environment;
private final ScanOptions options;
private final Set<ClassDescriptor> discoveredClasses;
private final Set<PackageDescriptor> discoveredPackages;
private final Set<MappingFileDescriptor> discoveredMappingFiles;
public ScanResultCollector(ScanEnvironment environment, ScanOptions options) {
this.environment = environment;
this.options = options;
if ( options.getJandexIndexer() == null && options.getJandexView() == null ) {
throw new IllegalArgumentException( "Indexer and IndexView cannot both be null" );
}
if ( environment.getExplicitlyListedClassNames() == null ) {
throw new IllegalArgumentException( "ScanEnvironment#getExplicitlyListedClassNames should not return null" );
}
if ( environment.getExplicitlyListedMappingFiles() == null ) {
throw new IllegalArgumentException( "ScanEnvironment#getExplicitlyListedMappingFiles should not return null" );
}
this.discoveredPackages = new HashSet<PackageDescriptor>();
this.discoveredClasses = new HashSet<ClassDescriptor>();
this.discoveredMappingFiles = new HashSet<MappingFileDescriptor>();
}
public void handleClass(ClassDescriptor classDescriptor, boolean rootUrl) {
// Always make sure the Jandex entry is created.
final ClassInfo classInfo;
if ( options.getJandexIndexer() != null ) {
classInfo = indexStream( classDescriptor );
}
else {
classInfo = options.getJandexView().getClassByName( DotName.createSimple( classDescriptor.getName() ) );
}
if ( classInfo == null ) {
log.debugf(
"Could not locate class [%s] in Jandex; this will most likely lead to problems later",
classDescriptor.getName()
);
return;
}
// see if "discovery" of this entry is allowed
if ( !isListedOrDetectable( classInfo.name().toString(), rootUrl ) ) {
return;
}
if ( !containsClassAnnotationsOfInterest( classInfo ) ) {
// not strictly needed, but helps cut down on the size of discoveredClasses
return;
}
discoveredClasses.add( classDescriptor );
}
@SuppressWarnings("SimplifiableIfStatement")
protected boolean isListedOrDetectable(String name, boolean rootUrl) {
// IMPL NOTE : protect the calls to getExplicitlyListedClassNames unless needed,
// since it can take time with lots of listed classes.
if ( rootUrl ) {
// the entry comes from the root url, allow it if either:
// 1) we are allowed to discover classes/packages in the root url
// 2) the entry was explicitly listed
return options.canDetectUnlistedClassesInRoot()
|| environment.getExplicitlyListedClassNames().contains( name );
}
else {
// the entry comes from a non-root url, allow it if either:
// 1) we are allowed to discover classes/packages in non-root urls
// 2) the entry was explicitly listed
return options.canDetectUnlistedClassesInNonRoot()
|| environment.getExplicitlyListedClassNames().contains( name );
}
}
private ClassInfo indexStream(ClassDescriptor classDescriptor) {
InputStream stream = classDescriptor.getStreamAccess().accessInputStream();
try {
return options.getJandexIndexer().index( stream );
}
catch (IOException e) {
throw new PersistenceException(
"Could not add class [" + classDescriptor.getName() + "] to Jandex Indexer",
e
);
}
finally {
try {
stream.close();
}
catch (IOException ignore) {
}
}
}
@SuppressWarnings("SimplifiableIfStatement")
private boolean containsClassAnnotationsOfInterest(ClassInfo classInfo) {
if ( classInfo.annotations() == null ) {
return false;
}
return classInfo.annotations().containsKey( JPADotNames.ENTITY )
|| classInfo.annotations().containsKey( JPADotNames.MAPPED_SUPERCLASS )
|| classInfo.annotations().containsKey( JPADotNames.EMBEDDABLE )
|| classInfo.annotations().containsKey( JPADotNames.CONVERTER );
}
public void handlePackage(PackageDescriptor packageDescriptor, boolean rootUrl) {
// Always make sure the Jandex entry is created.
if ( options.getJandexIndexer() != null ) {
indexStream( packageDescriptor );
}
if ( !isListedOrDetectable( packageDescriptor.getName(), rootUrl ) ) {
// not strictly needed, but helps cut down on the size of discoveredPackages
return;
}
discoveredPackages.add( packageDescriptor );
}
private ClassInfo indexStream(PackageDescriptor packageDescriptor) {
InputStream stream = packageDescriptor.getStreamAccess().accessInputStream();
try {
return options.getJandexIndexer().index( stream );
}
catch (IOException e) {
throw new PersistenceException(
"Could not add package [" + packageDescriptor.getName() + "] to Jandex Indexer",
e
);
}
finally {
try {
stream.close();
}
catch (IOException ignore) {
}
}
}
public void handleMappingFile(MappingFileDescriptor mappingFileDescriptor, boolean rootUrl) {
if ( acceptAsMappingFile( mappingFileDescriptor, rootUrl ) ) {
discoveredMappingFiles.add( mappingFileDescriptor );
}
}
@SuppressWarnings("SimplifiableIfStatement")
private boolean acceptAsMappingFile(MappingFileDescriptor mappingFileDescriptor, boolean rootUrl) {
if ( mappingFileDescriptor.getName().endsWith( "hbm.xml" ) ) {
return options.canDetectHibernateMappingFiles();
}
if ( mappingFileDescriptor.getName().endsWith( "META-INF/orm.xml" ) ) {
if ( environment.getExplicitlyListedMappingFiles().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 rootUrl;
}
return true;
}
return environment.getExplicitlyListedMappingFiles().contains( mappingFileDescriptor.getName() );
}
public ScanResult toScanResult() {
return new ScanResultImpl(
Collections.unmodifiableSet( discoveredPackages ),
Collections.unmodifiableSet( discoveredClasses ),
Collections.unmodifiableSet( discoveredMappingFiles )
);
}
}

View File

@ -0,0 +1,64 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2014, 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 java.util.Set;
import org.hibernate.jpa.boot.scan.spi.ScanResult;
import org.hibernate.jpa.boot.spi.ClassDescriptor;
import org.hibernate.jpa.boot.spi.MappingFileDescriptor;
import org.hibernate.jpa.boot.spi.PackageDescriptor;
/**
* @author Steve Ebersole
*/
public class ScanResultImpl implements ScanResult {
private final Set<PackageDescriptor> packageDescriptorSet;
private final Set<ClassDescriptor> classDescriptorSet;
private final Set<MappingFileDescriptor> mappingFileSet;
public ScanResultImpl(
Set<PackageDescriptor> packageDescriptorSet,
Set<ClassDescriptor> classDescriptorSet,
Set<MappingFileDescriptor> mappingFileSet) {
this.packageDescriptorSet = packageDescriptorSet;
this.classDescriptorSet = classDescriptorSet;
this.mappingFileSet = mappingFileSet;
}
@Override
public Set<PackageDescriptor> getLocatedPackages() {
return packageDescriptorSet;
}
@Override
public Set<ClassDescriptor> getLocatedClasses() {
return classDescriptorSet;
}
@Override
public Set<MappingFileDescriptor> getLocatedMappingFiles() {
return mappingFileSet;
}
}

View File

@ -0,0 +1,80 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2014, 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 java.net.URL;
import java.util.Collections;
import java.util.List;
import org.hibernate.jpa.boot.scan.spi.ScanEnvironment;
import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor;
/**
* Implementation of ScanEnvironment leveraging a JPA deployment descriptor.
*
* @author Steve Ebersole
*/
public class StandardJpaScanEnvironmentImpl implements ScanEnvironment {
private final PersistenceUnitDescriptor persistenceUnitDescriptor;
private final List<String> explicitlyListedClassNames;
private final List<String> explicitlyListedMappingFiles;
public StandardJpaScanEnvironmentImpl(PersistenceUnitDescriptor persistenceUnitDescriptor) {
this.persistenceUnitDescriptor = persistenceUnitDescriptor;
this.explicitlyListedClassNames = persistenceUnitDescriptor.getManagedClassNames() == null
? Collections.<String>emptyList()
: persistenceUnitDescriptor.getManagedClassNames();
this.explicitlyListedMappingFiles = persistenceUnitDescriptor.getMappingFileNames() == null
? Collections.<String>emptyList()
: persistenceUnitDescriptor.getMappingFileNames();
}
@Override
public URL getRootUrl() {
return persistenceUnitDescriptor.getPersistenceUnitRootUrl();
}
@Override
public List<URL> getNonRootUrls() {
return persistenceUnitDescriptor.getJarFileUrls();
}
@Override
public List<String> getExplicitlyListedClassNames() {
return explicitlyListedClassNames;
}
@Override
public List<String> getExplicitlyListedMappingFiles() {
return explicitlyListedMappingFiles;
}
@Override
public PersistenceUnitDescriptor getPersistenceUnitDescriptor() {
return persistenceUnitDescriptor;
}
}

View File

@ -25,30 +25,27 @@
import org.hibernate.jpa.boot.scan.spi.ScanOptions;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.Indexer;
/**
* @author Steve Ebersole
*/
public class StandardScanOptions implements ScanOptions {
private final Indexer jandexIndexer;
private final IndexView jandexView;
private final boolean detectClassesInRoot;
private final boolean detectClassesInNonRoot;
private final boolean detectHibernateMappingFiles;
private final Indexer jandexIndexer;
public StandardScanOptions() {
this( "hbm,class", false );
}
public StandardScanOptions(
Indexer jandexIndexer,
IndexView jandexView,
String explicitDetectionSetting,
boolean persistenceUnitExcludeUnlistedClassesValue) {
this( explicitDetectionSetting, persistenceUnitExcludeUnlistedClassesValue, null );
}
public StandardScanOptions(
String explicitDetectionSetting,
boolean persistenceUnitExcludeUnlistedClassesValue,
Indexer jandexIndexer) {
this.jandexIndexer = jandexIndexer;
this.jandexView = jandexView;
if ( explicitDetectionSetting == null ) {
this.detectHibernateMappingFiles = true;
this.detectClassesInRoot = ! persistenceUnitExcludeUnlistedClassesValue;
@ -59,7 +56,32 @@ public StandardScanOptions(
this.detectClassesInRoot = explicitDetectionSetting.contains( "class" );
this.detectClassesInNonRoot = this.detectClassesInRoot;
}
this.jandexIndexer = jandexIndexer;
}
/**
* INTENDED FOR TESTING ONLY
*/
public StandardScanOptions() {
this( "hbm,class", false );
}
/**
* INTENDED FOR TESTING ONLY
*/
public StandardScanOptions(
String explicitDetectionSetting,
boolean persistenceUnitExcludeUnlistedClassesValue) {
this( new Indexer(), null, explicitDetectionSetting, persistenceUnitExcludeUnlistedClassesValue );
}
@Override
public Indexer getJandexIndexer() {
return jandexIndexer;
}
@Override
public IndexView getJandexView() {
return jandexView;
}
@Override
@ -76,9 +98,4 @@ public boolean canDetectUnlistedClassesInNonRoot() {
public boolean canDetectHibernateMappingFiles() {
return detectHibernateMappingFiles;
}
@Override
public Indexer getJandexIndexer() {
return jandexIndexer;
}
}

View File

@ -24,6 +24,7 @@
package org.hibernate.jpa.boot.scan.internal;
import org.hibernate.jpa.boot.archive.internal.StandardArchiveDescriptorFactory;
import org.hibernate.jpa.boot.archive.spi.ArchiveDescriptorFactory;
import org.hibernate.jpa.boot.scan.spi.AbstractScannerImpl;
/**
@ -35,6 +36,10 @@
*/
public class StandardScanner extends AbstractScannerImpl {
public StandardScanner() {
super( StandardArchiveDescriptorFactory.INSTANCE );
this( StandardArchiveDescriptorFactory.INSTANCE );
}
public StandardScanner(ArchiveDescriptorFactory value) {
super( value );
}
}

View File

@ -1,67 +0,0 @@
/*
* 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.ArchiveEntryHandler;
/**
* Base class for commonality between handling class file entries and handling package-info file entries.
*
* @author Steve Ebersole
*/
public abstract class AbstractJavaArtifactArchiveEntryHandler implements ArchiveEntryHandler {
private final ScanOptions scanOptions;
protected AbstractJavaArtifactArchiveEntryHandler(ScanOptions scanOptions) {
this.scanOptions = scanOptions;
}
/**
* Check to see if the incoming name (class/package name) is either:<ul>
* <li>explicitly listed in a {@code <class/>} entry within the {@code <persistence-unit/>}</li>
* <li>whether the scan options indicate that we are allowed to detect this entry</li>
* </ul>
*
* @param context Information about the archive. Mainly whether it is the root of the PU
* @param name The class/package name
*
* @return {@code true} if the named class/package is either detectable or explicitly listed; {@code false}
* otherwise.
*/
protected boolean isListedOrDetectable(ArchiveContext context, String name) {
// IMPL NOTE : protect the isExplicitlyListed call unless needed, since it can take time in a PU
// with lots of listed classes. The other conditions are simple boolean flag checks.
if ( context.isRootUrl() ) {
return scanOptions.canDetectUnlistedClassesInRoot() || isExplicitlyListed( context, name );
}
else {
return scanOptions.canDetectUnlistedClassesInNonRoot() || isExplicitlyListed( context, name );
}
}
private boolean isExplicitlyListed(ArchiveContext context, String name) {
return context.getPersistenceUnitDescriptor().getManagedClassNames().contains( name );
}
}

View File

@ -23,29 +23,15 @@
*/
package org.hibernate.jpa.boot.scan.spi;
import java.io.IOException;
import java.io.InputStream;
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 javax.persistence.PersistenceException;
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;
import org.hibernate.jpa.boot.scan.internal.ResultCoordinator;
import org.hibernate.jpa.boot.scan.internal.ScanResultCollector;
/**
* @author Steve Ebersole
@ -59,295 +45,62 @@ protected AbstractScannerImpl(ArchiveDescriptorFactory archiveDescriptorFactory)
}
@Override
public ScanResult scan(PersistenceUnitDescriptor persistenceUnit, ScanOptions scanOptions) {
final ResultCollector resultCollector = new ResultCollector( scanOptions );
public ScanResult scan(ScanEnvironment environment, ScanOptions options) {
final ScanResultCollector collector = new ScanResultCollector( environment, options );
final ResultCoordinator resultCoordinator = new ResultCoordinator( collector );
if ( persistenceUnit.getJarFileUrls() != null ) {
for ( URL url : persistenceUnit.getJarFileUrls() ) {
final ArchiveDescriptor descriptor = buildArchiveDescriptor( url, false, scanOptions );
final ArchiveContext context = buildArchiveContext( persistenceUnit, false, resultCollector );
if ( environment.getNonRootUrls() != null ) {
final ArchiveContext context = new ArchiveContextImpl( environment, false, resultCoordinator );
for ( URL url : environment.getNonRootUrls() ) {
final ArchiveDescriptor descriptor = buildArchiveDescriptor( url, false );
descriptor.visitArchive( context );
}
}
if ( persistenceUnit.getPersistenceUnitRootUrl() != null ) {
final ArchiveDescriptor descriptor = buildArchiveDescriptor( persistenceUnit.getPersistenceUnitRootUrl(), true, scanOptions );
final ArchiveContext context = buildArchiveContext( persistenceUnit, true, resultCollector );
if ( environment.getRootUrl() != null ) {
final ArchiveContext context = new ArchiveContextImpl( environment, true, resultCoordinator );
final ArchiveDescriptor descriptor = buildArchiveDescriptor( environment.getRootUrl(), true );
descriptor.visitArchive( context );
}
return ScanResultImpl.from( resultCollector );
return collector.toScanResult();
}
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) {
private ArchiveDescriptor buildArchiveDescriptor(URL url, boolean isRootUrl) {
final ArchiveDescriptor descriptor;
final ArchiveDescriptorInfo descriptorInfo = archiveDescriptorCache.get( url );
if ( descriptorInfo == null ) {
descriptor = archiveDescriptorFactory.buildArchiveDescriptor( url );
archiveDescriptorCache.put(
url,
new ArchiveDescriptorInfo( descriptor, isRootUrl, scanOptions )
new ArchiveDescriptorInfo( descriptor, isRootUrl )
);
}
else {
validateReuse( descriptorInfo, isRootUrl, scanOptions );
validateReuse( descriptorInfo, isRootUrl );
descriptor = descriptorInfo.archiveDescriptor;
}
return descriptor;
}
public static class ResultCollector
implements ArchiveEntryHandlers,
PackageInfoArchiveEntryHandler.Callback,
ClassFileArchiveEntryHandler.Callback,
NonClassFileArchiveEntryHandler.Callback {
private final ScanOptions scanOptions;
private final ClassFileArchiveEntryHandler classFileHandler;
private final PackageInfoArchiveEntryHandler packageInfoHandler;
private final NonClassFileArchiveEntryHandler fileHandler;
private final Set<PackageDescriptor> packageDescriptorSet = new HashSet<PackageDescriptor>();
private final Set<ClassDescriptor> classDescriptorSet = new HashSet<ClassDescriptor>();
private final Set<MappingFileDescriptor> mappingFileSet = new HashSet<MappingFileDescriptor>();
public ResultCollector(ScanOptions scanOptions) {
this.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) {
final PackageDescriptor keeper;
if ( PackageDescriptorImpl.class.isInstance( packageDescriptor ) ) {
keeper = packageDescriptor;
}
else {
// to make sure we have proper equals/hashcode
keeper = new PackageDescriptorImpl(
packageDescriptor.getName(),
packageDescriptor.getStreamAccess()
);
}
if ( scanOptions.getJandexIndexer() != null ) {
InputStream stream = keeper.getStreamAccess().accessInputStream();
try {
scanOptions.getJandexIndexer().index( stream );
}
catch (IOException e) {
throw new PersistenceException( "Could not add package-info to Jandex Indexer", e );
}
finally {
try {
stream.close();
}
catch (IOException ignore) {
}
}
}
packageDescriptorSet.add( keeper );
}
@Override
public void locatedClass(ClassDescriptor classDescriptor) {
// to make sure we have proper equals/hashcode
final ClassDescriptor keeper;
if ( ClassDescriptorImpl.class.isInstance( classDescriptor ) ) {
keeper = classDescriptor;
}
else {
keeper = new ClassDescriptorImpl(
classDescriptor.getName(),
classDescriptor.getStreamAccess()
);
}
if ( scanOptions.getJandexIndexer() != null ) {
InputStream stream = keeper.getStreamAccess().accessInputStream();
try {
scanOptions.getJandexIndexer().index( stream );
}
catch (IOException e) {
throw new PersistenceException(
"Could not add class [" + keeper.getName() + "] to Jandex Indexer",
e
);
}
finally {
try {
stream.close();
}
catch (IOException ignore) {
}
}
}
classDescriptorSet.add( keeper );
}
@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<PackageDescriptor> getPackageDescriptorSet() {
return packageDescriptorSet;
}
public Set<ClassDescriptor> getClassDescriptorSet() {
return classDescriptorSet;
}
public Set<MappingFileDescriptor> getMappingFileSet() {
return mappingFileSet;
}
}
// This needs to be protected and attributes/constructor visible in case
// a custom scanner needs to override validateReuse.
protected static class ArchiveDescriptorInfo {
public final ArchiveDescriptor archiveDescriptor;
public final boolean isRoot;
public final ScanOptions scanOptions;
public ArchiveDescriptorInfo(
ArchiveDescriptor archiveDescriptor,
boolean isRoot,
ScanOptions scanOptions) {
public ArchiveDescriptorInfo(ArchiveDescriptor archiveDescriptor, boolean isRoot) {
this.archiveDescriptor = archiveDescriptor;
this.isRoot = isRoot;
this.scanOptions = scanOptions;
}
}
protected void validateReuse(ArchiveDescriptorInfo descriptor, boolean root, ScanOptions options) {
@SuppressWarnings("UnusedParameters")
protected void validateReuse(ArchiveDescriptorInfo descriptor, boolean root) {
// 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<PackageDescriptor> packageDescriptorSet;
private final Set<ClassDescriptor> classDescriptorSet;
private final Set<MappingFileDescriptor> mappingFileSet;
private ScanResultImpl(
Set<PackageDescriptor> packageDescriptorSet,
Set<ClassDescriptor> classDescriptorSet,
Set<MappingFileDescriptor> 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<PackageDescriptor> getLocatedPackages() {
return packageDescriptorSet;
}
@Override
public Set<ClassDescriptor> getLocatedClasses() {
return classDescriptorSet;
}
@Override
public Set<MappingFileDescriptor> getLocatedMappingFiles() {
return mappingFileSet;
}
}
}

View File

@ -0,0 +1,73 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2014, 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.spi.PersistenceUnitDescriptor;
/**
* @author Steve Ebersole
*/
public class ArchiveContextImpl implements ArchiveContext {
private final ScanEnvironment environment;
private final boolean isRootUrl;
private final ArchiveEntryHandlers entryHandlers;
public ArchiveContextImpl(
ScanEnvironment environment,
boolean isRootUrl,
ArchiveEntryHandlers entryHandlers) {
this.environment = environment;
this.isRootUrl = isRootUrl;
this.entryHandlers = entryHandlers;
}
@Override
@SuppressWarnings("deprecation")
public PersistenceUnitDescriptor getPersistenceUnitDescriptor() {
return environment.getPersistenceUnitDescriptor();
}
@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();
}
}
}

View File

@ -0,0 +1,37 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2014, 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.ArchiveEntryHandler;
/**
* Composite of the various types of ArchiveEntryHandlers
*
* @author Steve Ebersole
*/
public interface ArchiveEntryHandlers {
public ArchiveEntryHandler getClassFileHandler();
public ArchiveEntryHandler getPackageInfoHandler();
public ArchiveEntryHandler getFileHandler();
}

View File

@ -26,18 +26,15 @@
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.persistence.Converter;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
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.internal.ScanResultCollector;
import org.hibernate.jpa.boot.spi.ClassDescriptor;
/**
@ -45,37 +42,16 @@
*
* @author Steve Ebersole
*/
public class ClassFileArchiveEntryHandler extends AbstractJavaArtifactArchiveEntryHandler {
private final Callback callback;
public class ClassFileArchiveEntryHandler implements ArchiveEntryHandler {
private final ScanResultCollector resultCollector;
/**
* Contract for the thing interested in being notified about accepted class descriptors.
*/
public static interface Callback {
public void locatedClass(ClassDescriptor classDescriptor);
}
public ClassFileArchiveEntryHandler(ScanOptions scanOptions, Callback callback) {
super( scanOptions );
this.callback = callback;
public ClassFileArchiveEntryHandler(ScanResultCollector resultCollector) {
this.resultCollector = resultCollector;
}
@Override
public void handleEntry(ArchiveEntry entry, ArchiveContext context) {
final ClassFile classFile = toClassFile( entry );
final ClassDescriptor classDescriptor = toClassDescriptor( classFile, entry );
if ( ! isListedOrDetectable( context, classDescriptor.getName() ) ) {
return;
}
// we are only interested in classes with certain annotations, so see if the JavaTypeDescriptor
// represents a class which contains any of those annotations
if ( ! containsClassAnnotationsOfInterest( classFile ) ) {
return;
}
notifyMatchedClass( classDescriptor );
resultCollector.handleClass( toClassDescriptor( toClassFile( entry ), entry ), context.isRootUrl() );
}
private ClassFile toClassFile(ArchiveEntry entry) {
@ -102,24 +78,7 @@ private ClassFile toClassFile(ArchiveEntry entry) {
}
}
@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) {
private ClassDescriptor toClassDescriptor(ClassFile classFile, ArchiveEntry entry) {
return new ClassDescriptorImpl( classFile.getName(), entry.getStreamAccess() );
}
protected final void notifyMatchedClass(ClassDescriptor classDescriptor) {
callback.locatedClass( classDescriptor );
}
}

View File

@ -27,7 +27,7 @@
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;
import org.hibernate.jpa.boot.scan.internal.ScanResultCollector;
/**
* Defines handling and filtering for all non-class file (package-info is also a class file...) entries within an archive
@ -35,54 +35,17 @@
* @author Steve Ebersole
*/
public class NonClassFileArchiveEntryHandler implements ArchiveEntryHandler {
private final ScanOptions scanOptions;
private final Callback callback;
private final ScanResultCollector resultCollector;
/**
* Contract for the thing interested in being notified about accepted mapping file descriptors.
*/
public static interface Callback {
public void locatedMappingFile(MappingFileDescriptor mappingFileDescriptor);
}
public NonClassFileArchiveEntryHandler(ScanOptions scanOptions, Callback callback) {
this.scanOptions = scanOptions;
this.callback = callback;
public NonClassFileArchiveEntryHandler(ScanResultCollector resultCollector) {
this.resultCollector = resultCollector;
}
@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.getNameWithinArchive().endsWith( "hbm.xml" ) ) {
return scanOptions.canDetectHibernateMappingFiles();
}
// todo : should really do this case-insensitively
// use getNameWithinArchive, not getName -- ensure paths are normalized (Windows, etc.)
if ( entry.getNameWithinArchive().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() );
resultCollector.handleMappingFile(
new MappingFileDescriptorImpl( entry.getNameWithinArchive(), entry.getStreamAccess() ),
context.isRootUrl()
);
}
}

View File

@ -25,7 +25,9 @@
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.scan.internal.ScanResultCollector;
import org.hibernate.jpa.boot.spi.PackageDescriptor;
/**
@ -33,19 +35,11 @@
*
* @author Steve Ebersole
*/
public class PackageInfoArchiveEntryHandler extends AbstractJavaArtifactArchiveEntryHandler {
private final Callback callback;
public class PackageInfoArchiveEntryHandler implements ArchiveEntryHandler {
private final ScanResultCollector resultCollector;
/**
* Contract for the thing interested in being notified about accepted package-info descriptors.
*/
public static interface Callback {
public void locatedPackage(PackageDescriptor packageDescriptor);
}
public PackageInfoArchiveEntryHandler(ScanOptions scanOptions, Callback callback) {
super( scanOptions );
this.callback = callback;
public PackageInfoArchiveEntryHandler(ScanResultCollector resultCollector) {
this.resultCollector = resultCollector;
}
@Override
@ -55,12 +49,7 @@ public void handleEntry(ArchiveEntry entry, ArchiveContext context) {
return;
}
if ( ! isListedOrDetectable( context, entry.getName() ) ) {
// the package is not explicitly listed, and we are not allowed to detect it.
return;
}
notifyMatchedPackage( toPackageDescriptor( entry ) );
resultCollector.handlePackage( toPackageDescriptor( entry ), context.isRootUrl() );
}
protected PackageDescriptor toPackageDescriptor(ArchiveEntry entry) {
@ -70,8 +59,4 @@ protected PackageDescriptor toPackageDescriptor(ArchiveEntry entry) {
return new PackageDescriptorImpl( packageName, entry.getStreamAccess() );
}
protected final void notifyMatchedPackage(PackageDescriptor packageDescriptor) {
callback.locatedPackage( packageDescriptor );
}
}

View File

@ -0,0 +1,48 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2014, 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.List;
import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor;
/**
* @author Steve Ebersole
*/
public interface ScanEnvironment {
public URL getRootUrl();
public List<URL> getNonRootUrls();
public List<String> getExplicitlyListedClassNames();
public List<String> getExplicitlyListedMappingFiles();
/**
* @deprecated Added temporarily to support legacy
* {@link org.hibernate.jpa.boot.archive.spi.ArchiveContext#getPersistenceUnitDescriptor()}
* calls
*/
@Deprecated
public PersistenceUnitDescriptor getPersistenceUnitDescriptor();
}

View File

@ -23,16 +23,58 @@
*/
package org.hibernate.jpa.boot.scan.spi;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.Indexer;
/**
* Options for performing scanning
*
* @author Steve Ebersole
*/
public interface ScanOptions {
/**
* The Jandex Indexer to use.
* <p/>
* Note that either this or {@link #getJandexView()} must return non-null.
*
* @return The Jandex Indexer to use
*/
public Indexer getJandexIndexer();
/**
* The Jandex index to use.
* <p/>
* Note that either this or {@link #getJandexIndexer()} must return non-null.
*
* @return The Jandex index to use.
*/
public IndexView getJandexView();
/**
* Is detection of managed classes from root url allowed? In strict JPA
* sense, this would be controlled by the {@code <exclude-unlisted-classes/>}
* element.
*
* @return Whether detection of classes from root url is allowed
*/
public boolean canDetectUnlistedClassesInRoot();
/**
* Is detection of managed classes from non-root urls allowed? In strict JPA
* sense, this would always be allowed.
*
* @return Whether detection of classes from non-root urls is allowed
*/
public boolean canDetectUnlistedClassesInNonRoot();
/**
* Is detection of Hibernate Mapping files allowed?
*
* @return Whether detection of Mapping files is allowed.
*
* @deprecated With move to unified schema, this setting is now deprecated and will
* be removed once support for reading {@code hbm.xml} files is fully removed.
*/
@Deprecated
public boolean canDetectHibernateMappingFiles();
public Indexer getJandexIndexer();
}

View File

@ -23,8 +23,6 @@
*/
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.
@ -34,13 +32,11 @@
*/
public interface Scanner {
/**
* Perform the scanning against the described persistence unit using the defined options, and return the scan
* results.
* Perform the scanning against the described environment 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.
* @param environment The scan environment
* @param options The options to control the scanning
*/
public ScanResult scan(PersistenceUnitDescriptor persistenceUnit, ScanOptions options);
public ScanResult scan(ScanEnvironment environment, ScanOptions options);
}

View File

@ -1,7 +1,7 @@
package org.hibernate.jpa.test.packaging;
import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor;
import org.hibernate.jpa.boot.scan.internal.StandardScanner;
import org.hibernate.jpa.boot.scan.spi.ScanEnvironment;
import org.hibernate.jpa.boot.scan.spi.ScanOptions;
import org.hibernate.jpa.boot.scan.spi.ScanResult;
import org.hibernate.jpa.boot.scan.spi.Scanner;
@ -22,8 +22,8 @@ public static void resetUsed() {
}
@Override
public ScanResult scan(PersistenceUnitDescriptor persistenceUnit, ScanOptions options) {
public ScanResult scan(ScanEnvironment environment, ScanOptions options) {
isUsed = true;
return delegate.scan( persistenceUnit, options );
return delegate.scan( environment, options );
}
}

View File

@ -32,32 +32,37 @@
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
import java.util.Collections;
import java.util.List;
import org.hibernate.dialect.H2Dialect;
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.ArchiveContext;
import org.hibernate.jpa.boot.archive.spi.ArchiveDescriptor;
import org.hibernate.jpa.boot.internal.ClassDescriptorImpl;
import org.hibernate.jpa.boot.scan.internal.ResultCoordinator;
import org.hibernate.jpa.boot.scan.internal.ScanResultCollector;
import org.hibernate.jpa.boot.scan.internal.StandardScanOptions;
import org.hibernate.jpa.boot.scan.spi.AbstractScannerImpl;
import org.hibernate.jpa.boot.scan.internal.StandardScanner;
import org.hibernate.jpa.boot.scan.spi.ArchiveContextImpl;
import org.hibernate.jpa.boot.scan.spi.ScanEnvironment;
import org.hibernate.jpa.boot.scan.spi.ScanResult;
import org.hibernate.jpa.boot.spi.MappingFileDescriptor;
import org.hibernate.jpa.test.PersistenceUnitDescriptorAdapter;
import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor;
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
@ -69,7 +74,7 @@
public class JarVisitorTest extends PackagingTestCase {
@Test
public void testHttp() throws Exception {
URL url = ArchiveHelper.getJarURLFromURLEntry(
final URL url = ArchiveHelper.getJarURLFromURLEntry(
new URL(
"jar:http://www.ibiblio.org/maven/hibernate/jars/hibernate-annotations-3.0beta1.jar!/META-INF/persistence.xml"
),
@ -83,18 +88,49 @@ public void testHttp() throws Exception {
//fail silently
return;
}
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() );
ScanResult result = standardScan( url );
assertEquals( 0, result.getLocatedClasses().size() );
assertEquals( 0, result.getLocatedPackages().size() );
assertEquals( 0, result.getLocatedMappingFiles().size() );
}
private ScanResult standardScan(URL url) {
ScanEnvironment env = new ScanEnvironmentImpl( url );
return new StandardScanner().scan( env, new StandardScanOptions() );
}
private static class ScanEnvironmentImpl implements ScanEnvironment {
private final URL rootUrl;
private ScanEnvironmentImpl(URL rootUrl) {
this.rootUrl = rootUrl;
}
@Override
public URL getRootUrl() {
return rootUrl;
}
@Override
public List<URL> getNonRootUrls() {
return Collections.emptyList();
}
@Override
public List<String> getExplicitlyListedClassNames() {
return Collections.emptyList();
}
@Override
public List<String> getExplicitlyListedMappingFiles() {
return Collections.emptyList();
}
@Override
public PersistenceUnitDescriptor getPersistenceUnitDescriptor() {
return null;
}
}
@Test
@ -102,36 +138,22 @@ public void testInputStreamZippedJar() throws Exception {
File defaultPar = buildDefaultPar();
addPackageToClasspath( defaultPar );
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 );
ScanResult result = standardScan( defaultPar.toURL() );
validateResults( result, 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() );
private void validateResults(ScanResult scanResult, Class... expectedClasses) throws IOException {
assertEquals( 3, scanResult.getLocatedClasses().size() );
for ( Class expectedClass : expectedClasses ) {
assertTrue(
resultCollector.getClassDescriptorSet().contains(
scanResult.getLocatedClasses().contains(
new ClassDescriptorImpl( expectedClass.getName(), null )
)
);
}
assertEquals( 2, resultCollector.getMappingFileSet().size() );
for ( MappingFileDescriptor mappingFileDescriptor : resultCollector.getMappingFileSet() ) {
assertEquals( 2, scanResult.getLocatedMappingFiles().size() );
for ( MappingFileDescriptor mappingFileDescriptor : scanResult.getLocatedMappingFiles() ) {
assertNotNull( mappingFileDescriptor.getStreamAccess() );
final InputStream stream = mappingFileDescriptor.getStreamAccess().accessInputStream();
assertNotNull( stream );
@ -147,39 +169,54 @@ public void testNestedJarProtocol() throws Exception {
addPackageToClasspath( nestedEar );
String jarFileName = nestedEar.toURL().toExternalForm() + "!/defaultpar.par";
URL rootUrl = new URL( jarFileName );
JarProtocolArchiveDescriptor archiveDescriptor = new JarProtocolArchiveDescriptor(
StandardArchiveDescriptorFactory.INSTANCE,
new URL( jarFileName ),
rootUrl,
""
);
AbstractScannerImpl.ResultCollector resultCollector = new AbstractScannerImpl.ResultCollector( new StandardScanOptions() );
ScanEnvironment environment = new ScanEnvironmentImpl( rootUrl );
ScanResultCollector collector = new ScanResultCollector( environment, new StandardScanOptions() );
archiveDescriptor.visitArchive(
new AbstractScannerImpl.ArchiveContextImpl(
new PersistenceUnitDescriptorAdapter(),
new ArchiveContextImpl(
environment,
true,
resultCollector
new ResultCoordinator( collector )
)
);
validateResults( resultCollector, org.hibernate.jpa.test.pack.defaultpar.ApplicationServer.class, Version.class );
validateResults(
collector.toScanResult(),
org.hibernate.jpa.test.pack.defaultpar.ApplicationServer.class,
Version.class
);
jarFileName = nestedEarDir.toURL().toExternalForm() + "!/defaultpar.par";
rootUrl = new URL( jarFileName );
archiveDescriptor = new JarProtocolArchiveDescriptor(
StandardArchiveDescriptorFactory.INSTANCE,
new URL( jarFileName ),
rootUrl,
""
);
resultCollector = new AbstractScannerImpl.ResultCollector( new StandardScanOptions() );
environment = new ScanEnvironmentImpl( rootUrl );
collector = new ScanResultCollector( environment, new StandardScanOptions() );
archiveDescriptor.visitArchive(
new AbstractScannerImpl.ArchiveContextImpl(
new PersistenceUnitDescriptorAdapter(),
new ArchiveContextImpl(
environment,
true,
resultCollector
new ResultCoordinator( collector )
)
);
validateResults( resultCollector, org.hibernate.jpa.test.pack.defaultpar.ApplicationServer.class, Version.class );
validateResults(
collector.toScanResult(),
org.hibernate.jpa.test.pack.defaultpar.ApplicationServer.class,
Version.class
);
}
@Test
@ -188,23 +225,27 @@ public void testJarProtocol() throws Exception {
addPackageToClasspath( war );
String jarFileName = war.toURL().toExternalForm() + "!/WEB-INF/classes";
URL rootUrl = new URL( jarFileName );
JarProtocolArchiveDescriptor archiveDescriptor = new JarProtocolArchiveDescriptor(
StandardArchiveDescriptorFactory.INSTANCE,
new URL( jarFileName ),
rootUrl,
""
);
AbstractScannerImpl.ResultCollector resultCollector = new AbstractScannerImpl.ResultCollector( new StandardScanOptions() );
final ScanEnvironment environment = new ScanEnvironmentImpl( rootUrl );
final ScanResultCollector collector = new ScanResultCollector( environment, new StandardScanOptions() );
archiveDescriptor.visitArchive(
new AbstractScannerImpl.ArchiveContextImpl(
new PersistenceUnitDescriptorAdapter(),
new ArchiveContextImpl(
environment,
true,
resultCollector
new ResultCoordinator( collector )
)
);
validateResults(
resultCollector,
collector.toScanResult(),
org.hibernate.jpa.test.pack.war.ApplicationServer.class,
org.hibernate.jpa.test.pack.war.Version.class
);
@ -215,21 +256,12 @@ public void testZippedJar() throws Exception {
File defaultPar = buildDefaultPar();
addPackageToClasspath( defaultPar );
JarFileBasedArchiveDescriptor archiveDescriptor = new JarFileBasedArchiveDescriptor(
StandardArchiveDescriptorFactory.INSTANCE,
defaultPar.toURL(),
""
ScanResult result = standardScan( defaultPar.toURL() );
validateResults(
result,
org.hibernate.jpa.test.pack.defaultpar.ApplicationServer.class,
Version.class
);
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
@ -243,31 +275,18 @@ public void testExplodedJar() throws Exception {
dirPath = dirPath.substring( 0, dirPath.length() - 1 );
}
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() );
ScanResult result = standardScan( ArchiveHelper.getURLFromPath( dirPath ) );
assertEquals( 1, result.getLocatedClasses().size() );
assertEquals( 1, result.getLocatedPackages().size() );
assertEquals( 1, result.getLocatedMappingFiles().size() );
assertTrue(
resultCollector.getClassDescriptorSet().contains(
result.getLocatedClasses().contains(
new ClassDescriptorImpl( Carpet.class.getName(), null )
)
);
for ( MappingFileDescriptor mappingFileDescriptor : resultCollector.getMappingFileSet() ) {
for ( MappingFileDescriptor mappingFileDescriptor : result.getLocatedMappingFiles() ) {
assertNotNull( mappingFileDescriptor.getStreamAccess() );
final InputStream stream = mappingFileDescriptor.getStreamAccess().accessInputStream();
assertNotNull( stream );

View File

@ -45,6 +45,7 @@
import org.hibernate.jpa.test.Distributor;
import org.hibernate.jpa.test.Item;
import org.hibernate.jpa.test.Kitten;
import org.hibernate.jpa.test.LastUpdateListener;
import org.hibernate.jpa.test.pack.cfgxmlpar.Morito;
import org.hibernate.jpa.test.pack.defaultpar.ApplicationServer;
import org.hibernate.jpa.test.pack.defaultpar.IncrementListener;
@ -289,7 +290,11 @@ protected File buildCfgXmlPar() {
JavaArchive archive = ShrinkWrap.create( JavaArchive.class,fileName );
archive.addClasses(
Morito.class,
Item.class
Item.class,
Distributor.class,
Cat.class,
Kitten.class,
LastUpdateListener.class
);
ArchivePath path = ArchivePaths.create( "META-INF/persistence.xml" );

View File

@ -31,13 +31,15 @@
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor;
import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor;
import org.hibernate.jpa.boot.scan.internal.StandardJpaScanEnvironmentImpl;
import org.hibernate.jpa.boot.scan.internal.StandardScanOptions;
import org.hibernate.jpa.boot.scan.spi.ScanEnvironment;
import org.hibernate.jpa.boot.scan.spi.ScanOptions;
import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor;
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;
@ -61,9 +63,10 @@ public void testNativeScanner() throws Exception {
addPackageToClasspath( defaultPar );
PersistenceUnitDescriptor descriptor = new ParsedPersistenceXmlDescriptor( defaultPar.toURL() );
ScanEnvironment env = new StandardJpaScanEnvironmentImpl( descriptor );
ScanOptions options = new StandardScanOptions( "hbm,class", descriptor.isExcludeUnlistedClasses() );
Scanner scanner = new StandardScanner();
ScanResult scanResult = scanner.scan( descriptor, options );
ScanResult scanResult = scanner.scan( env, options );
assertEquals( 3, scanResult.getLocatedClasses().size() );
assertClassesContained( scanResult, ApplicationServer.class );