McTheels

McTheels

Call Java from Elixir

Good day,

I am trying to make use of a Java class that handles encryption and decryption. How can I call the member function of that class from a .java file in Elixir?

Thanks!

Most Liked

mindok

mindok

Ports are the way to go - search for Erlang Java ports (either here or google). When I’m back at the PC I’ll dig something up if you don’t manage to get anywhere.

But yes, if it’s standard encryption it would be less hassle and likely more reliable in deployment and operation to rewrite in Elixir.

mindok

mindok

Ok,

Here’s an ugly PoC chunk of java code. I have extracted out the bits that are sensitive so it almost certainly won’t work as-is, but hopefully it gives you some pointers. You will need to install the Ericsson otp java package (see the import statement at the top).

There were a couple of gotchas:

  1. Marshalling data types - strings, doubles, dates etc all need to be explicitly marshalled between built-in Java datatypes and otp ready data types. I wrote quite a few helper functions to do this.
  2. The Ericsson package fails to emit a required byte to the stream of bytes sent back to the port, so the sendResponse method has the required hack to make it work.
  3. Note the warnings from @jayjun in Killing java processes started from Port.open - #10 by jayjun re: port buffer deadlocks. I haven’t handled that potential issue as it was good enough for our PoC as-is

Don’t pick on my java code - I hadn’t written any since 2005 before this.

package com.mindok;

import java.io.*;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


import com.ericsson.otp.erlang.*;

public class Main {

    static SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd' 'HH:mm");

    public static void main(String[] args) throws Exception {
        OtpErlangObject payload = null;

        while(true) {
            try {
                payload = receivePayload();
            } catch (Exception e) {
                sendResponse(toOtpString("Receive " + e.getStackTrace()));
                break;
            }

            if (payload == null) {
                sendResponse(toOtpString("No payload received - exiting"));
                break;
            }

            OtpErlangObject response = null;
            try {
                response = processPayload(payload);
            } catch (Exception e) {
                sendResponse(toOtpString("Process " + e.getMessage()));
                Object[] ste = e.getStackTrace();
                for (int i=0; i < 5; ++i) {
                    sendResponse(toOtpString("Process " + ste[i]));
                }
                break;
            }
            if (response == null) {
                sendResponse(toOtpString("No response received - exiting"));
                break;
            }

            try {
                sendResponse(response);
            } catch (Exception e) {
                sendResponse(toOtpString("Send " + e.getStackTrace()));
                break;
            }
        }

    }

    private static void sendResponse(OtpErlangObject response) throws IOException {
        OtpOutputStream otpOutputStream = new OtpOutputStream(response);
        byte[] output = otpOutputStream.toByteArray();

        System.out.write(encodeLength(output.length + 1)); // Add one for the header byte added below
        System.out.write((byte) 131); // for some reason the encoding doesn't include the very thing that identifies it as Erlang ETF
        System.out.write(output);
    }

    private static byte[] encodeLength(int length) {
        ByteBuffer bb = ByteBuffer.allocate(4);
        bb.putInt(length);
        return bb.array();
    }

    private static OtpErlangObject processPayload(OtpErlangObject payload) {
        OtpErlangTuple tuple = (OtpErlangTuple)payload;
        OtpErlangAtom command = (OtpErlangAtom)tuple.elementAt(0);
        OtpErlangObject commandParameters = tuple.elementAt(1);

        switch (command.atomValue()) {
            case "open_file" :
                return packageOutput("open_file", openFile(commandParameters));

            case "something_else" :
                // *** etc 

            default:
                return payload;
        }
    }

    private static OtpErlangObject openFile(OtpErlangObject commandParameters) {
        String outcome = "ok";
        String msg = "Successfully read ";
        String fileName = "";
        try {
            fileName = new String(((OtpErlangBinary)commandParameters).binaryValue());
            msg = "Successfully read " + fileName;
        } catch (Exception e) {
            outcome = "error";
            msg = "Error reading file: " + e.getMessage();
        }

        if (outcome != "error") {
            try {
                // ***  Do something with file that was opened
            } catch (Exception e) {
                outcome = "error";
                msg = "Error reading file " + fileName + " - " + e.getMessage();
            }
        }

        OtpErlangObject[] tupleContents = {toOtpAtom(outcome), toOtp(msg)};
        return new OtpErlangTuple(tupleContents);
    }

    private static OtpErlangObject packageOutput(String cmd, OtpErlangObject payload) {
        OtpErlangObject cmdAtom = toOtpAtom(cmd);
        OtpErlangObject[] tupleContents = {cmdAtom, payload};
        return new OtpErlangTuple(tupleContents);
    }

    private static int fromOtpInt(OtpErlangObject otpInt) {
        try {
            return ((OtpErlangLong)otpInt).intValue();
        } catch (OtpErlangRangeException e) {
            return -1;

        }
    }

    private static OtpErlangObject toOtp(Object o) {
        if (o == null) {
            return new OtpErlangAtom("nil");
        } else {
            if (o instanceof Integer) { return toOtpInt((Integer)o);}
            if (o instanceof String) { return toOtpString((String)o);}
            if (o instanceof Date) { return toOtpDate((Date)o);}
            if (o instanceof Double) { return toOtpDouble((Double)o);}
        }
        return new OtpErlangAtom("nil");
    }

    private static OtpErlangObject toOtpDouble(Double dbl) {
        return new OtpErlangDouble(dbl);
    }

    private static OtpErlangString toOtpString(String str) {
        return new OtpErlangString(str);
    }

    private static OtpErlangString toOtpDate(Date date) {
        return toOtpString(dateFormatter.format(date));
    }

    private static OtpErlangLong toOtpInt(Integer integer) {
        return new OtpErlangLong(integer);
    }

    private static OtpErlangAtom toOtpAtom(String str) {
       return new OtpErlangAtom(str);
    }


    private static OtpErlangObject receivePayload() throws Exception {
        byte[] msgLen = new byte[4];

        int readByteCount = System.in.read(msgLen);
        if (readByteCount != msgLen.length) {throw new Exception("Didn't get message length");}

        ByteBuffer bb = ByteBuffer.wrap(msgLen);
        int encodedLength = bb.getInt();

        byte[] rawPayload = new byte[encodedLength];

        readByteCount = System.in.read(rawPayload);
        if (readByteCount != encodedLength) {throw new Exception("Payload didn't match message length");}

        OtpInputStream otpInputStream = new OtpInputStream(rawPayload);
        return otpInputStream.read_any();
    }

}

mindok

mindok

One more thing. To open the port on the elixir side you will need something like:

    jar_location = ~S|path\to\my\java_jars\encryptor_interface.jar|
    cmd = "java -jar #{jar_location}"
    Port.open({:spawn, cmd}, [:binary, :exit_status, :use_stdio, :stderr_to_stdout, {:packet, 4}])

Exadra37

Exadra37

Do you really need to use a Java class, can’t you use the built-in Erlang crypto module for encryption and decryption or one of the many Elixir libraries?

Anyway, I am not an expert but it looks like you will need to write a NIF to call your java class, by leveraging the BEAM interoperability capabilities:

http://erlang.org/doc/tutorial/introduction.html

This section informs on interoperability, that is, information exchange, between Erlang and other programming languages. The included examples mainly treat interoperability between Erlang and C.

Where Next?

Popular in Questions Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
Kagamiiiii
Student &amp; New to elixir. Nice language. I want to convert a english character, e.g. “a”, which is stored in a variable, to it’s asci...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
mathew4509
I have a list say x = ["23gh", "56kh", "97mh"] I would like to pass each element to Val in each iteration. Say, in iteration 1 -------...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New

Other popular topics Top

jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
AstonJ
You’re a programmer, so you don’t need spoon feeding with the conventional drivel about “this is an integer.” No. You need to know what’s...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
magnetic
Hey :wave:t3: Elixir community, I’ve been learning Elixir, and working on some side projects. My editor of choice is VSCode, and althoug...
New

We're in Beta

About us Mission Statement