1

I need some help to serialize an Entity to send it by Akka Remote.

This is the serializer class:

@Override
public void toBinary(Object o, ByteBuffer buf)  {

    byte[] bytes = null;
    ByteArrayOutputStream bos = null;
    ObjectOutputStream oos = null;
    try {
        bos = new ByteArrayOutputStream();
        oos = new ObjectOutputStream(bos);
        oos.writeObject(o);
        oos.flush();
        bytes = bos.toByteArray();
    }
    catch(Exception e){
        //System.out.println(e.getStackTrace());
        e.printStackTrace();
    }
    buf.put(bytes);
}

@Override
    public Object fromBinary(ByteBuffer buf, String manifest) {
        Object obj = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;

        try {
            bis = new ByteArrayInputStream(buf.array());
            ois = new ObjectInputStream(bis);
            obj = ois.readObject();
         }
        catch(Exception e){
            //System.out.println(e.getStackTrace());
            e.printStackTrace();
        }
        return obj;
    }

Im getting the following exception in line 5

java.lang.UnsupportedOperationException
    at java.nio.ByteBuffer.array(ByteBuffer.java:994)
    at serializers.ExampleByteBufSerializer.fromBinary(ExampleByteBufSerializer.java:67)
    at akka.serialization.Serialization.deserializeByteBuffer(Serialization.scala:190)
    at akka.remote.MessageSerializer$.deserializeForArtery(MessageSerializer.scala:91)
    at akka.remote.artery.Deserializer$$anon$3.onPush(Codecs.scala:620)
    at akka.stream.impl.fusing.GraphInterpreter.processPush(GraphInterpreter.scala:499)
    at akka.stream.impl.fusing.GraphInterpreter.execute(GraphInterpreter.scala:401)
    at akka.stream.impl.fusing.GraphInterpreterShell.runBatch(ActorGraphInterpreter.scala:571)
    at akka.stream.impl.fusing.GraphInterpreterShell$AsyncInput.execute(ActorGraphInterpreter.scala:457)
    at akka.stream.impl.fusing.GraphInterpreterShell.processEvent(ActorGraphInterpreter.scala:546)
    at akka.stream.impl.fusing.ActorGraphInterpreter.akka$stream$impl$fusing$ActorGraphInterpreter$$processEvent(ActorGraphInterpreter.scala:725)
    at akka.stream.impl.fusing.ActorGraphInterpreter$$anonfun$receive$1.applyOrElse(ActorGraphInterpreter.scala:740)
    at akka.actor.Actor$class.aroundReceive(Actor.scala:513)
    at akka.stream.impl.fusing.ActorGraphInterpreter.aroundReceive(ActorGraphInterpreter.scala:650)
    at akka.actor.ActorCell.receiveMessage(ActorCell.scala:527)
    at akka.actor.ActorCell.invoke(ActorCell.scala:496)
    at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:257)
    at akka.dispatch.Mailbox.run(Mailbox.scala:224)
    at akka.dispatch.Mailbox.exec(Mailbox.scala:234)
    at akka.dispatch.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
    at akka.dispatch.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
    at akka.dispatch.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
    at akka.dispatch.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)

This is the message Im sending to the remote actor:

public class Message2Serialize implements Serializable {

    String nombre;

    public Message2Serialize(String nombre) {
        this.nombre = nombre;
    }

    public Message2Serialize() {
    }

    public String getNombre() {
        return nombre;
    }


    public void setNombre(String nombre) {
        this.nombre = nombre;
    }
}

The weird thing is that it works in one way, if we send the message using this it works fine in the receptor:

ActorSelection selection = getContext().system().actorSelection("akka://applicationremote@localhost:25521/user/actors.MessageReceiverActor");
selection.tell(message2Serialize, getSelf());

But when we replay to the sender actor using getSender().tell(m, getSelf()); then we got the exception.

We are using Java 1.8 and akka-remote_2.11:2.5.3

Thanks in advance! Rodri

  • Did you try to do this without defining a custom serializer? – thwiegan Jun 27 '17 at 20:02
  • Yes, it works perfectly with Default java serializer – Rodrigo Moralez Jun 28 '17 at 12:39
  • Any reason you are using the custom one then? Afaik the default one does the same as you do. – thwiegan Jun 28 '17 at 12:40
  • its because performance. Akka Developers recommend do not use Java Serializer in production. – Rodrigo Moralez Jun 28 '17 at 13:26
  • I know, I just thought that the default one also does serialization with byte arrays. in this case there would be no improvement with your custom one. But I can't find info on what the default one is doing right now. Just fyi, I usually use akka-kryo – thwiegan Jun 28 '17 at 14:07
  • thats interesting, I think you are right. Are you using akka-kryo in production? how is the performance? What Im trying to do is to follow akka recommendation: ByteBuffer based serialization Artery introduces a new serialization mechanism which allows the ByteBufferSerializer to directly write into a shared java.nio.ByteBuffer instead of being forced to allocate and return an Array[Byte] for each serialized message. For high-throughput messaging this API change can yield significant performance benefits, so we recommend changing your serializers to use this new mechanism. – Rodrigo Moralez Jun 28 '17 at 14:42

2 Answers2

0

javadoc extract

@throws UnsupportedOperationException. If this buffer is not backed by an accessible array

the ByteBuffer seems not to be completely initialized... furthermore the javadoc also says what to do

Invoke the {@link #hasArray hasArray} method before invoking this method in order to ensure that this buffer has an accessible backing array.

André
  • 172
  • 13
  • Thats correct, the problem is that its not initialized. By using hasArray, I don't get the exception, but I don't get any data to deserialize. Its empty – Rodrigo Moralez Jun 28 '17 at 12:43
0

By changing this line: bis = newByteArrayInputStream(arr); to

byte[] arr = new byte[buf.remaining()];
buf.get(arr);
bis = new ByteArrayInputStream(arr);

It works, but Im not sure why.