Skip to content

Commit 8c5bafa

Browse files
committed
HHH-18455 - Change Binder.bind(InputStream, Origin) method signature to bind(InputStreamAccess, Origin) to allow repeatable access to the InputStream, needed for strict Jpa XML validation
HHH-18455 - Implement option to run strict JPA compliance validation Signed-off-by: Jan Schatteman <[email protected]>
1 parent 88f2a06 commit 8c5bafa

35 files changed

+513
-95
lines changed

hibernate-core/src/main/java/org/hibernate/boot/SessionFactoryBuilder.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
2323
import org.hibernate.resource.jdbc.spi.StatementInspector;
2424
import org.hibernate.type.format.FormatMapper;
25+
import org.hibernate.boot.xsd.XmlValidationMode;
2526

2627
import jakarta.persistence.criteria.Nulls;
2728

@@ -755,6 +756,18 @@ public interface SessionFactoryBuilder {
755756
@Incubating
756757
SessionFactoryBuilder applyXmlFormatMapper(FormatMapper xmlFormatMapper);
757758

759+
/**
760+
* Specifies a {@link XmlValidationMode validation mode} to use for validation of XML files.
761+
*
762+
* @param xmlValidationMode The {@link XmlValidationMode} to use.
763+
*
764+
* @return {@code this}, for method chaining
765+
*
766+
* @see org.hibernate.cfg.AvailableSettings#XML_VALIDATION_MODE
767+
*/
768+
@Incubating
769+
SessionFactoryBuilder applyXmlValidationMode(XmlValidationMode xmlValidationMode);
770+
758771
/**
759772
* After all options have been set, build the SessionFactory.
760773
*
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.boot.archive.internal;
6+
7+
8+
import org.hibernate.HibernateException;
9+
import org.hibernate.boot.archive.spi.InputStreamAccess;
10+
11+
import java.io.ByteArrayInputStream;
12+
import java.io.IOException;
13+
import java.io.InputStream;
14+
15+
/**
16+
* @author Jan Schatteman
17+
*/
18+
public class RepeatableInputStreamAccess implements InputStreamAccess {
19+
20+
private final String resourceName;
21+
private byte[] bytes = new byte[0];
22+
23+
public RepeatableInputStreamAccess(String resourceName, InputStream inputStream) {
24+
if ( inputStream == null ) {
25+
throw new HibernateException( "InputStream is null for resource " + resourceName );
26+
}
27+
this.resourceName = resourceName;
28+
try {
29+
bytes = inputStream.readAllBytes();
30+
}
31+
catch (IOException | OutOfMemoryError e) {
32+
throw new HibernateException( "Could not read resource " + resourceName, e );
33+
}
34+
}
35+
36+
@Override
37+
public String getStreamName() {
38+
return resourceName;
39+
}
40+
41+
@Override
42+
public InputStream accessInputStream() {
43+
return new ByteArrayInputStream( bytes );
44+
}
45+
46+
}

hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryBuilderImpl.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.hibernate.boot.spi.MetadataImplementor;
1919
import org.hibernate.boot.spi.SessionFactoryBuilderImplementor;
2020
import org.hibernate.boot.spi.SessionFactoryOptions;
21+
import org.hibernate.boot.xsd.XmlValidationMode;
2122
import org.hibernate.bytecode.internal.SessionFactoryObserverForBytecodeEnhancer;
2223
import org.hibernate.bytecode.spi.BytecodeProvider;
2324
import org.hibernate.cache.spi.TimestampsCacheFactory;
@@ -431,6 +432,12 @@ public void disableJtaTransactionAccess() {
431432
this.optionsBuilder.disableJtaTransactionAccess();
432433
}
433434

435+
@Override
436+
public SessionFactoryBuilder applyXmlValidationMode(XmlValidationMode xmlValidationMode) {
437+
this.optionsBuilder.applyXmlValidationMode( xmlValidationMode );
438+
return this;
439+
}
440+
434441
@Override
435442
public SessionFactory build() {
436443
return new SessionFactoryImpl( metadata, buildSessionFactoryOptions(), bootstrapContext );

hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.hibernate.boot.spi.BootstrapContext;
4343
import org.hibernate.boot.spi.MetadataBuildingContext;
4444
import org.hibernate.boot.spi.SessionFactoryOptions;
45+
import org.hibernate.boot.xsd.XmlValidationMode;
4546
import org.hibernate.cache.internal.NoCachingRegionFactory;
4647
import org.hibernate.cache.internal.StandardTimestampsCacheFactory;
4748
import org.hibernate.cache.spi.RegionFactory;
@@ -240,6 +241,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
240241

241242
private final int queryStatisticsMaxSize;
242243

244+
private XmlValidationMode xmlValidationMode;
243245
private final Map<String, Object> defaultSessionProperties;
244246
private final CacheStoreMode defaultCacheStoreMode;
245247
private final CacheRetrieveMode defaultCacheRetrieveMode;
@@ -538,6 +540,8 @@ public SessionFactoryOptionsBuilder(StandardServiceRegistry serviceRegistry, Boo
538540

539541
defaultLockOptions = defaultLockOptions( defaultSessionProperties );
540542
initialSessionFlushMode = defaultFlushMode( defaultSessionProperties );
543+
544+
xmlValidationMode = ConfigurationHelper.resolveXmlValidationMode( settings );
541545
}
542546

543547
@Deprecated(forRemoval = true)
@@ -1409,6 +1413,9 @@ public boolean isPreferJdbcDatetimeTypesInNativeQueriesEnabled() {
14091413
return preferJdbcDatetimeTypes;
14101414
}
14111415

1416+
@Override
1417+
public XmlValidationMode getXmlValidationMode() { return xmlValidationMode; }
1418+
14121419
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14131420
// In-flight mutation access
14141421

@@ -1686,7 +1693,6 @@ public void enableGeneratorNameScopeCompliance(boolean enabled) {
16861693
mutableJpaCompliance().setGeneratorNameScopeCompliance( enabled );
16871694
}
16881695

1689-
16901696
public void enableCollectionInDefaultFetchGroup(boolean enabled) {
16911697
this.collectionsInDefaultFetchGroupEnabled = enabled;
16921698
}
@@ -1695,6 +1701,10 @@ public void disableJtaTransactionAccess() {
16951701
this.jtaTransactionAccessEnabled = false;
16961702
}
16971703

1704+
public void applyXmlValidationMode(XmlValidationMode xmlValidationMode) {
1705+
this.xmlValidationMode = xmlValidationMode;
1706+
}
1707+
16981708
public SessionFactoryOptions buildOptions() {
16991709
if ( jpaCompliance instanceof MutableJpaCompliance mutableJpaCompliance ) {
17001710
jpaCompliance = mutableJpaCompliance.immutableCopy();

hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/AbstractBinder.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515

1616
import org.hibernate.boot.MappingException;
1717
import org.hibernate.boot.ResourceStreamLocator;
18+
import org.hibernate.boot.archive.spi.InputStreamAccess;
1819
import org.hibernate.boot.jaxb.Origin;
1920
import org.hibernate.boot.jaxb.internal.stax.BufferedXMLEventReader;
2021
import org.hibernate.boot.jaxb.internal.stax.LocalXmlResourceResolver;
2122
import org.hibernate.boot.jaxb.spi.Binder;
2223
import org.hibernate.boot.jaxb.spi.Binding;
24+
import org.hibernate.boot.xsd.XmlValidationMode;
2325
import org.hibernate.internal.util.StringHelper;
2426

2527
import org.jboss.logging.Logger;
@@ -36,15 +38,18 @@ public abstract class AbstractBinder<T> implements Binder<T> {
3638

3739
private final LocalXmlResourceResolver xmlResourceResolver;
3840

41+
protected InputStreamAccess streamAccess;
42+
3943
protected AbstractBinder(ResourceStreamLocator resourceStreamLocator) {
4044
this.xmlResourceResolver = new LocalXmlResourceResolver( resourceStreamLocator );
4145
}
4246

43-
public abstract boolean isValidationEnabled();
47+
public abstract XmlValidationMode getXmlValidationMode();
4448

4549
@Override
46-
public <X extends T> Binding<X> bind(InputStream stream, Origin origin) {
47-
final XMLEventReader eventReader = createReader( stream, origin );
50+
public <X extends T> Binding<X> bind(InputStreamAccess streamAccess, Origin origin) {
51+
this.streamAccess = streamAccess;
52+
final XMLEventReader eventReader = createReader( streamAccess.accessInputStream(), origin );
4853
try {
4954
return doBind( eventReader, origin );
5055
}
@@ -150,12 +155,7 @@ protected <X extends T> X jaxb(XMLEventReader reader, Schema xsd, JAXBContext ja
150155

151156
try {
152157
final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
153-
if ( isValidationEnabled() ) {
154-
unmarshaller.setSchema( xsd );
155-
}
156-
else {
157-
unmarshaller.setSchema( null );
158-
}
158+
unmarshaller.setSchema( xsd );
159159
unmarshaller.setEventHandler( handler );
160160

161161
//noinspection unchecked
@@ -172,5 +172,4 @@ protected <X extends T> X jaxb(XMLEventReader reader, Schema xsd, JAXBContext ja
172172
}
173173
}
174174

175-
176175
}

hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/ConfigurationBinder.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import javax.xml.stream.XMLEventFactory;
88
import javax.xml.stream.XMLEventReader;
99
import javax.xml.stream.events.StartElement;
10+
import javax.xml.validation.Schema;
1011

1112
import org.hibernate.Internal;
1213
import org.hibernate.boot.ResourceStreamLocator;
@@ -15,11 +16,13 @@
1516
import org.hibernate.boot.jaxb.internal.stax.ConfigurationEventReader;
1617
import org.hibernate.boot.jaxb.spi.Binding;
1718
import org.hibernate.boot.xsd.ConfigXsdSupport;
19+
import org.hibernate.boot.xsd.XmlValidationMode;
1820
import org.hibernate.internal.util.config.ConfigurationException;
1921

2022
import jakarta.xml.bind.JAXBContext;
2123
import jakarta.xml.bind.JAXBException;
2224

25+
2326
/**
2427
* @author Steve Ebersole
2528
*/
@@ -32,8 +35,8 @@ public ConfigurationBinder(ResourceStreamLocator resourceStreamLocator) {
3235
}
3336

3437
@Override
35-
public boolean isValidationEnabled() {
36-
return false;
38+
public XmlValidationMode getXmlValidationMode() {
39+
return XmlValidationMode.DISABLED;
3740
}
3841

3942
@Override
@@ -42,9 +45,19 @@ protected <X extends JaxbPersistenceImpl> Binding<X> doBind(
4245
StartElement rootElementStartEvent,
4346
Origin origin) {
4447
final XMLEventReader reader = new ConfigurationEventReader( staxEventReader, xmlEventFactory );
48+
49+
final Schema xsd;
50+
// evaluate extended (the former validate_xml 'true') in case anyone should override getXmlValidationMode() to switch it on
51+
if ( getXmlValidationMode() == XmlValidationMode.EXTENDED ) {
52+
xsd = ConfigXsdSupport.configurationXsd().getSchema();
53+
}
54+
else {
55+
xsd = null;
56+
}
57+
4558
final JaxbPersistenceImpl bindingRoot = jaxb(
4659
reader,
47-
ConfigXsdSupport.configurationXsd().getSchema(),
60+
xsd,
4861
jaxbContext(),
4962
origin
5063
);

hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/InputStreamAccessXmlSource.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ public Binding doBind(Binder binder) {
2727
}
2828

2929
public static Binding doBind(Binder binder, InputStreamAccess inputStreamAccess, Origin origin) {
30-
return inputStreamAccess.fromStream(
31-
inputStream -> binder.bind( inputStream, origin )
32-
);
30+
return binder.bind( inputStreamAccess, origin ) ;
3331
}
3432
}

hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/InputStreamXmlSource.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.io.InputStream;
99

1010
import org.hibernate.boot.InvalidMappingException;
11+
import org.hibernate.boot.archive.internal.RepeatableInputStreamAccess;
1112
import org.hibernate.boot.jaxb.Origin;
1213
import org.hibernate.boot.jaxb.spi.Binder;
1314
import org.hibernate.boot.jaxb.spi.Binding;
@@ -38,7 +39,7 @@ public <T> Binding<T> doBind(Binder<T> binder) {
3839

3940
public static <T> Binding<T> doBind(Binder<T> binder, InputStream inputStream, Origin origin, boolean autoClose) {
4041
try {
41-
return binder.bind( inputStream, origin );
42+
return binder.bind( new RepeatableInputStreamAccess( origin.getName(), inputStream), origin );
4243
}
4344
catch ( Exception e ) {
4445
throw new InvalidMappingException( origin, e );

0 commit comments

Comments
 (0)