McTheels

McTheels

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!

Showing Posts 1 to 6

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:

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.

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.

McTheels

McTheels OP

I followed an ruby example using ports here, but couldn’t quite get the java to do the same .
Kindly do share a sample if you can.

Thank you

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}])

— All posts loaded —

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
itzmidinesh
Hi all, I’m working on a Phoenix project where I need to make external HTTP requests and I’m trying to choose between Req and HTTPoison....
New
pragdave
How do I tell Hologram not to escape HTML when substituting into templates? {Textile.render(@product.long_description)} escapes the HTM...
New
leoschweizer
Hi, we run different LLM models across a cluster, with each node loading a configurable subset. Model-specific queues let nodes consume ...
New
rlipscombe
I’d like to create a file with 600 permissions (rw-------), but there’s a race condition if I create it and then change the permissions. ...
New
saleyn
Is there a way to import record definitions in the iex shell for pretty printing (similar to the rr command in the erl shell)? This woul...
New
rlipscombe
When I use Mix.install with a git dependency, using git@github.com:... how do I change the SSH identity used? I’ve got two Github accoun...
New

Other Trending Topics Top

dominicletz
Hey there, I haven’t seen a list of useful Elixir specific skills for the popular AI tools yet so I’m starting one here with my first. ...
New
raa-ziq
Hi everyone :waving_hand: I wanted to share something I’ve been building — NowBlind, a random chat app designed to help people discover ...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
OndrejValenta
(I just needed to vent somewhere and LinkedIn is full of hope, or hype, I’m not sure which exactly) AI dream has many faces, but general...
New
kip
Localize is the next generation localisation library for Elixir. Think of it as ex_cldr version 3.0. The first version will be released ...
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews