From c84869bdac61a643299ebd7a39ae52e392b6f408 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Thu, 12 Jan 2017 09:30:31 -0800 Subject: [PATCH] HHH-11202 : IllegalAccessException on Embeddable ID after serializing Getter in cache key (cherry picked from commit 99a033c21c607ba0e50ef9ae935c120aaf6f7525) --- .../internal/AbstractFieldSerialForm.java | 4 +- .../property/access/spi/GetterMethodImpl.java | 4 +- .../property/access/spi/SetterMethodImpl.java | 4 +- .../GetterSetterSerializationTest.java | 136 ++++++++++++++++++ .../serialization/entity/AnEntity.java | 33 +++++ .../hibernate/serialization/entity/PK.java | 33 +++++ .../infinispan/CacheKeySerializationTest.java | 118 +++++++++++++++ .../infinispan/functional/entities/PK.java | 47 ++++++ .../functional/entities/WithEmbeddedId.java | 24 ++++ .../functional/entities/WithSimpleId.java | 24 ++++ 10 files changed, 424 insertions(+), 3 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/serialization/GetterSetterSerializationTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/serialization/entity/AnEntity.java create mode 100644 hibernate-core/src/test/java/org/hibernate/serialization/entity/PK.java create mode 100644 hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/CacheKeySerializationTest.java create mode 100644 hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/entities/PK.java create mode 100644 hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/entities/WithEmbeddedId.java create mode 100644 hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/entities/WithSimpleId.java diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/internal/AbstractFieldSerialForm.java b/hibernate-core/src/main/java/org/hibernate/property/access/internal/AbstractFieldSerialForm.java index 4088f0b1e9..3e185964ab 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/internal/AbstractFieldSerialForm.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/internal/AbstractFieldSerialForm.java @@ -31,7 +31,9 @@ public abstract class AbstractFieldSerialForm implements Serializable { protected Field resolveField() { try { - return declaringClass.getDeclaredField( fieldName ); + final Field field = declaringClass.getDeclaredField( fieldName ); + field.setAccessible( true ); + return field; } catch (NoSuchFieldException e) { throw new PropertyAccessSerializationException( diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/spi/GetterMethodImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/spi/GetterMethodImpl.java index bbd981eaee..80730f825f 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/spi/GetterMethodImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/spi/GetterMethodImpl.java @@ -121,7 +121,9 @@ public class GetterMethodImpl implements Getter { @SuppressWarnings("unchecked") private Method resolveMethod() { try { - return declaringClass.getDeclaredMethod( methodName ); + final Method method = declaringClass.getDeclaredMethod( methodName ); + method.setAccessible( true ); + return method; } catch (NoSuchMethodException e) { throw new PropertyAccessSerializationException( diff --git a/hibernate-core/src/main/java/org/hibernate/property/access/spi/SetterMethodImpl.java b/hibernate-core/src/main/java/org/hibernate/property/access/spi/SetterMethodImpl.java index 30f841bfb5..942062878e 100644 --- a/hibernate-core/src/main/java/org/hibernate/property/access/spi/SetterMethodImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/property/access/spi/SetterMethodImpl.java @@ -145,7 +145,9 @@ public class SetterMethodImpl implements Setter { @SuppressWarnings("unchecked") private Method resolveMethod() { try { - return declaringClass.getDeclaredMethod( methodName, argumentType ); + final Method method = declaringClass.getDeclaredMethod( methodName, argumentType ); + method.setAccessible( true ); + return method; } catch (NoSuchMethodException e) { throw new PropertyAccessSerializationException( diff --git a/hibernate-core/src/test/java/org/hibernate/serialization/GetterSetterSerializationTest.java b/hibernate-core/src/test/java/org/hibernate/serialization/GetterSetterSerializationTest.java new file mode 100644 index 0000000000..875119d8e2 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/serialization/GetterSetterSerializationTest.java @@ -0,0 +1,136 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.serialization; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import org.junit.Test; + +import org.hibernate.internal.util.ReflectHelper; +import org.hibernate.property.access.spi.Getter; +import org.hibernate.property.access.spi.GetterFieldImpl; +import org.hibernate.property.access.spi.GetterMethodImpl; +import org.hibernate.property.access.spi.Setter; +import org.hibernate.property.access.spi.SetterFieldImpl; +import org.hibernate.property.access.spi.SetterMethodImpl; +import org.hibernate.serialization.entity.AnEntity; +import org.hibernate.serialization.entity.PK; +import org.hibernate.testing.TestForIssue; + +import static org.junit.Assert.assertSame; + +/** + * Tests that the can access inaccessible private fields and + * inaccessible protected methods via Getter/Setter. + * + * @author Gail Badner + */ +public class GetterSetterSerializationTest { + + @Test + @TestForIssue( jiraKey = "HHH-11202") + public void testPrivateFieldGetter() throws Exception { + final AnEntity entity = new AnEntity( new PK( 1L ) ); + + final Getter getter = new GetterFieldImpl( + AnEntity.class, + "pk", + ReflectHelper.findField( AnEntity.class, "pk") + ); + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject( getter ); + + final ObjectInputStream ois = new ObjectInputStream( new ByteArrayInputStream( baos.toByteArray() ) ); + + final Getter getterClone = (Getter) ois.readObject(); + + assertSame( getter.get( entity ), getterClone.get( entity ) ); + } + + @Test + @TestForIssue( jiraKey = "HHH-11202") + public void testPrivateFieldSetter() throws Exception { + AnEntity entity = new AnEntity( new PK( 1L ) ); + + final Getter getter = new GetterFieldImpl( + AnEntity.class, + "pk", + ReflectHelper.findField( AnEntity.class, "pk") + ); + final Setter setter = new SetterFieldImpl( + AnEntity.class, + "pk", + ReflectHelper.findField( AnEntity.class, "pk") + ); + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject( setter ); + + final ObjectInputStream ois = new ObjectInputStream( new ByteArrayInputStream( baos.toByteArray() ) ); + + final Setter setterClone = (Setter) ois.readObject(); + final PK pkNew = new PK( 2L ); + setterClone.set( entity, pkNew, null ); + + assertSame( pkNew, getter.get( entity ) ); + } + + @Test + @TestForIssue( jiraKey = "HHH-11202") + public void testProtectedMethodGetter() throws Exception { + final AnEntity entity = new AnEntity( new PK( 1L ) ); + + final Getter getter = new GetterMethodImpl( + AnEntity.class, + "pk", + ReflectHelper.findGetterMethod( AnEntity.class, "pk" ) + ); + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject( getter ); + + final ObjectInputStream ois = new ObjectInputStream( new ByteArrayInputStream( baos.toByteArray() ) ); + + final Getter getterClone = (Getter) ois.readObject(); + + assertSame( getter.get( entity ), getterClone.get( entity ) ); + } + + @Test + @TestForIssue( jiraKey = "HHH-11202") + public void testProtectedMethodSetter() throws Exception { + final AnEntity entity = new AnEntity( new PK( 1L ) ); + + final Getter getter = new GetterMethodImpl( + AnEntity.class, + "pk", + ReflectHelper.findGetterMethod( AnEntity.class, "pk" ) + ); + final Setter setter = new SetterMethodImpl( + AnEntity.class, + "pk", + ReflectHelper.findSetterMethod( AnEntity.class, "pk", PK.class ) + ); + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject( setter ); + + final ObjectInputStream ois = new ObjectInputStream( new ByteArrayInputStream( baos.toByteArray() ) ); + + final Setter setterClone = (Setter) ois.readObject(); + final PK pkNew = new PK( 2L ); + setterClone.set( entity, pkNew, null ); + + assertSame( pkNew, getter.get( entity ) ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/serialization/entity/AnEntity.java b/hibernate-core/src/test/java/org/hibernate/serialization/entity/AnEntity.java new file mode 100644 index 0000000000..89080e9761 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/serialization/entity/AnEntity.java @@ -0,0 +1,33 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.serialization.entity; + +/** + * The class should be in a package that is different from the test + * so that the test does not have access to private field, + * and the protected getter and setter. + * + * @author Gail Badner + */ +public class AnEntity { + private PK pk; + + public AnEntity() { + } + + public AnEntity(PK pk) { + this.pk = pk; + } + + protected PK getPk() { + return pk; + } + + protected void setPk(PK pk) { + this.pk = pk; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/serialization/entity/PK.java b/hibernate-core/src/test/java/org/hibernate/serialization/entity/PK.java new file mode 100644 index 0000000000..3d8df2b127 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/serialization/entity/PK.java @@ -0,0 +1,33 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.serialization.entity; + +/** + * This class should be in a package that is different from the test + * so that the test and entity that uses this class for its primary + * key does not have access to private field, and the protected getter and setter. + * + * @author Gail Badner + */ +public class PK { + private Long value; + + public PK() { + } + + public PK(Long value) { + this.value = value; + } + + protected Long getValue() { + return value; + } + + protected void setValue(Long value) { + this.value = value; + } +} diff --git a/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/CacheKeySerializationTest.java b/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/CacheKeySerializationTest.java new file mode 100644 index 0000000000..b67e061a83 --- /dev/null +++ b/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/CacheKeySerializationTest.java @@ -0,0 +1,118 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.cache.infinispan; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import org.junit.Rule; +import org.junit.Test; + +import org.hibernate.cache.internal.DefaultCacheKeysFactory; +import org.hibernate.cache.internal.SimpleCacheKeysFactory; +import org.hibernate.cache.spi.CacheKeysFactory; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.jpa.AvailableSettings; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.test.cache.infinispan.functional.entities.WithEmbeddedId; +import org.hibernate.test.cache.infinispan.functional.entities.PK; +import org.hibernate.test.cache.infinispan.functional.entities.WithSimpleId; +import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup; +import org.hibernate.test.cache.infinispan.util.TestInfinispanRegionFactory; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseUnitTestCase; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @author Gail Badner + */ +public class CacheKeySerializationTest extends BaseUnitTestCase { + @Rule + public InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup(); + + private SessionFactoryImplementor getSessionFactory(String cacheKeysFactory) { + Configuration configuration = new Configuration() + .setProperty( Environment.USE_SECOND_LEVEL_CACHE, "true") + .setProperty(Environment.CACHE_REGION_FACTORY, TestInfinispanRegionFactory.class.getName()) + .setProperty(Environment.DEFAULT_CACHE_CONCURRENCY_STRATEGY, "transactional") + .setProperty( AvailableSettings.SHARED_CACHE_MODE, "ALL") + .setProperty(Environment.HBM2DDL_AUTO, "create-drop"); + if (cacheKeysFactory != null) { + configuration.setProperty(Environment.CACHE_KEYS_FACTORY, cacheKeysFactory); + } + configuration.addAnnotatedClass( WithSimpleId.class ); + configuration.addAnnotatedClass( WithEmbeddedId.class ); + return (SessionFactoryImplementor) configuration.buildSessionFactory(); + } + + @Test + @TestForIssue(jiraKey = "HHH-11202") + public void testSimpleCacheKeySimpleId() throws Exception { + testId( SimpleCacheKeysFactory.INSTANCE, WithSimpleId.class.getName(), 1L ); + } + + @Test + @TestForIssue(jiraKey = "HHH-11202") + public void testSimpleCacheKeyEmbeddedId() throws Exception { + testId( SimpleCacheKeysFactory.INSTANCE, WithEmbeddedId.class.getName(), new PK( 1L ) ); + } + + @Test + @TestForIssue(jiraKey = "HHH-11202") + public void testDefaultCacheKeySimpleId() throws Exception { + testId( DefaultCacheKeysFactory.INSTANCE, WithSimpleId.class.getName(), 1L ); + } + + @Test + @TestForIssue(jiraKey = "HHH-11202") + public void testDefaultCacheKeyEmbeddedId() throws Exception { + testId( DefaultCacheKeysFactory.INSTANCE, WithEmbeddedId.class.getName(), new PK( 1L ) ); + } + + private void testId(CacheKeysFactory cacheKeysFactory, String entityName, Object id) throws Exception { + final SessionFactoryImplementor sessionFactory = getSessionFactory( cacheKeysFactory.getClass().getName() ); + final EntityPersister persister = sessionFactory.getEntityPersister( entityName ); + final Object key = cacheKeysFactory.createEntityKey( + id, + persister, + sessionFactory, + null + ); + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject( key ); + + final ObjectInputStream ois = new ObjectInputStream( new ByteArrayInputStream( baos.toByteArray() ) ); + final Object keyClone = ois.readObject(); + + try { + assertEquals( key, keyClone ); + assertEquals( keyClone, key ); + + assertEquals( key.hashCode(), keyClone.hashCode() ); + + final Object idClone = cacheKeysFactory.getEntityId( keyClone ); + + assertEquals( id.hashCode(), idClone.hashCode() ); + assertEquals( id, idClone ); + assertEquals( idClone, id ); + assertTrue( persister.getIdentifierType().isEqual( id, idClone, sessionFactory ) ); + assertTrue( persister.getIdentifierType().isEqual( idClone, id, sessionFactory ) ); + sessionFactory.close(); + } + finally { + sessionFactory.close(); + } + } +} diff --git a/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/entities/PK.java b/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/entities/PK.java new file mode 100644 index 0000000000..3cec299cf0 --- /dev/null +++ b/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/entities/PK.java @@ -0,0 +1,47 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.cache.infinispan.functional.entities; + +import java.io.Serializable; + +/** + * This class should be in a package that is different from the test + * so that the test and entity that uses this class for its primary + * key does not have access to private field. + * + * @author Gail Badner + */ +public class PK implements Serializable { + private Long id; + + public PK() { + } + + public PK(Long id) { + this.id = id; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + PK pk = (PK) o; + + return !( id != null ? !id.equals( pk.id ) : pk.id != null ); + + } + + @Override + public int hashCode() { + return id != null ? id.hashCode() : 0; + } +} diff --git a/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/entities/WithEmbeddedId.java b/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/entities/WithEmbeddedId.java new file mode 100644 index 0000000000..a9fc270fed --- /dev/null +++ b/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/entities/WithEmbeddedId.java @@ -0,0 +1,24 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.cache.infinispan.functional.entities; + +import javax.persistence.Cacheable; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; + +/** + * The class should be in a package that is different from the test + * so that the test does not have access to the private embedded ID. + * + * @author Gail Badner + */ +@Entity +@Cacheable +public class WithEmbeddedId { + @EmbeddedId + private PK embeddedId; +} diff --git a/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/entities/WithSimpleId.java b/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/entities/WithSimpleId.java new file mode 100644 index 0000000000..9e7723b9a0 --- /dev/null +++ b/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/entities/WithSimpleId.java @@ -0,0 +1,24 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.cache.infinispan.functional.entities; + +import javax.persistence.Cacheable; +import javax.persistence.Entity; +import javax.persistence.Id; + +/** + * The class should be in a package that is different from the test + * so that the test does not have access to the private ID field. + * + * @author Gail Badner + */ +@Entity +@Cacheable +public class WithSimpleId { + @Id + private Long id; +}