/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.boot;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import javax.xml.transform.dom.DOMSource;
import org.hibernate.HibernateException;
import org.hibernate.boot.archive.spi.InputStreamAccess;
import org.hibernate.boot.internal.MetadataBuilderImpl;
import org.hibernate.boot.jaxb.Origin;
import org.hibernate.boot.jaxb.SourceType;
import org.hibernate.boot.jaxb.internal.CacheableFileXmlSource;
import org.hibernate.boot.jaxb.internal.JarFileEntryXmlSource;
import org.hibernate.boot.jaxb.internal.JaxpSourceXmlSource;
import org.hibernate.boot.jaxb.spi.Binding;
import org.hibernate.boot.registry.BootstrapServiceRegistry;
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.MetadataBuilderFactory;
import org.hibernate.boot.spi.XmlMappingBinderAccess;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.SerializationException;
import org.w3c.dom.Document;
Entry point into working with sources of metadata information (mapping XML, annotations). Tell Hibernate about sources and then call buildMetadata()
, or use getMetadataBuilder()
to customize how sources are processed (naming strategies, etc). Author: Steve Ebersole Since: 5.0
/**
* Entry point into working with sources of metadata information (mapping XML, annotations). Tell Hibernate
* about sources and then call {@link #buildMetadata()}, or use {@link #getMetadataBuilder()} to customize
* how sources are processed (naming strategies, etc).
*
* @author Steve Ebersole
*
* @since 5.0
*/
public class MetadataSources implements Serializable {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( MetadataSources.class );
private final ServiceRegistry serviceRegistry;
private XmlMappingBinderAccess xmlMappingBinderAccess;
private List<Binding> xmlBindings = new ArrayList<>();
private LinkedHashSet<Class<?>> annotatedClasses = new LinkedHashSet<>();
private LinkedHashSet<String> annotatedClassNames = new LinkedHashSet<>();
private LinkedHashSet<String> annotatedPackages = new LinkedHashSet<>();
public MetadataSources() {
this( new BootstrapServiceRegistryBuilder().build() );
}
Create a metadata sources using the specified service registry.
Params: - serviceRegistry – The service registry to use.
/**
* Create a metadata sources using the specified service registry.
*
* @param serviceRegistry The service registry to use.
*/
public MetadataSources(ServiceRegistry serviceRegistry) {
// service registry really should be either BootstrapServiceRegistry or StandardServiceRegistry type...
if ( ! isExpectedServiceRegistryType( serviceRegistry ) ) {
LOG.debugf(
"Unexpected ServiceRegistry type [%s] encountered during building of MetadataSources; may cause " +
"problems later attempting to construct MetadataBuilder",
serviceRegistry.getClass().getName()
);
}
this.serviceRegistry = serviceRegistry;
this.xmlMappingBinderAccess = new XmlMappingBinderAccess( serviceRegistry );
}
protected static boolean isExpectedServiceRegistryType(ServiceRegistry serviceRegistry) {
return BootstrapServiceRegistry.class.isInstance( serviceRegistry )
|| StandardServiceRegistry.class.isInstance( serviceRegistry );
}
public XmlMappingBinderAccess getXmlMappingBinderAccess() {
return xmlMappingBinderAccess;
}
public List<Binding> getXmlBindings() {
return xmlBindings;
}
public Collection<String> getAnnotatedPackages() {
return annotatedPackages;
}
public Collection<Class<?>> getAnnotatedClasses() {
return annotatedClasses;
}
public Collection<String> getAnnotatedClassNames() {
return annotatedClassNames;
}
public ServiceRegistry getServiceRegistry() {
return serviceRegistry;
}
Get a builder for metadata where non-default options can be specified.
Returns: The built metadata.
/**
* Get a builder for metadata where non-default options can be specified.
*
* @return The built metadata.
*/
public MetadataBuilder getMetadataBuilder() {
MetadataBuilderImpl defaultBuilder = new MetadataBuilderImpl( this );
return getCustomBuilderOrDefault( defaultBuilder );
}
Get a builder for metadata where non-default options can be specified.
Returns: The built metadata. Deprecated: Use getMetadataBuilder()
instead
/**
* Get a builder for metadata where non-default options can be specified.
*
* @return The built metadata.
* @deprecated Use {@link #getMetadataBuilder()} instead
*/
@Deprecated
public MetadataBuilder getMetadataBuilder(StandardServiceRegistry serviceRegistry) {
MetadataBuilderImpl defaultBuilder = new MetadataBuilderImpl( this, serviceRegistry );
return getCustomBuilderOrDefault( defaultBuilder );
}
In case a custom MetadataBuilderFactory
creates a custom builder, return that one, otherwise the default builder. /**
* In case a custom {@link MetadataBuilderFactory} creates a custom builder, return that one, otherwise the default
* builder.
*/
private MetadataBuilder getCustomBuilderOrDefault(MetadataBuilderImpl defaultBuilder) {
final ClassLoaderService cls = serviceRegistry.getService( ClassLoaderService.class );
final java.util.Collection<MetadataBuilderFactory> discoveredBuilderFactories = cls.loadJavaServices( MetadataBuilderFactory.class );
MetadataBuilder builder = null;
List<String> activeFactoryNames = null;
for ( MetadataBuilderFactory discoveredBuilderFactory : discoveredBuilderFactories ) {
final MetadataBuilder returnedBuilder = discoveredBuilderFactory.getMetadataBuilder( this, defaultBuilder );
if ( returnedBuilder != null ) {
if ( activeFactoryNames == null ) {
activeFactoryNames = new ArrayList<>();
}
activeFactoryNames.add( discoveredBuilderFactory.getClass().getName() );
builder = returnedBuilder;
}
}
if ( activeFactoryNames != null && activeFactoryNames.size() > 1 ) {
throw new HibernateException(
"Multiple active MetadataBuilder definitions were discovered : " +
String.join(", ", activeFactoryNames)
);
}
return builder != null ? builder : defaultBuilder;
}
Short-hand form of calling getMetadataBuilder()
and using its MetadataBuilder.build()
method in cases where the application wants to accept the defaults. Returns: The built metadata.
/**
* Short-hand form of calling {@link #getMetadataBuilder()} and using its
* {@link org.hibernate.boot.MetadataBuilder#build()} method in cases where the application wants
* to accept the defaults.
*
* @return The built metadata.
*/
public Metadata buildMetadata() {
return getMetadataBuilder().build();
}
public Metadata buildMetadata(StandardServiceRegistry serviceRegistry) {
return getMetadataBuilder( serviceRegistry ).build();
}
Read metadata from the annotations attached to the given class.
Params: - annotatedClass – The class containing annotations
Returns: this (for method chaining)
/**
* Read metadata from the annotations attached to the given class.
*
* @param annotatedClass The class containing annotations
*
* @return this (for method chaining)
*/
public MetadataSources addAnnotatedClass(Class annotatedClass) {
annotatedClasses.add( annotatedClass );
return this;
}
Read metadata from the annotations attached to the given class. The important distinction here is that the Class
will not be accessed until later which is important for on-the-fly bytecode-enhancement Params: - annotatedClassName – The name of a class containing annotations
Returns: this (for method chaining)
/**
* Read metadata from the annotations attached to the given class. The important
* distinction here is that the {@link Class} will not be accessed until later
* which is important for on-the-fly bytecode-enhancement
*
* @param annotatedClassName The name of a class containing annotations
*
* @return this (for method chaining)
*/
public MetadataSources addAnnotatedClassName(String annotatedClassName) {
annotatedClassNames.add( annotatedClassName );
return this;
}
Read package-level metadata.
Params: - packageName – java package name without trailing '.', cannot be
null
Returns: this (for method chaining)
/**
* Read package-level metadata.
*
* @param packageName java package name without trailing '.', cannot be {@code null}
*
* @return this (for method chaining)
*/
public MetadataSources addPackage(String packageName) {
if ( packageName == null ) {
throw new IllegalArgumentException( "The specified package name cannot be null" );
}
if ( packageName.endsWith( "." ) ) {
packageName = packageName.substring( 0, packageName.length() - 1 );
}
annotatedPackages.add( packageName );
return this;
}
Read package-level metadata.
Params: - packageRef – Java Package reference
Returns: this (for method chaining)
/**
* Read package-level metadata.
*
* @param packageRef Java Package reference
*
* @return this (for method chaining)
*/
public MetadataSources addPackage(Package packageRef) {
annotatedPackages.add( packageRef.getName() );
return this;
}
Read a mapping as an application resource using the convention that a class named foo.bar.Foo
is mapped by a file named foo/bar/Foo.hbm.xml
which can be resolved as a classpath resource. Params: - entityClass – The mapped class. Cannot be
null
null.
Returns: this (for method chaining purposes) Deprecated: hbm.xml is a legacy mapping format now considered deprecated.
/**
* Read a mapping as an application resource using the convention that a class named {@code foo.bar.Foo} is
* mapped by a file named {@code foo/bar/Foo.hbm.xml} which can be resolved as a classpath resource.
*
* @param entityClass The mapped class. Cannot be {@code null} null.
*
* @return this (for method chaining purposes)
*
* @deprecated hbm.xml is a legacy mapping format now considered deprecated.
*/
@Deprecated
public MetadataSources addClass(Class entityClass) {
if ( entityClass == null ) {
throw new IllegalArgumentException( "The specified class cannot be null" );
}
LOG.debugf( "adding resource mappings from class convention : %s", entityClass.getName() );
final String mappingResourceName = entityClass.getName().replace( '.', '/' ) + ".hbm.xml";
addResource( mappingResourceName );
return this;
}
Read mappings as a application resourceName (i.e. classpath lookup).
Params: - name – The resource name
Returns: this (for method chaining purposes)
/**
* Read mappings as a application resourceName (i.e. classpath lookup).
*
* @param name The resource name
*
* @return this (for method chaining purposes)
*/
public MetadataSources addResource(String name) {
xmlBindings.add( getXmlMappingBinderAccess().bind( name ) );
return this;
}
Read mappings from a particular XML file
Params: - path – The path to a file. Expected to be resolvable by
File(String)
See Also: Returns: this (for method chaining purposes)
/**
* Read mappings from a particular XML file
*
* @param path The path to a file. Expected to be resolvable by {@link java.io.File#File(String)}
*
* @return this (for method chaining purposes)
*
* @see #addFile(java.io.File)
*/
public MetadataSources addFile(String path) {
addFile( new File( path ) );
return this;
}
Read mappings from a particular XML file
Params: - file – The reference to the XML file
Returns: this (for method chaining purposes)
/**
* Read mappings from a particular XML file
*
* @param file The reference to the XML file
*
* @return this (for method chaining purposes)
*/
public MetadataSources addFile(File file) {
xmlBindings.add( getXmlMappingBinderAccess().bind( file ) );
return this;
}
See addCacheableFile(File)
for description Params: - path – The path to a file. Expected to be resolvable by
File(String)
See Also: Returns: this (for method chaining purposes)
/**
* See {@link #addCacheableFile(java.io.File)} for description
*
* @param path The path to a file. Expected to be resolvable by {@link java.io.File#File(String)}
*
* @return this (for method chaining purposes)
*
* @see #addCacheableFile(java.io.File)
*/
public MetadataSources addCacheableFile(String path) {
final Origin origin = new Origin( SourceType.FILE, path );
addCacheableFile( origin, new File( path ) );
return this;
}
private void addCacheableFile(Origin origin, File file) {
xmlBindings.add( new CacheableFileXmlSource( origin, file, false ).doBind( getXmlMappingBinderAccess().getMappingBinder() ) );
}
Add a cached mapping file. A cached file is a serialized representation of the DOM structure of a particular mapping. It is saved from a previous call as a file with the name {xmlFile}.bin
where {xmlFile}
is the name of the original mapping file. If a cached {xmlFile}.bin
exists and is newer than {xmlFile}
, the {xmlFile}.bin
file will be read directly. Otherwise {xmlFile}
is read and then serialized to {xmlFile}.bin
for use the next time. Params: - file – The cacheable mapping file to be added,
{xmlFile}
in above discussion.
Returns: this (for method chaining purposes)
/**
* Add a cached mapping file. A cached file is a serialized representation of the DOM structure of a
* particular mapping. It is saved from a previous call as a file with the name {@code {xmlFile}.bin}
* where {@code {xmlFile}} is the name of the original mapping file.
* </p>
* If a cached {@code {xmlFile}.bin} exists and is newer than {@code {xmlFile}}, the {@code {xmlFile}.bin}
* file will be read directly. Otherwise {@code {xmlFile}} is read and then serialized to {@code {xmlFile}.bin} for
* use the next time.
*
* @param file The cacheable mapping file to be added, {@code {xmlFile}} in above discussion.
*
* @return this (for method chaining purposes)
*/
public MetadataSources addCacheableFile(File file) {
final Origin origin = new Origin( SourceType.FILE, file.getName() );
addCacheableFile( origin, file );
return this;
}
INTENDED FOR TESTSUITE USE ONLY!
Much like addCacheableFile(File)
except that here we will fail immediately if the cache version cannot be found or used for whatever reason Params: - file – The xml file, not the bin!
Throws: - SerializationException – Indicates a problem deserializing the cached dom tree
- FileNotFoundException – Indicates that the cached file was not found or was not usable.
Returns: The dom "deserialized" from the cached file.
/**
* <b>INTENDED FOR TESTSUITE USE ONLY!</b>
* <p/>
* Much like {@link #addCacheableFile(java.io.File)} except that here we will fail immediately if
* the cache version cannot be found or used for whatever reason
*
* @param file The xml file, not the bin!
*
* @return The dom "deserialized" from the cached file.
*
* @throws org.hibernate.type.SerializationException Indicates a problem deserializing the cached dom tree
* @throws java.io.FileNotFoundException Indicates that the cached file was not found or was not usable.
*/
public MetadataSources addCacheableFileStrictly(File file) throws SerializationException, FileNotFoundException {
final Origin origin = new Origin( SourceType.FILE, file.getAbsolutePath() );
xmlBindings.add( new CacheableFileXmlSource( origin, file, true ).doBind( getXmlMappingBinderAccess().getMappingBinder() ) );
return this;
}
Read metadata from an InputStream
access Params: - xmlInputStreamAccess – Access to an input stream containing a DOM.
Returns: this (for method chaining purposes)
/**
* Read metadata from an {@link java.io.InputStream} access
*
* @param xmlInputStreamAccess Access to an input stream containing a DOM.
*
* @return this (for method chaining purposes)
*/
public MetadataSources addInputStream(InputStreamAccess xmlInputStreamAccess) {
xmlBindings.add( getXmlMappingBinderAccess().bind( xmlInputStreamAccess ) );
return this;
}
Read metadata from an InputStream
. Params: - xmlInputStream – The input stream containing a DOM.
Returns: this (for method chaining purposes)
/**
* Read metadata from an {@link java.io.InputStream}.
*
* @param xmlInputStream The input stream containing a DOM.
*
* @return this (for method chaining purposes)
*/
public MetadataSources addInputStream(InputStream xmlInputStream) {
xmlBindings.add( getXmlMappingBinderAccess().bind( xmlInputStream ) );
return this;
}
Read mappings from a URL
Params: - url – The url for the mapping document to be read.
Returns: this (for method chaining purposes)
/**
* Read mappings from a {@link java.net.URL}
*
* @param url The url for the mapping document to be read.
*
* @return this (for method chaining purposes)
*/
public MetadataSources addURL(URL url) {
xmlBindings.add( getXmlMappingBinderAccess().bind( url ) );
return this;
}
Read mappings from a DOM Document
Params: - document – The DOM document
Returns: this (for method chaining purposes) Deprecated: since 5.0. Use one of the other methods for passing mapping source(s).
/**
* Read mappings from a DOM {@link org.w3c.dom.Document}
*
* @param document The DOM document
*
* @return this (for method chaining purposes)
*
* @deprecated since 5.0. Use one of the other methods for passing mapping source(s).
*/
@Deprecated
public MetadataSources addDocument(Document document) {
final Origin origin = new Origin( SourceType.DOM, Origin.UNKNOWN_FILE_PATH );
xmlBindings.add( new JaxpSourceXmlSource( origin, new DOMSource( document ) ).doBind( getXmlMappingBinderAccess().getMappingBinder() ) );
return this;
}
Read all mappings from a jar file.
Assumes that any file named *.hbm.xml is a mapping document.
Params: - jar – a jar file
Returns: this (for method chaining purposes)
/**
* Read all mappings from a jar file.
* <p/>
* Assumes that any file named <tt>*.hbm.xml</tt> is a mapping document.
*
* @param jar a jar file
*
* @return this (for method chaining purposes)
*/
public MetadataSources addJar(File jar) {
LOG.debugf( "Seeking mapping documents in jar file : %s", jar.getName() );
final Origin origin = new Origin( SourceType.JAR, jar.getAbsolutePath() );
try {
JarFile jarFile = new JarFile( jar );
try {
Enumeration jarEntries = jarFile.entries();
while ( jarEntries.hasMoreElements() ) {
final ZipEntry zipEntry = (ZipEntry) jarEntries.nextElement();
if ( zipEntry.getName().endsWith( ".hbm.xml" ) ) {
LOG.tracef( "found mapping document : %s", zipEntry.getName() );
xmlBindings.add(
new JarFileEntryXmlSource( origin, jarFile, zipEntry ).doBind( getXmlMappingBinderAccess().getMappingBinder() )
);
}
}
}
finally {
try {
jarFile.close();
}
catch ( Exception ignore ) {
}
}
}
catch ( IOException e ) {
throw new MappingNotFoundException( e, origin );
}
return this;
}
Read all mapping documents from a directory tree.
Assumes that any file named *.hbm.xml is a mapping document.
Params: - dir – The directory
Throws: - MappingException – Indicates problems reading the jar file or
processing the contained mapping documents.
Returns: this (for method chaining purposes)
/**
* Read all mapping documents from a directory tree.
* <p/>
* Assumes that any file named <tt>*.hbm.xml</tt> is a mapping document.
*
* @param dir The directory
*
* @return this (for method chaining purposes)
*
* @throws org.hibernate.MappingException Indicates problems reading the jar file or
* processing the contained mapping documents.
*/
public MetadataSources addDirectory(File dir) {
File[] files = dir.listFiles();
if ( files != null && files.length > 0 ) {
for ( File file : files ) {
if ( file.isDirectory() ) {
addDirectory( file );
}
else if ( file.getName().endsWith( ".hbm.xml" ) ) {
addFile( file );
}
}
}
return this;
}
}