/*
 * 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:
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; } }