From 9525ac7fd2f039ecb7ac0daae8a53d086b85811c Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Sat, 18 Dec 2021 23:35:49 -0500 Subject: [PATCH] HHH-14725 Fix reset handling on BlobProxy --- .../engine/jdbc/proxy/BlobProxy.java | 21 ++++ .../integration/blob/BasicBlobTest.java | 96 +++++++++++++++++++ .../orm/test/envers/integration/blob/blob.txt | 31 ++++++ 3 files changed, 148 insertions(+) create mode 100644 hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/blob/BasicBlobTest.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/blob/blob.txt diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/proxy/BlobProxy.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/proxy/BlobProxy.java index 953cf49481..7eb1069912 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/proxy/BlobProxy.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/proxy/BlobProxy.java @@ -40,6 +40,8 @@ public final class BlobProxy implements Blob, BlobImplementer { // no longer necessary. The class name could be updated to reflect this but that would break APIs. private final BinaryStream binaryStream; + private final int markBytes; + private boolean resetAllowed; private boolean needsReset; /** @@ -50,6 +52,8 @@ public final class BlobProxy implements Blob, BlobImplementer { */ private BlobProxy(byte[] bytes) { binaryStream = new ArrayBackedBinaryStream( bytes ); + markBytes = bytes.length + 1; + setStreamMark(); } /** @@ -61,6 +65,18 @@ public final class BlobProxy implements Blob, BlobImplementer { */ private BlobProxy(InputStream stream, long length) { this.binaryStream = new StreamBackedBinaryStream( stream, length ); + this.markBytes = (int) length + 1; + setStreamMark(); + } + + private void setStreamMark() { + if ( binaryStream.getInputStream().markSupported() ) { + binaryStream.getInputStream().mark( markBytes ); + resetAllowed = true; + } + else { + resetAllowed = false; + } } private InputStream getStream() throws SQLException { @@ -76,7 +92,12 @@ public final class BlobProxy implements Blob, BlobImplementer { private void resetIfNeeded() throws SQLException { try { if ( needsReset ) { + if ( !resetAllowed ) { + throw new SQLException( "Underlying stream does not allow reset" ); + } + binaryStream.getInputStream().reset(); + setStreamMark(); } } catch ( IOException ioe) { diff --git a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/blob/BasicBlobTest.java b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/blob/BasicBlobTest.java new file mode 100644 index 0000000000..1b69fe4f84 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/blob/BasicBlobTest.java @@ -0,0 +1,96 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.envers.integration.blob; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.fail; + +import java.io.BufferedInputStream; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.sql.Blob; + +import org.hamcrest.Matchers; +import org.hibernate.engine.jdbc.proxy.BlobProxy; +import org.hibernate.envers.Audited; +import org.hibernate.orm.test.envers.BaseEnversJPAFunctionalTestCase; +import org.hibernate.orm.test.envers.Priority; +import org.junit.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +/** + * @author Chris Cranford + */ +public class BasicBlobTest extends BaseEnversJPAFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Asset.class }; + } + + @Test + @Priority(10) + public void initData() { + final Path path = Path.of( getClass().getResource( "./blob.txt" ).getPath() ); + doInJPA( this::entityManagerFactory, entityManager -> { + try { + final Asset asset = new Asset(); + asset.setFileName( "blob.txt" ); + + final InputStream stream = new BufferedInputStream( Files.newInputStream( path ) ); + assertThat( stream.markSupported(), Matchers.is( true ) ); + + Blob blob = BlobProxy.generateProxy( stream, Files.size( path ) ); + + asset.setData( blob ); + entityManager.persist( asset ); + } + catch (Exception e) { + e.printStackTrace(); + fail( "Failed to persist the entity" ); + } + } ); + } + + @Audited + @Entity(name = "Asset") + public static class Asset { + @Id + @GeneratedValue + private Integer id; + private String fileName; + private Blob data; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public Blob getData() { + return data; + } + + public void setData(Blob data) { + this.data = data; + } + } + +} diff --git a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/blob/blob.txt b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/blob/blob.txt new file mode 100644 index 0000000000..7a1354040f --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/blob/blob.txt @@ -0,0 +1,31 @@ + + + + + + + create-drop + + false + false + + org.hibernate.dialect.H2Dialect + jdbc:h2:mem:envers + org.h2.Driver + sa + + + + + + + + + + +