From 079ad817408685421001f1746043502a6f0122df Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Sat, 10 Aug 2024 19:15:00 -0500 Subject: [PATCH] BL-425 #resolve When doing class serialization make sure to identify which properties have `serialize=false` on them --- .../ortus/boxlang/runtime/scopes/Key.java | 1 + .../util/conversion/BoxClassState.java | 52 ++++++++++++++++--- .../util/conversion/ObjectMarshaller.java | 6 +-- src/test/bx/Person.bx | 2 +- ...zerTest.java => ObjectMarshallerTest.java} | 18 ++++--- 5 files changed, 60 insertions(+), 19 deletions(-) rename src/test/java/ortus/boxlang/runtime/bifs/global/system/{ObjectSerializerTest.java => ObjectMarshallerTest.java} (89%) diff --git a/src/main/java/ortus/boxlang/runtime/scopes/Key.java b/src/main/java/ortus/boxlang/runtime/scopes/Key.java index 56a246087..e0c72cd27 100644 --- a/src/main/java/ortus/boxlang/runtime/scopes/Key.java +++ b/src/main/java/ortus/boxlang/runtime/scopes/Key.java @@ -568,6 +568,7 @@ public class Key implements Comparable, Serializable { public static final Key second = Key.of( "second" ); public static final Key seconds = Key.of( "seconds" ); public static final Key secure = Key.of( "secure" ); + public static final Key serializable = Key.of( "serializable" ); public static final Key seed = Key.of( "seed" ); public static final Key seekable = Key.of( "seekable" ); public static final Key separator = Key.of( "separator" ); diff --git a/src/main/java/ortus/boxlang/runtime/util/conversion/BoxClassState.java b/src/main/java/ortus/boxlang/runtime/util/conversion/BoxClassState.java index f99495ca8..27ed2937d 100644 --- a/src/main/java/ortus/boxlang/runtime/util/conversion/BoxClassState.java +++ b/src/main/java/ortus/boxlang/runtime/util/conversion/BoxClassState.java @@ -19,12 +19,15 @@ import java.io.ObjectStreamException; import java.io.Serializable; +import java.util.Optional; import ortus.boxlang.runtime.BoxRuntime; import ortus.boxlang.runtime.context.IBoxContext; +import ortus.boxlang.runtime.dynamic.casters.BooleanCaster; import ortus.boxlang.runtime.loader.ClassLocator; import ortus.boxlang.runtime.runnables.IClassRunnable; import ortus.boxlang.runtime.scopes.Key; +import ortus.boxlang.runtime.types.Array; import ortus.boxlang.runtime.types.Function; import ortus.boxlang.runtime.types.IStruct; import ortus.boxlang.runtime.types.Struct; @@ -56,23 +59,60 @@ public class BoxClassState implements Serializable { /** * Constructor + * + * @param target The target class to serialize */ - public BoxClassState( Key classPath, IStruct variablesScope, IStruct thisScope ) { - this.classPath = classPath; + public BoxClassState( IClassRunnable target ) { + // Store the class path + this.classPath = target.getName(); + // Get the metadata properties to see which ones + // are NOT serializable + Array aProperties = target.getBoxMeta().getMeta().getAsArray( Key.properties ); - variablesScope.entrySet() + // Serialize the variables and this scope + target.getVariablesScope() + .entrySet() .stream() - // Filter out any functions, we won't serialize those. + // Filter out any functions, we won't serialize those for now, unless + // We figure out how to recreate them. .filter( entry -> ! ( entry.getValue() instanceof Function ) ) + // Filter out non-serializable properties + .filter( entry -> isSerializable( aProperties, entry.getKey() ) ) .forEach( entry -> this.variablesScope.put( entry.getKey(), entry.getValue() ) ); - thisScope.entrySet() + target.getThisScope() + .entrySet() .stream() - // Filter out any functions, we won't serialize those. .filter( entry -> ! ( entry.getValue() instanceof Function ) ) .forEach( entry -> this.thisScope.put( entry.getKey(), entry.getValue() ) ); } + /** + * Check if a property is serializable + * + * @param properties The metadata properties + * @param property The property to check + * + * @return True if the property is serializable + */ + private Boolean isSerializable( Array properties, Key property ) { + Optional propertyMetadata = properties + .stream() + .map( IStruct.class::cast ) + .filter( prop -> prop.getAsKey( Key.nameAsKey ).equals( property ) ) + .findFirst(); + + if ( propertyMetadata.isPresent() ) { + // Check if the property is serializable + return BooleanCaster.cast( + propertyMetadata.get() + .getAsStruct( Key.annotations ) + .getOrDefault( Key.serializable, true ) + ); + } + return true; + } + /** * This method is called to return a new instance of Target Class after deserialization * diff --git a/src/main/java/ortus/boxlang/runtime/util/conversion/ObjectMarshaller.java b/src/main/java/ortus/boxlang/runtime/util/conversion/ObjectMarshaller.java index 4dc3f5aec..8074856a6 100644 --- a/src/main/java/ortus/boxlang/runtime/util/conversion/ObjectMarshaller.java +++ b/src/main/java/ortus/boxlang/runtime/util/conversion/ObjectMarshaller.java @@ -153,11 +153,7 @@ public static Object deserialize( IBoxContext context, byte[] data ) { */ public static Object serializeClass( IClassRunnable target ) { // Create the state of the class - return new BoxClassState( - target.getName(), - target.getVariablesScope(), - target.getThisScope() - ); + return new BoxClassState( target ); } } diff --git a/src/test/bx/Person.bx b/src/test/bx/Person.bx index d81b14fc1..24e7f0388 100644 --- a/src/test/bx/Person.bx +++ b/src/test/bx/Person.bx @@ -6,7 +6,7 @@ class{ property String name; property String surname; - property numeric age; + property numeric age serializable=false; property Date createdDate; property Date modifiedDate; property boolean isActive; diff --git a/src/test/java/ortus/boxlang/runtime/bifs/global/system/ObjectSerializerTest.java b/src/test/java/ortus/boxlang/runtime/bifs/global/system/ObjectMarshallerTest.java similarity index 89% rename from src/test/java/ortus/boxlang/runtime/bifs/global/system/ObjectSerializerTest.java rename to src/test/java/ortus/boxlang/runtime/bifs/global/system/ObjectMarshallerTest.java index bc557a9ed..4375c760f 100644 --- a/src/test/java/ortus/boxlang/runtime/bifs/global/system/ObjectSerializerTest.java +++ b/src/test/java/ortus/boxlang/runtime/bifs/global/system/ObjectMarshallerTest.java @@ -34,7 +34,7 @@ import ortus.boxlang.runtime.scopes.VariablesScope; import ortus.boxlang.runtime.types.Query; -public class ObjectSerializerTest { +public class ObjectMarshallerTest { static BoxRuntime instance; static IBoxContext context; @@ -112,15 +112,19 @@ public void testSerializationDeserializationBxClasses() { """ // Class person = new src.test.bx.Person() - person.setName( "Luis" ); - person.setSurname( "Majano" ); - serialized = objectSerialize( person ); + person.setName( "Luis" ) + person.setSurname( "Majano" ) + person.setAge( 1 ) + println( person.toJson() ) - result = objectDeserialize( serialized ); + serialized = objectSerialize( person ) + result = objectDeserialize( serialized ) println( result.toJson() ) - assert result.getName() == "Luis"; - assert result.getSurname() == "Majano"; + assert result.getName() == "Luis" + assert result.getSurname() == "Majano" + // Should be null, since it is not serializable + assert result.getAge() == null """, context ); // @formatter:on