Converting binary response from JInterface Java app back into list of strings in Elixir

apostrophedottilde :

I have a small java app and I have used JInterface to essentially expose it as an OTP process in my elixir app. I can call it and get a response successfully.

My problem is that the response I get back in elixir is of a binary but I cannot figure out how to convert a binary to a list of strings which is what the response is.

The code for my OTP node in Java using JInterface is below:

public void performAction(Object requestData, OtpMbox mbox, OtpErlangPid lastPid){
    List<String> sentences = paragraphSplitter.splitParagraphIntoSentences((String) requestData, Locale.JAPAN);
    mbox.send(lastPid, new OtpErlangBinary(getOtpStrings(sentences)));
    System.out.println("OK");
}

private List<OtpErlangString> getOtpStrings(List<String> sentences) {
    List<OtpErlangString> erlangStrings = new ArrayList<>();
    for(int i = 0; i < sentences.size(); i++){
        erlangStrings.add(new OtpErlangString(sentences.get(i)));
    }
    return erlangStrings;
}

It is necessary to wrap the response in an OtpErlangBinary and I have concerted the strings to OTPErlangString. I have also tried without converting the strings to OTPErlangString.

On the elixir side I can receive the binary response and IO.inspect it.

Does anybody know how to use JInterface to deserialise the results correctly when it's anything other than a single string? Or maybe, if I have made some mistake, how to build the correct response type so that I can deserialise it correctly?

Any help would be really appreciated as I have been trying to figure this out for ages. Thanks in advance.

Christophe De Troyer :

I have been playing around with JInterface and Elixir and I think I've got your problem figured out.

So you are trying to send a list of strings from an Elixir/Erlang node to a Java node, but you cannot get it to de-serialize properly.

Elixir has its own types (e.g., atoms, tuples, ..) and Java has its own types (e.g., Object, String, List<String>,..). There needs to be a conversion from the one type to the other if they're supposed to talk to each other. In the end it's just a bunch of 1's and 0's that get sent over the wire anyway.

If an Erlang list is sent to Java, what arrives can always be interpreted as an OtpErlangObject. It's up to you to then try and guess what the actual type is before we can even begin turning it into a Java value.

// We know that everything is at least an OtpErlangObject value!
OtpErlangObject o = mbox.receive();

But given that you know that it's in fact a list, we can turn it into an OtpErlangList value.

// We know o is an Erlang list!
OtpErlangList erlList = (OtpErlangList) o;

The elements of this list however, are still unknown. So at this point its still a list of OtpErlangObjects.

But, we know that it's a list of strings, so we can interpret the list of OtpErlangObjects as list of OtpErlangStrings, and convert those to Java strings.

public static List<String> ErlangListToStringList(OtpErlangList estrs) {
    OtpErlangObject[] erlObjs = estrs.elements();
    List<String> strs = new LinkedList<String>();

    for (OtpErlangObject erlO : erlObjs) {
        strs.add(erlO.toString());
    }
    return strs;
}

Note that I used the term list here a lot, because it's in fact an Erlang list, in Java it's all represented as an array!

My entire code is listed below. The way to run this is to paste it into a Java IDE, and start a REPL with the following parameters:

iex --name [email protected] --cookie "secret"

Java part:

import com.ericsson.otp.erlang.*;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

public class Main {
    public static OtpErlangList StringListToErlangList(List<String> strs) {
        OtpErlangObject[] elems = new OtpErlangObject[strs.size()];

        int idx = 0;
        for (String str : strs) {
            elems[idx] = new OtpErlangString(str);
            idx++;
        }

        return new OtpErlangList(elems);
    }

    public static List<String> ErlangListToStringList(OtpErlangList estrs) {
        OtpErlangObject[] erlObjs = estrs.elements();
        List<String> strs = new LinkedList<String>();

        for (OtpErlangObject erlO : erlObjs) {
            strs.add(erlO.toString());
        }
        return strs;
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        // Do some initial setup.
        OtpNode node = new OtpNode("alice", "secret");
        OtpMbox mbox = node.createMbox();
        mbox.registerName("alice");

        // Check that the remote node is actually online.
        if (node.ping("[email protected]", 2000)) {
            System.out.println("remote is up");
        } else {
            System.out.println("remote is not up");
        }

        // Create the list of strings that needs to be sent to the other node.
        List<String> strs = new LinkedList<String>();
        strs.add("foo");
        strs.add("bar");
        OtpErlangList erlangStrs = StringListToErlangList(strs);

        // Create a tuple so the other node can reply to use.
        OtpErlangObject[] msg = new OtpErlangObject[2];
        msg[0] = mbox.self();
        msg[1] = erlangStrs;
        OtpErlangTuple tuple = new OtpErlangTuple(msg);

        // Send the tuple to the other node.
        mbox.send("echo", "[email protected]", tuple);

        // Await the reply.
        while (true) {
            try {
                System.out.println("Waiting for response!");
                OtpErlangObject o = mbox.receive();

                if (o instanceof OtpErlangList) {
                    OtpErlangList erlList = (OtpErlangList) o;
                    List<String> receivedStrings = ErlangListToStringList(erlList);
                    for (String s : receivedStrings) {
                        System.out.println(s);
                    }
                }
                if (o instanceof OtpErlangTuple) {
                    OtpErlangTuple m = (OtpErlangTuple) o;
                    OtpErlangPid from = (OtpErlangPid) (m.elementAt(0));
                    OtpErlangList value = (OtpErlangList) m.elementAt(1);
                    List<String> receivedStrings = ErlangListToStringList(value);

                    for (String s : receivedStrings) {
                        System.out.println(s);
                    }
                }

            } catch (OtpErlangExit otpErlangExit) {
                otpErlangExit.printStackTrace();
            } catch (OtpErlangDecodeException e) {
                e.printStackTrace();
            }
        }
    }
}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=417847&siteId=1