HHH-4667 accept orm 2 and orm 1 files. Also improved error reports

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18263 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Emmanuel Bernard 2009-12-18 10:02:26 +00:00
parent cab9873205
commit a87e031bd5
4 changed files with 122 additions and 35 deletions

View File

@ -26,6 +26,7 @@ package org.hibernate.cfg;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.StringReader;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@ -54,6 +55,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.ErrorHandler;
import org.hibernate.AnnotationException; import org.hibernate.AnnotationException;
import org.hibernate.DuplicateMappingException; import org.hibernate.DuplicateMappingException;
@ -67,6 +70,7 @@ import org.hibernate.annotations.common.reflection.MetadataProviderInjector;
import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XClass; import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.java.JavaReflectionManager; import org.hibernate.annotations.common.reflection.java.JavaReflectionManager;
import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.cfg.annotations.Version; import org.hibernate.cfg.annotations.Version;
import org.hibernate.cfg.annotations.reflection.JPAMetadataProvider; import org.hibernate.cfg.annotations.reflection.JPAMetadataProvider;
import org.hibernate.cfg.beanvalidation.BeanValidationActivator; import org.hibernate.cfg.beanvalidation.BeanValidationActivator;
@ -839,32 +843,76 @@ public class AnnotationConfiguration extends Configuration {
@Override @Override
public AnnotationConfiguration addInputStream(InputStream xmlInputStream) throws MappingException { public AnnotationConfiguration addInputStream(InputStream xmlInputStream) throws MappingException {
try { try {
List errors = new ArrayList(); /*
SAXReader saxReader = xmlHelper.createSAXReader( "XML InputStream", errors, getEntityResolver() ); * try and parse the document:
try { * - try and validate the document with orm_2_0.xsd
saxReader.setFeature( "http://apache.org/xml/features/validation/schema", true ); * - if it fails because of the version attribute mismatch, try and validate the document with orm_1_0.xsd
//saxReader.setFeature( "http://apache.org/xml/features/validation/dynamic", true ); */
//set the default schema locators List<SAXParseException> errors = new ArrayList<SAXParseException>();
saxReader.setProperty( SAXReader saxReader = new SAXReader( );
"http://apache.org/xml/properties/schema/external-schemaLocation", saxReader.setEntityResolver( getEntityResolver() );
"http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd" saxReader.setErrorHandler( new ErrorLogger(errors) );
); saxReader.setMergeAdjacentText(true);
} saxReader.setValidation(true);
catch ( SAXException e ) {
saxReader.setValidation( false );
}
org.dom4j.Document doc = saxReader
.read( new InputSource( xmlInputStream ) );
setValidationFor( saxReader, "orm_2_0.xsd" );
org.dom4j.Document doc = null;
try {
doc = saxReader.read( new InputSource( xmlInputStream ) );
}
catch ( DocumentException e ) {
//the document is syntactically incorrect
//DOM4J sometimes wraps the SAXParseException wo much interest
final Throwable throwable = e.getCause();
if ( e.getCause() == null || !( throwable instanceof SAXParseException ) ) {
throw new MappingException( "Could not parse JPA mapping document", e );
}
errors.add( (SAXParseException) throwable );
}
boolean isV1Schema = false;
if ( errors.size() != 0 ) { if ( errors.size() != 0 ) {
throw new MappingException( "invalid mapping", ( Throwable ) errors.get( 0 ) ); SAXParseException exception = errors.get( 0 );
final String errorMessage = exception.getMessage();
//does the error look like a schema mismatch?
isV1Schema = doc != null
&& errorMessage.contains("1.0")
&& errorMessage.contains("2.0")
&& errorMessage.contains("version");
}
if (isV1Schema) {
//reparse with v1
errors.clear();
setValidationFor( saxReader, "orm_1_0.xsd" );
try {
//too bad we have to reparse to validate again :(
saxReader.read( new StringReader( doc.asXML() ) );
}
catch ( DocumentException e ) {
//oops asXML fails even if the core doc parses initially
new AssertionFailure("Error in DOM4J leads to a bug in Hibernate", e);
}
}
if ( errors.size() != 0 ) {
//report errors in exception
StringBuilder errorMessage = new StringBuilder( );
for (SAXParseException error : errors) {
errorMessage.append("Error parsing XML (line")
.append(error.getLineNumber())
.append(" : column ")
.append(error.getColumnNumber())
.append("): ")
.append(error.getMessage())
.append("\n");
}
throw new MappingException( "Invalid ORM mapping file.\n" + errorMessage.toString() );
} }
add( doc ); add( doc );
return this; return this;
} }
catch ( DocumentException e ) {
throw new MappingException( "Could not parse mapping document in input stream", e );
}
finally { finally {
try { try {
xmlInputStream.close(); xmlInputStream.close();
@ -875,6 +923,40 @@ public class AnnotationConfiguration extends Configuration {
} }
} }
private static class ErrorLogger implements ErrorHandler {
private List<SAXParseException> errors;
public ErrorLogger(List<SAXParseException> errors) {
this.errors = errors;
}
public void warning(SAXParseException exception) throws SAXException {
errors.add( exception );
}
public void error(SAXParseException exception) throws SAXException {
errors.add( exception );
}
public void fatalError(SAXParseException exception) throws SAXException {
}
}
private void setValidationFor(SAXReader saxReader, String xsd) {
try {
saxReader.setFeature( "http://apache.org/xml/features/validation/schema", true );
//saxReader.setFeature( "http://apache.org/xml/features/validation/dynamic", true );
//set the default schema locators
saxReader.setProperty(
"http://apache.org/xml/properties/schema/external-schemaLocation",
"http://java.sun.com/xml/ns/persistence/orm " + xsd
);
}
catch ( SAXException e ) {
saxReader.setValidation( false );
}
}
public SessionFactory buildSessionFactory() throws HibernateException { public SessionFactory buildSessionFactory() throws HibernateException {
enableLegacyHibernateValidator(); enableLegacyHibernateValidator();
enableBeanValidation(); enableBeanValidation();

View File

@ -1,9 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="1.0" version="2.0">
>
<!-- no grammar specified should pass --> <!-- no grammar specified should pass -->
<persistence-unit-metadata> <persistence-unit-metadata>
<persistence-unit-defaults> <persistence-unit-defaults>

View File

@ -2,9 +2,8 @@
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm/orm_2_0.xsd"
version="1.0" version="2.0">
>
<package>org.hibernate.test.annotations.xml.ejb3</package> <package>org.hibernate.test.annotations.xml.ejb3</package>
<entity class="Lighter" access="FIELD" metadata-complete="true"> <entity class="Lighter" access="FIELD" metadata-complete="true">
<attributes> <attributes>

View File

@ -68,6 +68,11 @@ public final class PersistenceXmlLoader {
} }
private static Document loadURL(URL configURL, EntityResolver resolver) throws Exception { private static Document loadURL(URL configURL, EntityResolver resolver) throws Exception {
/*
* try and parse the document:
* - try and validate the document with persistence_2_0.xsd
* - if it fails because of the version attribute mismatch, try and validate the document with persistence_1_0.xsd
*/
InputStream is = null; InputStream is = null;
if (configURL != null) { if (configURL != null) {
URLConnection conn = configURL.openConnection(); URLConnection conn = configURL.openConnection();
@ -75,8 +80,9 @@ public final class PersistenceXmlLoader {
is = conn.getInputStream(); is = conn.getInputStream();
} }
if ( is == null ) { if ( is == null ) {
throw new IOException( "Failed to obtain InputStream from url: " + configURL ); throw new IOException( "Failed to obtain InputStream while reading persistence.xml file: " + configURL );
} }
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
docBuilderFactory.setNamespaceAware( true ); docBuilderFactory.setNamespaceAware( true );
final Schema v2Schema = SchemaFactory.newInstance( javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI ) final Schema v2Schema = SchemaFactory.newInstance( javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI )
@ -91,6 +97,8 @@ public final class PersistenceXmlLoader {
docBuilder.setEntityResolver( resolver ); docBuilder.setEntityResolver( resolver );
List<SAXParseException> errors = new ArrayList<SAXParseException>(); List<SAXParseException> errors = new ArrayList<SAXParseException>();
Document doc = null; Document doc = null;
//first sparse document and collect syntaxic errors
try { try {
doc = docBuilder.parse( source ); doc = docBuilder.parse( source );
} }
@ -99,7 +107,7 @@ public final class PersistenceXmlLoader {
} }
if (errors.size() == 0) { if (errors.size() == 0) {
v2Validator.setErrorHandler( new ErrorLogger( "XML InputStream", errors, resolver ) ); v2Validator.setErrorHandler( new ErrorLogger( errors ) );
log.trace("Validate with persistence_2_0.xsd schema on file {}", configURL); log.trace("Validate with persistence_2_0.xsd schema on file {}", configURL);
v2Validator.validate( new DOMSource( doc ) ); v2Validator.validate( new DOMSource( doc ) );
boolean isV1Schema = false; boolean isV1Schema = false;
@ -108,6 +116,7 @@ public final class PersistenceXmlLoader {
log.trace("Found error with persistence_2_0.xsd schema on file {}", configURL); log.trace("Found error with persistence_2_0.xsd schema on file {}", configURL);
SAXParseException exception = errors.get( 0 ); SAXParseException exception = errors.get( 0 );
final String errorMessage = exception.getMessage(); final String errorMessage = exception.getMessage();
//is it a validation error due to a v1 schema validated by a v2
isV1Schema = errorMessage.contains("1.0") isV1Schema = errorMessage.contains("1.0")
&& errorMessage.contains("2.0") && errorMessage.contains("2.0")
&& errorMessage.contains("version"); && errorMessage.contains("version");
@ -116,11 +125,12 @@ public final class PersistenceXmlLoader {
if (isV1Schema) { if (isV1Schema) {
log.trace("Validate with persistence_1_0.xsd schema on file {}", configURL); log.trace("Validate with persistence_1_0.xsd schema on file {}", configURL);
errors.clear(); errors.clear();
v1Validator.setErrorHandler( new ErrorLogger( "XML InputStream", errors, resolver ) ); v1Validator.setErrorHandler( new ErrorLogger( errors ) );
v1Validator.validate( new DOMSource( doc ) ); v1Validator.validate( new DOMSource( doc ) );
} }
} }
if ( errors.size() != 0 ) { if ( errors.size() != 0 ) {
//report all errors in the exception
StringBuilder errorMessage = new StringBuilder( ); StringBuilder errorMessage = new StringBuilder( );
for (SAXParseException error : errors) { for (SAXParseException error : errors) {
errorMessage.append("Error parsing XML (line") errorMessage.append("Error parsing XML (line")
@ -310,17 +320,14 @@ public final class PersistenceXmlLoader {
} }
public static class ErrorLogger implements ErrorHandler { public static class ErrorLogger implements ErrorHandler {
private String file; private List<SAXParseException> errors;
private List errors;
private EntityResolver resolver;
ErrorLogger(String file, List errors, EntityResolver resolver) { ErrorLogger(List<SAXParseException> errors) {
this.file = file;
this.errors = errors; this.errors = errors;
this.resolver = resolver;
} }
public void error(SAXParseException error) { public void error(SAXParseException error) {
//what was this commented code about?
// if ( resolver instanceof EJB3DTDEntityResolver ) { // if ( resolver instanceof EJB3DTDEntityResolver ) {
// if ( ( (EJB3DTDEntityResolver) resolver ).isResolved() == false ) return; // if ( ( (EJB3DTDEntityResolver) resolver ).isResolved() == false ) return;
// } // }