Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[core][sre] Ensure API events are fully serializable.
Signed-off-by: Stéphane Galland <galland@arakhne.org>
  • Loading branch information
gallandarakhneorg committed Nov 8, 2018
1 parent d0c3150 commit d3eaaec
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 11 deletions.
3 changes: 2 additions & 1 deletion main/apiplugins/io.sarl.core/META-INF/MANIFEST.MF
Expand Up @@ -6,5 +6,6 @@ Bundle-Version: 0.9.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Name: %Bundle-Name
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Require-Bundle: io.sarl.lang.core;bundle-version="0.9.0";visibility:=reexport
Require-Bundle: io.sarl.lang.core;bundle-version="0.9.0";visibility:=reexport,
io.sarl.util;bundle-version="0.9.0"
Export-Package: io.sarl.core
6 changes: 4 additions & 2 deletions main/apiplugins/io.sarl.core/src/io/sarl/core/events.sarl
Expand Up @@ -20,12 +20,14 @@
*/
package io.sarl.core

import java.util.Arrays
import java.util.Collection
import java.util.Collections
import java.util.UUID
import io.sarl.lang.^annotation.EarlyExit
import io.sarl.lang.core.SpaceID
import io.sarl.lang.core.Address
import io.sarl.util.Collections3

/**
* Initialization event.
Expand Down Expand Up @@ -160,7 +162,7 @@ final event AgentSpawned {
*/
new (source : Address, agentType : String, agentID : UUID*) {
setSource(source)
this.agentIdentifiers = newImmutableList(agentID)
this.agentIdentifiers = Collections::unmodifiableCollection(Arrays::asList(agentID))
this.agentID = agentID.get(0)
this.agentType = agentType
}
Expand All @@ -173,7 +175,7 @@ final event AgentSpawned {
*/
new (source : Address, agentType : String, agentID : Collection<UUID>) {
setSource(source)
this.agentIdentifiers = Collections::unmodifiableCollection(agentID)
this.agentIdentifiers = Collections::unmodifiableCollection(Collections3::serializableCollection(agentID))
this.agentID = agentID.iterator.next
this.agentType = agentType
}
Expand Down
112 changes: 109 additions & 3 deletions main/apiplugins/io.sarl.util/src/io/sarl/util/Collections3.sarl
Expand Up @@ -23,9 +23,12 @@ package io.sarl.util
import io.sarl.lang.util.SynchronizedCollection
import io.sarl.lang.util.SynchronizedIterable
import io.sarl.lang.util.SynchronizedSet
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.io.Serializable
import java.util.AbstractCollection
import java.util.AbstractSet
import java.util.ArrayList
import java.util.Collection
import java.util.Collections
import java.util.Iterator
Expand Down Expand Up @@ -59,7 +62,7 @@ final class Collections3 {
*/
private static class EmptySet extends AbstractSet<Object> implements SynchronizedSet<Object>, Serializable {

val mutex = new Object
transient var mutex = new Object

package new {
}
Expand All @@ -76,6 +79,21 @@ final class Collections3 {
0
}

@SuppressWarnings("unused_private_member")
private def writeObject(stream : ObjectOutputStream) {
synchronized (this.mutex) {
stream.defaultWriteObject
}
}

@SuppressWarnings("unused_private_member")
private def readObject(stream : ObjectInputStream) {
synchronized (this.mutex) {
stream.defaultReadObject
this.mutex = new Object
}
}

}

/** Copied from Collections.
Expand All @@ -94,7 +112,7 @@ final class Collections3 {

/** Object on which to synchronize.
*/
package val mutex : Object
package transient var mutex : Object

new (collection : Collection<E>, mutex : Object) {
this.collection = collection
Expand Down Expand Up @@ -206,6 +224,14 @@ final class Collections3 {
}
}

@SuppressWarnings("unused_private_member")
private def readObject(stream : ObjectInputStream) {
synchronized (this.mutex) {
stream.defaultReadObject
this.mutex = new Object
}
}

override mutex : Object {
this.mutex
}
Expand Down Expand Up @@ -565,6 +591,7 @@ final class Collections3 {
static def unmodifiableSynchronizedIterable(elements : Iterable<T>, mutex : Object) : SynchronizedIterable<T> with T {
new UnmodifiableSynchronizedIterableWrapper(elements, mutex)
}

/** Unmodifiable synchronized iterable.
*
* @param <E> the type of the elements in the collection.
Expand All @@ -577,7 +604,7 @@ final class Collections3 {

val iterable : Iterable<E>

val mutex : Object
transient var mutex : Object

package new (iterable : Iterable<E>, mutex : Object) {
this.iterable = iterable
Expand All @@ -592,5 +619,84 @@ final class Collections3 {
return this.mutex
}

@SuppressWarnings("unused_private_member")
private def writeObject(stream : ObjectOutputStream) {
synchronized (this.mutex) {
stream.defaultWriteObject
}
}

@SuppressWarnings("unused_private_member")
private def readObject(stream : ObjectInputStream) {
synchronized (this.mutex) {
stream.defaultReadObject
this.mutex = new Object
}
}

}

/** Replies a serializable collection.
* If the wrapped collection is serializable, it is replied itself without wrapping.
* If the wrapped collection is not serializable, it is wrapped into a collection
* that supports serialization of the collection elements.
*
* @param <T> - type of the elements in the collection.
* @param elements the set to transform as unmodifiable collection.
* @return an unmodifiable synchronized set.
* @since 0.8.6
*/
static def serializableCollection(elements : Collection<T>) : Collection<T> with T {
if (elements instanceof Serializable) {
return elements
}
return new SerializableCollectionWrapper(elements)
}

/** Unmodifiable synchronized iterable.
*
* @param <E> the type of the elements in the collection.
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 0.8.6
*/
private static class SerializableCollectionWrapper<E> extends AbstractCollection<E> implements Serializable {

transient var collection : Collection<E>

package new (collection : Collection<E>) {
this.collection = collection
}

override iterator : Iterator<E> {
this.collection.iterator
}

override size : int {
this.collection.size
}

@SuppressWarnings("unused_private_member")
private def writeObject(stream : ObjectOutputStream) {
stream.defaultWriteObject
stream.writeInt(collection.size);
for (element : this.collection) {
stream.writeObject(element);
}
}

@SuppressWarnings("unused_private_member")
private def readObject(stream : ObjectInputStream) {
stream.defaultReadObject
val s = stream.readInt
this.collection = new ArrayList(s)
for (var i = 0; i < s; i++) {
this.collection.add(stream.readObject as E)
}
}

}

}
Expand Up @@ -62,6 +62,8 @@
*/
public class JavaBinaryEventSerializer extends AbstractEventSerializer {

private static final String SPACE_SPECIFICATION_HEADER = "x-java-spacespec-class"; //$NON-NLS-1$

/**
* Constructs an GsonEventSerializer. The {@link EventEncrypter} is injected.
*
Expand All @@ -84,13 +86,18 @@ public EventEnvelope serialize(EventDispatch dispatch) throws Exception {

final Map<String, String> headers = dispatch.getCustomHeaders();
assert headers != null;
headers.put("x-java-spacespec-class", //$NON-NLS-1$
headers.put(SPACE_SPECIFICATION_HEADER,
spaceID.getSpaceSpecification().getName());

final Scope<?> scope = dispatch.getScope();

final EventEnvelope envelope = new EventEnvelope(NetworkUtil.toByteArray(spaceID.getContextID()),
NetworkUtil.toByteArray(spaceID.getID()), toBytes(scope), toBytes(dispatch.getCustomHeaders()), toBytes(event));
final byte[] serializedContextID = NetworkUtil.toByteArray(spaceID.getContextID());
final byte[] serializedSpaceID = NetworkUtil.toByteArray(spaceID.getID());
final byte[] serializedScope = toBytes(scope);
final byte[] serializedHeaders = toBytes(dispatch.getCustomHeaders());
final byte[] serializedEvent = toBytes(event);
final EventEnvelope envelope = new EventEnvelope(
serializedContextID, serializedSpaceID, serializedScope, serializedHeaders, serializedEvent);

this.encrypter.encrypt(envelope);

Expand All @@ -117,7 +124,7 @@ public EventDispatch deserialize(EventEnvelope envelope) throws Exception {
assert headers != null;

Class<?> spaceSpec = null;
final String classname = headers.get("x-java-spacespec-class"); //$NON-NLS-1$
final String classname = headers.get(SPACE_SPECIFICATION_HEADER);
if (classname != null) {
try {
spaceSpec = Class.forName(classname);
Expand Down
Expand Up @@ -223,7 +223,8 @@ protected void fireAgentSpawnedOutsideAgent(UUID spawningAgent, AgentContext con
final UUID spawner = spawningAgent == null ? context.getID() : spawningAgent;
final Address source = new Address(defSpace.getSpaceID(), spawner);
assert source != null;
final Collection<UUID> spawnedAgentIds = Collections2.transform(agents, it -> it.getID());
final Collection<UUID> spawnedAgentIds = Collections3.serializableCollection(
Collections2.transform(agents, it -> it.getID()));
final AgentSpawned event = new AgentSpawned(source, agentClazz.getName(), spawnedAgentIds);
final Scope<Address> scope = address -> {
final UUID receiver = address.getUUID();
Expand Down

0 comments on commit d3eaaec

Please sign in to comment.