Skip to content

Commit

Permalink
BL-425 #resolve
Browse files Browse the repository at this point in the history
When doing class serialization make sure to identify which properties have `serialize=false` on them
  • Loading branch information
lmajano committed Aug 11, 2024
1 parent e881818 commit 079ad81
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 19 deletions.
1 change: 1 addition & 0 deletions src/main/java/ortus/boxlang/runtime/scopes/Key.java
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,7 @@ public class Key implements Comparable<Key>, 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" );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<IStruct> 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
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
}

}
2 changes: 1 addition & 1 deletion src/test/bx/Person.bx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 079ad81

Please sign in to comment.