HHH-6161 Configure annotation based configuration via MetadataSources

* Removing obsolete XmlHelper
* Updating tests
* Fixing bug in JaxbHelper related to orm parsing
This commit is contained in:
Hardy Ferentschik 2011-04-26 18:37:02 +02:00
parent dd019a1f43
commit b398e8c932
10 changed files with 157 additions and 203 deletions

View File

@ -56,7 +56,7 @@ public class MetadataSources {
private static final Logger LOG = Logger.getLogger( MetadataSources.class );
private List<JaxbRoot> jaxbRootList = new ArrayList<JaxbRoot>();
private LinkedHashSet<Class> annotatedClasses = new LinkedHashSet<Class>();
private LinkedHashSet<Class<?>> annotatedClasses = new LinkedHashSet<Class<?>>();
private LinkedHashSet<String> annotatedPackages = new LinkedHashSet<String>();
private final JaxbHelper jaxbHelper;
@ -85,7 +85,7 @@ public class MetadataSources {
return annotatedPackages;
}
public Iterable<Class> getAnnotatedClasses() {
public Iterable<Class<?>> getAnnotatedClasses() {
return annotatedClasses;
}

View File

@ -1,26 +1,18 @@
package org.hibernate.metamodel.source.annotations.xml;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;
import javax.xml.bind.JAXBException;
import java.util.List;
import org.jboss.jandex.Index;
import org.hibernate.AnnotationException;
import org.hibernate.metamodel.source.internal.MetadataImpl;
import org.hibernate.metamodel.source.annotation.xml.XMLEntityMappings;
import org.hibernate.metamodel.source.util.xml.XmlHelper;
import org.hibernate.service.classloading.spi.ClassLoaderService;
import org.hibernate.metamodel.source.internal.JaxbRoot;
import org.hibernate.metamodel.source.internal.MetadataImpl;
/**
* @author Hardy Ferentschik
* @todo Need some create some XMLContext as well which can be populated w/ information which can not be expressed via annotations
*/
public class OrmXmlParser {
private static final String ORM1_MAPPING_XSD = "org/hibernate/ejb/orm_1_0.xsd";
private static final String ORM2_MAPPING_XSD = "org/hibernate/ejb/orm_2_0.xsd";
private final MetadataImpl meta;
public OrmXmlParser(MetadataImpl meta) {
@ -30,38 +22,16 @@ public class OrmXmlParser {
/**
* Parses the given xml configuration files and returns a updated annotation index
*
* @param mappingFileNames the file names of the xml files to parse
* @param mappings list of {@code XMLEntityMappings} created from the specified orm xml files
* @param annotationIndex the annotation index based on scanned annotations
*
* @return a new updated annotation index, enhancing and modifying the existing ones according to the jpa xml rules
*/
public Index parseAndUpdateIndex(Set<String> mappingFileNames, Index annotationIndex) {
ClassLoaderService classLoaderService = meta.getServiceRegistry().getService( ClassLoaderService.class );
Set<InputStream> mappingStreams = new HashSet<InputStream>();
for ( String fileName : mappingFileNames ) {
XMLEntityMappings entityMappings;
try {
entityMappings = XmlHelper.unmarshallXml(
fileName, ORM2_MAPPING_XSD, XMLEntityMappings.class, classLoaderService
).getRoot();
public Index parseAndUpdateIndex(List<JaxbRoot<XMLEntityMappings>> mappings, Index annotationIndex) {
for ( JaxbRoot<XMLEntityMappings> root : mappings ) {
root.getRoot().toString();
}
catch ( JAXBException orm2Exception ) {
// if we cannot parse against orm_2_0.xsd we try orm_1_0.xsd for backwards compatibility
try {
entityMappings = XmlHelper.unmarshallXml(
fileName, ORM1_MAPPING_XSD, XMLEntityMappings.class, classLoaderService
).getRoot();
}
catch ( JAXBException orm1Exception ) {
throw new AnnotationException( "Unable to parse xml configuration.", orm1Exception );
}
}
entityMappings.toString();
}
return null;
return annotationIndex;
}
}

View File

@ -24,10 +24,16 @@
package org.hibernate.metamodel.source.internal;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.ValidationEventLocator;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
@ -38,9 +44,6 @@ import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import org.jboss.logging.Logger;
import org.w3c.dom.Document;
@ -56,7 +59,10 @@ import org.hibernate.metamodel.source.hbm.xml.mapping.XMLHibernateMapping;
import org.hibernate.service.classloading.spi.ClassLoaderService;
/**
* Helper class for unmarshalling xml configuration using StAX and JAXB.
*
* @author Steve Ebersole
* @author Hardy Ferentschik
*/
public class JaxbHelper {
private static final Logger log = Logger.getLogger( JaxbHelper.class );
@ -79,11 +85,11 @@ public class JaxbHelper {
try {
staxReader.close();
}
catch (Exception ignore) {
catch ( Exception ignore ) {
}
}
}
catch (XMLStreamException e) {
catch ( XMLStreamException e ) {
throw new MappingException( "Unable to create stax reader", e, origin );
}
}
@ -97,25 +103,25 @@ public class JaxbHelper {
return staxFactory;
}
@SuppressWarnings( {"UnnecessaryLocalVariable"})
@SuppressWarnings( { "UnnecessaryLocalVariable" })
private XMLInputFactory buildStaxFactory() {
XMLInputFactory staxFactory = XMLInputFactory.newInstance();
return staxFactory;
}
private static final QName ORM_VERSION_ATTRIBUTE_QNAME = new QName( "http://java.sun.com/xml/ns/persistence/orm", "version" );
private static final QName ORM_VERSION_ATTRIBUTE_QNAME = new QName( "version" );
@SuppressWarnings( {"unchecked"})
private JaxbRoot unmarshal(XMLEventReader staxEventReader, Origin origin) {
@SuppressWarnings( { "unchecked" })
private JaxbRoot unmarshal(XMLEventReader staxEventReader, final Origin origin) {
XMLEvent event;
try {
event = staxEventReader.peek();
while ( event != null && ! event.isStartElement() ) {
while ( event != null && !event.isStartElement() ) {
staxEventReader.nextEvent();
event = staxEventReader.peek();
}
}
catch (Exception e) {
catch ( Exception e ) {
throw new MappingException( "Error accessing stax stream", e, origin );
}
@ -140,20 +146,30 @@ public class JaxbHelper {
}
final Object target;
final ContextProvidingValidationEventHandler handler = new ContextProvidingValidationEventHandler();
try {
JAXBContext jaxbContext = JAXBContext.newInstance( jaxbTarget );
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setSchema( validationSchema );
unmarshaller.setEventHandler( handler );
target = unmarshaller.unmarshal( staxEventReader );
}
catch (JAXBException e) {
throw new MappingException( "Unable to perform unmarshalling", e, origin );
catch ( JAXBException e ) {
StringBuilder builder = new StringBuilder();
builder.append( "Unable to perform unmarshalling at line number " );
builder.append( handler.getLineNumber() );
builder.append( " and column " );
builder.append( handler.getColumnNumber() );
builder.append( ". Message: " );
builder.append( handler.getMessage() );
throw new MappingException( builder.toString(), e, origin );
}
return new JaxbRoot( target, origin );
}
@SuppressWarnings( {"unchecked"})
@SuppressWarnings( { "unchecked" })
public JaxbRoot unmarshal(Document document, Origin origin) {
Element rootElement = document.getDocumentElement();
if ( rootElement == null ) {
@ -180,7 +196,7 @@ public class JaxbHelper {
unmarshaller.setSchema( validationSchema );
target = unmarshaller.unmarshal( new DOMSource( document ) );
}
catch (JAXBException e) {
catch ( JAXBException e ) {
throw new MappingException( "Unable to perform unmarshalling", e, origin );
}
@ -193,7 +209,7 @@ public class JaxbHelper {
return orm1Schema();
}
else if ( "2.0".equals( xsdVersionString ) ) {
return orm1Schema();
return orm2Schema();
}
throw new IllegalArgumentException( "Unsupported orm.xml XSD version encountered [" + xsdVersionString + "]" );
}
@ -234,16 +250,18 @@ public class JaxbHelper {
}
private Schema resolveLocalSchema(String schemaName, String schemaLanguage) {
URL url = metadataSources.getServiceRegistry().getService( ClassLoaderService.class ).locateResource( schemaName );
URL url = metadataSources.getServiceRegistry()
.getService( ClassLoaderService.class )
.locateResource( schemaName );
if ( url == null ) {
throw new XsdException( "Unable to locate schema [" + schemaName + "] via classpath", schemaName );
}
try {
InputStream schemaStream = url.openStream();
try {
StreamSource source = new StreamSource(url.openStream());
StreamSource source = new StreamSource( url.openStream() );
SchemaFactory schemaFactory = SchemaFactory.newInstance( schemaLanguage );
return schemaFactory.newSchema(source);
return schemaFactory.newSchema( source );
}
catch ( SAXException e ) {
throw new XsdException( "Unable to load schema [" + schemaName + "]", e, schemaName );
@ -265,5 +283,30 @@ public class JaxbHelper {
}
}
static class ContextProvidingValidationEventHandler implements ValidationEventHandler {
private int lineNumber;
private int columnNumber;
private String message;
@Override
public boolean handleEvent(ValidationEvent validationEvent) {
ValidationEventLocator locator = validationEvent.getLocator();
lineNumber = locator.getLineNumber();
columnNumber = locator.getColumnNumber();
message = validationEvent.getMessage();
return false;
}
public int getLineNumber() {
return lineNumber;
}
public int getColumnNumber() {
return columnNumber;
}
public String getMessage() {
return message;
}
}
}

View File

@ -23,15 +23,20 @@
*/
package org.hibernate.metamodel.source.internal;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jboss.jandex.Index;
import org.jboss.jandex.Indexer;
import org.jboss.logging.Logger;
import org.hibernate.DuplicateMappingException;
import org.hibernate.HibernateException;
import org.hibernate.cfg.NamingStrategy;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.mapping.MetadataSource;
@ -41,8 +46,11 @@ import org.hibernate.metamodel.binding.PluralAttributeBinding;
import org.hibernate.metamodel.relational.Database;
import org.hibernate.metamodel.source.Metadata;
import org.hibernate.metamodel.source.MetadataSources;
import org.hibernate.metamodel.source.annotation.xml.XMLEntityMappings;
import org.hibernate.metamodel.source.annotations.AnnotationBinder;
import org.hibernate.metamodel.source.annotations.xml.OrmXmlParser;
import org.hibernate.metamodel.source.hbm.HibernateXmlBinder;
import org.hibernate.metamodel.source.hbm.xml.mapping.XMLHibernateMapping;
import org.hibernate.metamodel.source.spi.MetadataImplementor;
import org.hibernate.service.BasicServiceRegistry;
@ -50,15 +58,15 @@ import org.hibernate.service.BasicServiceRegistry;
* Container for configuration data while building and binding the metamodel
*
* @author Steve Ebersole
* @author Hardy Ferentschik
*/
public class MetadataImpl implements Metadata, MetadataImplementor, Serializable {
private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, MetadataImpl.class.getName() );
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
CoreMessageLogger.class, MetadataImpl.class.getName()
);
private final BasicServiceRegistry serviceRegistry;
private final NamingStrategy namingStrategy;
final AnnotationBinder annotationBinder = new AnnotationBinder( this );
private final Database database = new Database();
private Map<String, EntityBinding> entityBindingMap = new HashMap<String, EntityBinding>();
@ -85,24 +93,49 @@ public class MetadataImpl implements Metadata, MetadataImplementor, Serializable
final HibernateXmlBinder hibernateXmlBinder = new HibernateXmlBinder( this );
for ( JaxbRoot jaxbRoot : metadataSources.getJaxbRootList() ) {
// filter to just hbm-based roots
if ( jaxbRoot.getRoot() instanceof XMLHibernateMapping ) {
hibernateXmlBinder.bindRoot( jaxbRoot );
}
}
}
private void applyAnnotationMappings(MetadataSources metadataSources, List<String> processedEntityNames) {
// todo : not sure how to best mesh what we have here with what AnnotationBinder is expecting... hardy?
// also, AnnotationBinder should become method local
// create a jandex index from the annotated classes
Indexer indexer = new Indexer();
for ( Class<?> clazz : metadataSources.getAnnotatedClasses() ) {
InputStream stream = getClass().getClassLoader().getResourceAsStream(
clazz.getName().replace( '.', '/' ) + ".class"
);
try {
indexer.index( stream );
}
catch ( IOException e ) {
// Todo which exception to throw here? (HF)
throw new HibernateException( "Unable to index" );
}
}
// Todo - take care of packages (HF)
Index index = indexer.complete();
// process the orm.xml files
final OrmXmlParser ormParser = new OrmXmlParser( this );
List<JaxbRoot<XMLEntityMappings>> mappings = new ArrayList<JaxbRoot<XMLEntityMappings>>();
for ( JaxbRoot<?> root : metadataSources.getJaxbRootList() ) {
if ( root.getRoot() instanceof XMLEntityMappings ) {
mappings.add( (JaxbRoot<XMLEntityMappings>) root );
}
}
index = ormParser.parseAndUpdateIndex( mappings, index );
final AnnotationBinder annotationBinder = new AnnotationBinder( this );
annotationBinder.bindMappedClasses( index );
}
public BasicServiceRegistry getServiceRegistry() {
return serviceRegistry;
}
public AnnotationBinder getAnnotationBinder() {
return annotationBinder;
}
public Database getDatabase() {
return database;
}

View File

@ -1,80 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, 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.metamodel.source.util.xml;
import java.io.InputStream;
import java.net.URL;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
import org.hibernate.metamodel.source.Origin;
import org.hibernate.metamodel.source.internal.JaxbRoot;
import org.hibernate.service.classloading.spi.ClassLoaderService;
/**
* @author Hardy Ferentschik
*/
public class XmlHelper {
private static final Logger log = LoggerFactory.getLogger( XmlHelper.class );
private XmlHelper() {
}
public static <T> JaxbRoot<T> unmarshallXml(String fileName, String schemaName, Class<T> clazz, ClassLoaderService classLoaderService)
throws JAXBException {
Schema schema = getMappingSchema( schemaName, classLoaderService );
InputStream in = classLoaderService.locateResourceStream( fileName );
JAXBContext jc = JAXBContext.newInstance( clazz );
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setSchema( schema );
StreamSource stream = new StreamSource( in );
JAXBElement<T> elem = unmarshaller.unmarshal( stream, clazz );
Origin origin = new Origin( null, fileName );
return new JaxbRoot<T>( elem.getValue(), origin );
}
private static Schema getMappingSchema(String schemaVersion, ClassLoaderService classLoaderService) {
URL schemaUrl = classLoaderService.locateResource( schemaVersion );
SchemaFactory sf = SchemaFactory.newInstance( javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI );
Schema schema = null;
try {
schema = sf.newSchema( schemaUrl );
}
catch ( SAXException e ) {
log.debug( "Unable to create schema for {}: {}", schemaVersion, e.getMessage() );
}
return schema;
}
}

View File

@ -23,22 +23,13 @@
*/
package org.hibernate.metamodel.binding;
import java.io.IOException;
import java.io.InputStream;
import org.jboss.jandex.Index;
import org.jboss.jandex.Indexer;
import org.junit.Test;
import org.hibernate.metamodel.source.MetadataSources;
import org.hibernate.metamodel.source.internal.MetadataImpl;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.Test;
import org.hibernate.testing.FailureExpected;
import static org.junit.Assert.fail;
/**
* Basic tests of annotation based binding code
*
@ -59,34 +50,18 @@ public class BasicAnnotationBindingTests extends AbstractBasicBindingTests {
}
public EntityBinding buildSimpleEntityBinding() {
Index index = indexForClass( SimpleEntity.class );
MetadataImpl metadata = (MetadataImpl) new MetadataSources( new ServiceRegistryBuilder().buildServiceRegistry() ).buildMetadata();
metadata.getAnnotationBinder().bindMappedClasses( index );
MetadataSources sources = new MetadataSources( new ServiceRegistryBuilder().buildServiceRegistry() );
sources.addAnnotatedClass( SimpleEntity.class );
MetadataImpl metadata = (MetadataImpl) sources.buildMetadata();
return metadata.getEntityBinding( SimpleEntity.class.getSimpleName() );
}
public EntityBinding buildSimpleVersionedEntityBinding() {
Index index = indexForClass( SimpleEntity.class );
MetadataImpl metadata = (MetadataImpl) new MetadataSources( new ServiceRegistryBuilder().buildServiceRegistry() ).buildMetadata();
metadata.getAnnotationBinder().bindMappedClasses( index );
MetadataSources sources = new MetadataSources( new ServiceRegistryBuilder().buildServiceRegistry() );
sources.addAnnotatedClass( SimpleVersionedEntity.class );
MetadataImpl metadata = (MetadataImpl) sources.buildMetadata();
return metadata.getEntityBinding( SimpleVersionedEntity.class.getSimpleName() );
}
private Index indexForClass(Class<?>... classes) {
Indexer indexer = new Indexer();
for ( Class<?> clazz : classes ) {
InputStream stream = getClass().getClassLoader().getResourceAsStream(
clazz.getName().replace( '.', '/' ) + ".class"
);
try {
indexer.index( stream );
}
catch ( IOException e ) {
fail( "Unable to index" );
}
}
return indexer.complete();
}
}

View File

@ -1,15 +1,11 @@
package org.hibernate.metamodel.source.annotations.xml;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.junit.Test;
import org.hibernate.metamodel.source.MappingException;
import org.hibernate.metamodel.source.MetadataSources;
import org.hibernate.metamodel.source.internal.MetadataImpl;
import org.hibernate.service.ServiceRegistryBuilder;
import org.hibernate.service.internal.BasicServiceRegistryImpl;
import org.hibernate.testing.junit4.BaseUnitTestCase;
/**
@ -18,20 +14,25 @@ import org.hibernate.testing.junit4.BaseUnitTestCase;
public class OrmXmlParserTests extends BaseUnitTestCase {
@Test
public void testSingleOrmXml() {
MetadataImpl metadata = (MetadataImpl) new MetadataSources( new ServiceRegistryBuilder().buildServiceRegistry() ).buildMetadata();
OrmXmlParser parser = new OrmXmlParser( metadata );
Set<String> xmlFiles = new HashSet<String>();
xmlFiles.add( "org/hibernate/metamodel/source/annotations/xml/orm.xml" );
parser.parseAndUpdateIndex( xmlFiles, null );
MetadataSources sources = new MetadataSources( new ServiceRegistryBuilder().buildServiceRegistry() );
sources.addResource( "org/hibernate/metamodel/source/annotations/xml/orm.xml" );
MetadataImpl metadata = (MetadataImpl) sources.buildMetadata();
// Todo assertions
}
@Test
public void testOrmXmlWithOldSchema() {
MetadataImpl metadata = (MetadataImpl) new MetadataSources( new ServiceRegistryBuilder().buildServiceRegistry() ).buildMetadata();
OrmXmlParser parser = new OrmXmlParser( metadata );
Set<String> xmlFiles = new HashSet<String>();
xmlFiles.add( "org/hibernate/metamodel/source/annotations/xml/orm-star.xml" );
parser.parseAndUpdateIndex( xmlFiles, null );
MetadataSources sources = new MetadataSources( new ServiceRegistryBuilder().buildServiceRegistry() );
sources.addResource( "org/hibernate/metamodel/source/annotations/xml/orm-star.xml" );
MetadataImpl metadata = (MetadataImpl) sources.buildMetadata();
// Todo assertions
}
@Test(expected = MappingException.class)
public void testInvalidOrmXmlThrowsException() {
MetadataSources sources = new MetadataSources( new ServiceRegistryBuilder().buildServiceRegistry() );
sources.addResource( "org/hibernate/metamodel/source/annotations/xml/orm-invalid.xml" );
sources.buildMetadata();
}
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="2.0">
<package>org.hibernate.metamodel.source.annotations.xml</package>
<entity class="Star" access="FIELD" metadata-complete="false" foo="bar">
<attributes>
<id name="id">
<column name="STAR_ID"/>
</id>
</attributes>
</entity>
</entity-mappings>

View File

@ -2,9 +2,7 @@
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
version="1.0"
>
version="1.0">
<!-- use orm_1_0 on purpose to test backward compatibility -->
<package>org.hibernate.metamodel.source.annotations.xml</package>
<entity class="Star" access="FIELD" metadata-complete="false">