Skip to content

Commit

Permalink
hibernate#87 - Make sure models are serializable
Browse files Browse the repository at this point in the history
  • Loading branch information
sebersole committed Oct 15, 2024
1 parent b9468ed commit d777a16
Show file tree
Hide file tree
Showing 36 changed files with 572 additions and 197 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package org.hibernate.models.jandex.internal;

import java.lang.annotation.Annotation;
import java.util.Map;

import org.hibernate.models.internal.AnnotationDescriptorRegistryStandard;
import org.hibernate.models.spi.AnnotationDescriptor;
Expand All @@ -24,4 +25,8 @@ protected <A extends Annotation> AnnotationDescriptor<A> buildAnnotationDescript
AnnotationDescriptor<? extends Annotation> containerDescriptor) {
return new JandexAnnotationDescriptorImpl<>( javaType, containerDescriptor, getModelBuildingContext() );
}

public Map<Class<? extends Annotation>, AnnotationDescriptor<? extends Annotation>> getDescriptorMap() {
return descriptorMap;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
import java.util.List;

import org.hibernate.models.internal.ClassDetailsSupport;
import org.hibernate.models.internal.SerialCassDetails;
import org.hibernate.models.internal.jdk.SerialJdkCassDetails;
import org.hibernate.models.internal.jdk.SerialJdkClassDetails;
import org.hibernate.models.internal.util.CollectionHelper;
import org.hibernate.models.serial.spi.SerialClassDetails;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.FieldDetails;
import org.hibernate.models.spi.MethodDetails;
Expand Down Expand Up @@ -280,8 +280,7 @@ private static List<TypeVariableDetails> determineTypeParameters(ClassInfo class
}

@Override
public SerialCassDetails toSerialForm(SourceModelBuildingContext context) {
final Class<Object> classForName = context.getClassLoading().classForName( getClassName() );
return new SerialJdkCassDetails( classForName.getName(), classForName );
public SerialClassDetails toStorableForm() {
return new SerialJdkClassDetails( getName(), toJavaClass() );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
*/
package org.hibernate.models.jandex.internal;

import java.util.Map;

import org.hibernate.models.UnknownClassException;
import org.hibernate.models.internal.AbstractClassDetailsRegistry;
import org.hibernate.models.internal.jdk.JdkBuilders;
Expand Down Expand Up @@ -50,4 +52,8 @@ protected ClassDetails createClassDetails(String name) {

throw new UnknownClassException( "Unable to resolve ClassDetails for `" + name + "`" );
}

protected Map<String, ClassDetails> getClassDetailsMap() {
return classDetailsMap;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import org.hibernate.models.jandex.spi.JandexModelBuildingContext;
import org.hibernate.models.jandex.spi.JandexValueConverter;
import org.hibernate.models.jandex.spi.JandexValueExtractor;
import org.hibernate.models.serial.internal.StorableContextImpl;
import org.hibernate.models.serial.spi.StorableContext;
import org.hibernate.models.spi.ClassLoading;
import org.hibernate.models.spi.RegistryPrimer;
import org.hibernate.models.spi.ValueTypeDescriptor;
Expand Down Expand Up @@ -98,4 +100,9 @@ public <V> JandexValueExtractor<V> getJandexValueExtractor(ValueTypeDescriptor<V
this
);
}

@Override
public StorableContext toStorableForm() {
return new StorableContextImpl( classDetailsRegistry.getClassDetailsMap(), descriptorRegistry.getDescriptorMap() );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.models.serialization;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.hibernate.models.SerializationHelper;
import org.hibernate.models.SourceModelTestHelper;
import org.hibernate.models.serial.spi.StorableContext;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.SourceModelBuildingContext;
import org.hibernate.models.spi.SourceModelContext;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.models.internal.SimpleClassLoading.SIMPLE_CLASS_LOADING;

public class SimpleSerializationTests {
protected SourceModelBuildingContext createModelContext(Class<?>... classes) {
return SourceModelTestHelper.createBuildingContext( classes );
}

@Test
void serializeSimpleClass() {
final SourceModelBuildingContext buildingContext = createModelContext( SimpleClass.class );

final ClassDetails classDetails = buildingContext.getClassDetailsRegistry().findClassDetails( SimpleClass.class.getName() );
assertThat( classDetails ).isNotNull();

final StorableContext serialContext = buildingContext.toStorableForm();
final StorableContext clonedSerialContext = SerializationHelper.clone( serialContext );
assertThat( serialContext ).isNotSameAs( clonedSerialContext );

final SourceModelContext restored = clonedSerialContext.fromStorableForm( SIMPLE_CLASS_LOADING );
assertThat( buildingContext ).isNotSameAs( restored );
assertThat( buildingContext.getClassDetailsRegistry() ).isNotSameAs( restored.getClassDetailsRegistry() );
assertThat( buildingContext.getAnnotationDescriptorRegistry() ).isNotSameAs( restored.getAnnotationDescriptorRegistry() );

final ClassDetails cloneCassDetails = restored.getClassDetailsRegistry().findClassDetails( SimpleClass.class.getName() );
assertThat( cloneCassDetails ).isNotNull();
assertThat( classDetails ).isNotSameAs( cloneCassDetails );
}

@Test
void serializeSimpleClassWithMembers() {
final SourceModelBuildingContext buildingContext = createModelContext( SimpleClassWithMembers.class );

final ClassDetails classDetails = buildingContext.getClassDetailsRegistry().findClassDetails( SimpleClassWithMembers.class.getName() );
assertThat( classDetails ).isNotNull();
assertThat( classDetails.getFields() ).hasSize( 1 );
assertThat( classDetails.getMethods() ).hasSize( 3 );

final StorableContext serialContext = buildingContext.toStorableForm();
final StorableContext clonedSerialContext = SerializationHelper.clone( serialContext );
assertThat( serialContext ).isNotSameAs( clonedSerialContext );

final SourceModelContext restored = clonedSerialContext.fromStorableForm( SIMPLE_CLASS_LOADING );
assertThat( buildingContext ).isNotSameAs( restored );
assertThat( buildingContext.getClassDetailsRegistry() ).isNotSameAs( restored.getClassDetailsRegistry() );
assertThat( buildingContext.getAnnotationDescriptorRegistry() ).isNotSameAs( restored.getAnnotationDescriptorRegistry() );

final ClassDetails cloneCassDetails = restored.getClassDetailsRegistry().findClassDetails( SimpleClassWithMembers.class.getName() );
assertThat( cloneCassDetails ).isNotNull();
assertThat( classDetails ).isNotSameAs( cloneCassDetails );
assertThat( cloneCassDetails.getFields() ).hasSize( 1 );
assertThat( cloneCassDetails.getMethods() ).hasSize( 3 );
}

@Test
void serializeSimpleClassWithAnnotations() {
final SourceModelBuildingContext buildingContext = createModelContext( SimpleClassWithAnnotations.class );

final ClassDetails classDetails = buildingContext.getClassDetailsRegistry().findClassDetails( SimpleClassWithAnnotations.class.getName() );
assertThat( classDetails ).isNotNull();
assertThat( classDetails.getDirectAnnotationUsages() ).hasSize( 1 );
assertThat( classDetails.getFields() ).hasSize( 1 );
assertThat( classDetails.getFields().iterator().next().getDirectAnnotationUsages() ).hasSize( 1 );
assertThat( classDetails.getMethods() ).hasSize( 1 );
assertThat( classDetails.getMethods().iterator().next().getDirectAnnotationUsages() ).hasSize( 1 );

final StorableContext serialContext = buildingContext.toStorableForm();
final StorableContext clonedSerialContext = SerializationHelper.clone( serialContext );
assertThat( serialContext ).isNotSameAs( clonedSerialContext );

final SourceModelContext restored = clonedSerialContext.fromStorableForm( SIMPLE_CLASS_LOADING );
assertThat( restored ).isNotNull();
assertThat( buildingContext ).isNotSameAs( restored );
assertThat( buildingContext.getClassDetailsRegistry() ).isNotSameAs( restored.getClassDetailsRegistry() );
assertThat( buildingContext.getAnnotationDescriptorRegistry() ).isNotSameAs( restored.getAnnotationDescriptorRegistry() );

final ClassDetails cloneCassDetails = restored.getClassDetailsRegistry().findClassDetails( SimpleClassWithAnnotations.class.getName() );
assertThat( classDetails ).isNotSameAs( cloneCassDetails );
assertThat( cloneCassDetails.getDirectAnnotationUsages() ).hasSize( 1 );
assertThat( cloneCassDetails.getFields() ).hasSize( 1 );
assertThat( cloneCassDetails.getFields().iterator().next().getDirectAnnotationUsages() ).hasSize( 1 );
assertThat( cloneCassDetails.getMethods() ).hasSize( 1 );
assertThat( cloneCassDetails.getMethods().iterator().next().getDirectAnnotationUsages() ).hasSize( 1 );

}

public static class SimpleClass {
}

public static class SimpleClassWithMembers {
public int anInt;

public int getAnInt() {
return anInt;
}

public void setAnInt(int anInt) {
this.anInt = anInt;
}

public void doStuff() {
}
}

@Target({ ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnAnnotation {
}

@AnAnnotation
public static class SimpleClassWithAnnotations {
@AnAnnotation
private int anInt;

@AnAnnotation
public int getAnInt() {
return anInt;
}

}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.models;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
* @author Steve Ebersole
*/
public abstract class AbstractAnnotationDescriptorRegistry implements AnnotationDescriptorRegistry {
protected final Map<Class<? extends Annotation>, AnnotationDescriptor<?>> descriptorMap;
protected final Map<AnnotationDescriptor<?>, AnnotationDescriptor<?>> repeatableByContainerMap;
protected final Map<Class<? extends Annotation>, AnnotationDescriptor<? extends Annotation>> descriptorMap;
protected final Map<AnnotationDescriptor<? extends Annotation>, AnnotationDescriptor<? extends Annotation>> repeatableByContainerMap;

public AbstractAnnotationDescriptorRegistry() {
this( new ConcurrentHashMap<>(), new ConcurrentHashMap<>() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,8 @@
*/
package org.hibernate.models.internal;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Annotation;
import java.lang.annotation.Repeatable;
import java.util.Map;

import org.hibernate.models.spi.AnnotationDescriptor;
import org.hibernate.models.spi.SourceModelBuildingContext;
Expand Down Expand Up @@ -89,20 +85,4 @@ protected <A extends Annotation> AnnotationDescriptor<A> buildAnnotationDescript
modelBuildingContext
);
}

public void serialize(ObjectOutputStream outputStream, BasicModelBuildingContextImpl context) throws IOException {
outputStream.writeInt( descriptorMap.size() );
for ( Map.Entry<Class<? extends Annotation>, AnnotationDescriptor<?>> entry : descriptorMap.entrySet() ) {
outputStream.writeObject( entry.getValue().toSerialForm( context ) );
}
}

public void deserialize(ObjectInputStream inputStream, SourceModelBuildingContext context) throws IOException, ClassNotFoundException {
final int count = inputStream.readInt();
for ( int i = 0; i < count; i++ ) {
final SerialAnnotationDescriptor<?> serialForm = (SerialAnnotationDescriptor<?>) inputStream.readObject();
register( serialForm.fromSerialForm( context ) );
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,8 @@
*/
package org.hibernate.models.internal;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serial;

import org.hibernate.models.spi.ClassDetailsBuilder;
import org.hibernate.models.serial.internal.StorableContextImpl;
import org.hibernate.models.serial.spi.StorableContext;
import org.hibernate.models.spi.ClassLoading;
import org.hibernate.models.spi.RegistryPrimer;

Expand All @@ -19,8 +15,8 @@
* @author Steve Ebersole
*/
public class BasicModelBuildingContextImpl extends AbstractModelBuildingContext {
private transient AnnotationDescriptorRegistryStandard descriptorRegistry;
private transient ClassDetailsRegistryStandard classDetailsRegistry;
private final AnnotationDescriptorRegistryStandard descriptorRegistry;
private final ClassDetailsRegistryStandard classDetailsRegistry;

public BasicModelBuildingContextImpl(ClassLoading classLoadingAccess) {
this( classLoadingAccess, null );
Expand All @@ -45,23 +41,8 @@ public MutableClassDetailsRegistry getClassDetailsRegistry() {
return classDetailsRegistry;
}

@Serial
private void writeObject(ObjectOutputStream outputStream) throws IOException {
outputStream.writeObject( classDetailsRegistry.getClassDetailsBuilder() );

descriptorRegistry.serialize( outputStream, this );
classDetailsRegistry.serialize( outputStream, this );

outputStream.flush();
}


@Serial
private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
descriptorRegistry = new AnnotationDescriptorRegistryStandard( this );
classDetailsRegistry = new ClassDetailsRegistryStandard( (ClassDetailsBuilder) inputStream.readObject(), this );

descriptorRegistry.deserialize( inputStream, this );
classDetailsRegistry.deserialize( inputStream, this );
@Override
public StorableContext toStorableForm() {
return new StorableContextImpl( classDetailsRegistry.classDetailsMap, descriptorRegistry.descriptorMap );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@
package org.hibernate.models.internal;


import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Map;

import org.hibernate.models.internal.jdk.JdkBuilders;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.ClassDetailsBuilder;
Expand Down Expand Up @@ -42,21 +37,4 @@ public ClassDetailsRegistryStandard(ClassDetailsBuilder classDetailsBuilder, Sou
protected ClassDetailsBuilder getClassDetailsBuilder() {
return classDetailsBuilder;
}

public void serialize(ObjectOutputStream outputStream, SourceModelBuildingContext context) throws IOException {
outputStream.writeInt( classDetailsMap.size() );
for ( Map.Entry<String, ClassDetails> entry : classDetailsMap.entrySet() ) {
outputStream.writeUTF( entry.getKey() );
outputStream.writeObject( entry.getValue().toSerialForm( context ) );
}
}

public void deserialize(ObjectInputStream inputStream, SourceModelBuildingContext context) throws IOException, ClassNotFoundException {
final int count = inputStream.readInt();
for ( int i = 0; i < count; i++ ) {
final String registrationName = inputStream.readUTF();
final SerialCassDetails serialForm = (SerialCassDetails) inputStream.readObject();
addClassDetails( registrationName, serialForm.fromSerialForm( context ) );
}
}
}
Loading

0 comments on commit d777a16

Please sign in to comment.