npgall / mobility-rpc

Add Code Mobility to any application
Apache License 2.0
32 stars 12 forks source link

Add support for sending lambdas #12

Open Adam5Wu opened 8 years ago

Adam5Wu commented 8 years ago
public class TestClient {
    public static void main(String[] argv) {
        try {
            QuickTask.execute("LocalHost", (Runnable) () -> {
                throw new Error("Test");
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Triggers exception message on the server:

Jan 25, 2016 9:38:41 PM com.googlecode.mobilityrpc.session.impl.MobilitySessionImpl receiveIncomingExecutionRequest
SEVERE: Unexpected exception processing execution task, for connection id: 127.0.0.1:64933:0, execution request: ExecutionRequest{serializedExecutableObject=111 bytes, serializationFormat=KRYO, executionMode=RETURN_RESPONSE, requestIdentifier=RequestIdentifier{sessionId=4aa76093-2798-425f-9dbc-07131e49f1dc, requestId=c41303bd-08e2-4127-8ee5-38eac2ac9e70, requestLabel='<no label>'}}
java.lang.IllegalStateException: Failed to process execution request, for connection id: 127.0.0.1:64933:0, execution request: ExecutionRequest{serializedExecutableObject=111 bytes, serializationFormat=KRYO, executionMode=RETURN_RESPONSE, requestIdentifier=RequestIdentifier{sessionId=4aa76093-2798-425f-9dbc-07131e49f1dc, requestId=c41303bd-08e2-4127-8ee5-38eac2ac9e70, requestLabel='<no label>'}}
    at com.googlecode.mobilityrpc.session.impl.MobilitySessionImpl.receiveIncomingExecutionRequest(MobilitySessionImpl.java:371)
    at com.googlecode.mobilityrpc.protocol.processors.impl.ExecutionRequestMessageProcessor.process(ExecutionRequestMessageProcessor.java:39)
    at com.googlecode.mobilityrpc.protocol.processors.impl.ExecutionRequestMessageProcessor.process(ExecutionRequestMessageProcessor.java:31)
    at com.googlecode.mobilityrpc.controller.impl.MobilityControllerImpl$MessageProcessorTask.processMessage(MobilityControllerImpl.java:128)
    at com.googlecode.mobilityrpc.controller.impl.MobilityControllerImpl$MessageProcessorTask.run(MobilityControllerImpl.java:108)
    at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.IllegalStateException: Exception deserializing object from 111 bytes data in KRYO format
    at com.googlecode.mobilityrpc.session.impl.MobilitySessionImpl.deserialize(MobilitySessionImpl.java:514)
    at com.googlecode.mobilityrpc.session.impl.MobilitySessionImpl.receiveIncomingExecutionRequest(MobilitySessionImpl.java:295)
    ... 9 more
Caused by: com.esotericsoftware.kryo.KryoException: Unable to find class: TestClient$$Lambda$1/1599771323
Serialization trace:
wrapped (com.googlecode.mobilityrpc.quickstart.QuickTask$SessionReleasingRunnable)
    at com.esotericsoftware.kryo.util.DefaultClassResolver.readName(DefaultClassResolver.java:156)
    at com.esotericsoftware.kryo.util.DefaultClassResolver.readClass(DefaultClassResolver.java:133)
    at com.esotericsoftware.kryo.Kryo.readClass(Kryo.java:670)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:118)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:551)
    at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:790)
    at com.googlecode.mobilityrpc.serialization.impl.KryoSerializer.deserialize(KryoSerializer.java:88)
    at com.googlecode.mobilityrpc.session.impl.MobilitySessionImpl.deserialize(MobilitySessionImpl.java:508)
    ... 10 more
Caused by: java.lang.ClassNotFoundException: TestClient$$Lambda$1/1599771323
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Unknown Source)
    at com.esotericsoftware.kryo.util.DefaultClassResolver.readName(DefaultClassResolver.java:154)
    ... 17 more

However, if a formal inheritance class is made like the test code in #11, then the error is gone.

npgall commented 8 years ago

Sending Java 8 lambdas to remote machines is not officially supported yet. At the moment Mobility-RPC targets Java 6 and later, but some newer language features are not guaranteed to work.

I'm going to rename this issue to Add support for sending lambdas, as I want to add support for this.

BrynCooke commented 8 years ago

Hi, I have in the past investigated serialization of lambdas. It basically works out of the box if Java serialization is used and the interface that the lambda is implementing is serializable. So the probable solution to this is to allow the user of Java serialization instead of Kryo.

BrynCooke commented 8 years ago

The other solution is to enable lambda serialization in Kryo: https://github.com/magro/kryo/blob/bf803974ba6351d993093636aac00245f1ec28bc/test/com/esotericsoftware/kryo/serializers/Java8ClosureSerializerTest.java#L52 This could be done with reflection to prevent a dependency on Java8.

npgall commented 8 years ago

I'm not too concerned about Java 6 compatibility anymore. And I think Java 8 lambdas are quite a natural way to send tasks to remote machines.

@BrynCooke I like the second option, to enable Java 8 closure support in Kryo. If you have time to try that out by modifying the KryoSerializer class in mobility-rpc and testing locally, it would help. I'm totally open to merging pull requests which would help with this!

The main challenge I can envisage is that currently the mobility-rpc protocol supports sending only Runnable or Callable objects. Any other type of object to be sent (including lambdas) need to be wrapped in one of these. So the easiest solution if we want to send a lambda, is to write a helper method which wraps the lambda in a Runnable or Callable object. If this is possible then it should not be a problem to send a lambda.

BrynCooke commented 8 years ago

Thanks, I'll create a PR.