Skip to content

Commit 551447f

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 9ce618e commit 551447f

34 files changed

+511
-98
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
@@ -21,6 +21,7 @@
2121
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
2222
import org.hibernate.resource.jdbc.spi.StatementInspector;
2323
import org.hibernate.type.format.FormatMapper;
24+
import org.hibernate.boot.xsd.XmlValidationMode;
2425

2526
import jakarta.persistence.criteria.Nulls;
2627

@@ -746,6 +747,18 @@ public interface SessionFactoryBuilder {
746747
@Incubating
747748
SessionFactoryBuilder applyXmlFormatMapper(FormatMapper xmlFormatMapper);
748749

750+
/**
751+
* Specifies a {@link XmlValidationMode validation mode} to use for validation of XML files.
752+
*
753+
* @param xmlValidationMode The {@link XmlValidationMode} to use.
754+
*
755+
* @return {@code this}, for method chaining
756+
*
757+
* @see org.hibernate.cfg.AvailableSettings#XML_VALIDATION_MODE
758+
*/
759+
@Incubating
760+
SessionFactoryBuilder applyXmlValidationMode(XmlValidationMode xmlValidationMode);
761+
749762
/**
750763
* After all options have been set, build the SessionFactory.
751764
*
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
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+
this.resourceName = resourceName;
25+
try {
26+
bytes = inputStream.readAllBytes();
27+
}
28+
catch (IOException | OutOfMemoryError e) {
29+
throw new HibernateException( "Could not read resource " + resourceName, e );
30+
}
31+
}
32+
33+
@Override
34+
public String getStreamName() {
35+
return resourceName;
36+
}
37+
38+
@Override
39+
public InputStream accessInputStream() {
40+
return new ByteArrayInputStream( bytes );
41+
}
42+
43+
}

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
@@ -19,6 +19,7 @@
1919
import org.hibernate.boot.spi.MetadataImplementor;
2020
import org.hibernate.boot.spi.SessionFactoryBuilderImplementor;
2121
import org.hibernate.boot.spi.SessionFactoryOptions;
22+
import org.hibernate.boot.xsd.XmlValidationMode;
2223
import org.hibernate.bytecode.internal.SessionFactoryObserverForBytecodeEnhancer;
2324
import org.hibernate.bytecode.spi.BytecodeProvider;
2425
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
@@ -39,6 +39,7 @@
3939
import org.hibernate.boot.spi.BootstrapContext;
4040
import org.hibernate.boot.spi.MetadataBuildingContext;
4141
import org.hibernate.boot.spi.SessionFactoryOptions;
42+
import org.hibernate.boot.xsd.XmlValidationMode;
4243
import org.hibernate.cache.internal.NoCachingRegionFactory;
4344
import org.hibernate.cache.internal.StandardTimestampsCacheFactory;
4445
import org.hibernate.cache.spi.RegionFactory;
@@ -253,6 +254,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
253254

254255
private final int queryStatisticsMaxSize;
255256

257+
private XmlValidationMode xmlValidationMode;
256258
private final Map<String, Object> defaultSessionProperties;
257259
private final CacheStoreMode defaultCacheStoreMode;
258260
private final CacheRetrieveMode defaultCacheRetrieveMode;
@@ -534,6 +536,8 @@ public SessionFactoryOptionsBuilder(StandardServiceRegistry serviceRegistry, Boo
534536

535537
defaultLockOptions = defaultLockOptions( defaultSessionProperties );
536538
initialSessionFlushMode = defaultFlushMode( defaultSessionProperties );
539+
540+
xmlValidationMode = ConfigurationHelper.resolveXmlValidationMode( settings );
537541
}
538542

539543
private TimeZone getJdbcTimeZone(Object jdbcTimeZoneValue) {
@@ -1279,6 +1283,9 @@ public boolean isPreferJdbcDatetimeTypesInNativeQueriesEnabled() {
12791283
return preferJdbcDatetimeTypes;
12801284
}
12811285

1286+
@Override
1287+
public XmlValidationMode getXmlValidationMode() { return xmlValidationMode; }
1288+
12821289
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12831290
// In-flight mutation access
12841291

@@ -1553,7 +1560,6 @@ public void enableGeneratorNameScopeCompliance(boolean enabled) {
15531560
mutableJpaCompliance().setGeneratorNameScopeCompliance( enabled );
15541561
}
15551562

1556-
15571563
public void enableCollectionInDefaultFetchGroup(boolean enabled) {
15581564
this.collectionsInDefaultFetchGroupEnabled = enabled;
15591565
}
@@ -1562,6 +1568,10 @@ public void disableJtaTransactionAccess() {
15621568
this.jtaTransactionAccessEnabled = false;
15631569
}
15641570

1571+
public void applyXmlValidationMode(XmlValidationMode xmlValidationMode) {
1572+
this.xmlValidationMode = xmlValidationMode;
1573+
}
1574+
15651575
public SessionFactoryOptions buildOptions() {
15661576
if ( jpaCompliance instanceof MutableJpaCompliance ) {
15671577
jpaCompliance = mutableJpaCompliance().immutableCopy();

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

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,23 @@
55
package org.hibernate.boot.jaxb.internal;
66

77
import java.io.InputStream;
8+
import java.util.function.Consumer;
89
import javax.xml.stream.XMLEventReader;
910
import javax.xml.stream.XMLInputFactory;
1011
import javax.xml.stream.XMLStreamException;
1112
import javax.xml.stream.events.StartElement;
1213
import javax.xml.stream.events.XMLEvent;
1314
import javax.xml.transform.Source;
14-
import javax.xml.validation.Schema;
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
}
@@ -146,17 +151,19 @@ protected static boolean hasNamespace(StartElement startElement) {
146151
return StringHelper.isNotEmpty( startElement.getName().getNamespaceURI() );
147152
}
148153

149-
protected <X extends T> X jaxb(XMLEventReader reader, Schema xsd, JAXBContext jaxbContext, Origin origin) {
154+
protected void validateXml(Unmarshaller unmarshaller, Consumer<Unmarshaller> validationAction) {
155+
// handled by subclasses if needed/applicable
156+
validationAction.accept(unmarshaller);
157+
}
158+
159+
protected <X extends T> X jaxb(XMLEventReader reader, JAXBContext jaxbContext, Origin origin, Consumer<Unmarshaller> validationAction) {
150160
final ContextProvidingValidationEventHandler handler = new ContextProvidingValidationEventHandler();
151161

152162
try {
153163
final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
154-
if ( isValidationEnabled() ) {
155-
unmarshaller.setSchema( xsd );
156-
}
157-
else {
158-
unmarshaller.setSchema( null );
159-
}
164+
165+
validateXml( unmarshaller, validationAction );
166+
160167
unmarshaller.setEventHandler( handler );
161168

162169
//noinspection unchecked

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

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,22 @@
88
import javax.xml.stream.XMLEventReader;
99
import javax.xml.stream.events.StartElement;
1010

11+
import jakarta.xml.bind.Unmarshaller;
1112
import org.hibernate.Internal;
1213
import org.hibernate.boot.ResourceStreamLocator;
1314
import org.hibernate.boot.jaxb.Origin;
1415
import org.hibernate.boot.jaxb.configuration.spi.JaxbPersistenceImpl;
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+
import java.util.function.Consumer;
26+
2327
/**
2428
* @author Steve Ebersole
2529
*/
@@ -32,8 +36,8 @@ public ConfigurationBinder(ResourceStreamLocator resourceStreamLocator) {
3236
}
3337

3438
@Override
35-
public boolean isValidationEnabled() {
36-
return false;
39+
public XmlValidationMode getXmlValidationMode() {
40+
return XmlValidationMode.DISABLED;
3741
}
3842

3943
@Override
@@ -42,11 +46,22 @@ protected <X extends JaxbPersistenceImpl> Binding<X> doBind(
4246
StartElement rootElementStartEvent,
4347
Origin origin) {
4448
final XMLEventReader reader = new ConfigurationEventReader( staxEventReader, xmlEventFactory );
49+
50+
final Consumer<Unmarshaller> validationAction;
51+
// evaluate extended (the former validate_xml 'true') in case anyone should override getXmlValidationMode() to switch it on
52+
if ( getXmlValidationMode() == XmlValidationMode.EXTENDED ) {
53+
validationAction = unmarshaller -> unmarshaller.setSchema(
54+
ConfigXsdSupport.configurationXsd().getSchema() );
55+
}
56+
else {
57+
validationAction = unmarshaller -> unmarshaller.setSchema( null );
58+
}
59+
4560
final JaxbPersistenceImpl bindingRoot = jaxb(
4661
reader,
47-
ConfigXsdSupport.configurationXsd().getSchema(),
4862
jaxbContext(),
49-
origin
63+
origin,
64+
validationAction
5065
);
5166
//noinspection unchecked
5267
return new Binding<>( (X) bindingRoot, origin );

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 Binding doBind(Binder binder) {
3839

3940
public static Binding doBind(Binder 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)