$30 off During Our Annual Pro Sale. View Details »

How to use a JVM library in node.js

How to use a JVM library in node.js

Laurynas Lubys

May 16, 2019
Tweet

More Decks by Laurynas Lubys

Other Decks in Programming

Transcript

  1. Laurynas Lubys, backend developer @ Wix
    How to use a JVM library
    in node.js
    laurynas[email protected] linkedin.com/in/laurynaslubys github.com/laurynasl-wix

    View Slide

  2. AGENDA
    Interop 101
    Sync interop
    Async interop

    View Slide

  3. How to use a JVM library
    in node.js

    View Slide

  4. The docs
    ▪ https://github.com/graalvm/graaljs/blob/master/docs/user/JavaInterop.md
    ▪ https://www.graalvm.org/docs/reference-manual/languages/js/
    INTEROP 101

    View Slide

  5. INTEROP 101
    Graal
    JVM Compiler Interface
    JVM Runtime
    Truffle
    Drop in replacement for
    node.js (mostly compatible
    with existing libs)
    TLDR;

    View Slide

  6. JVM interop enabled with
    --jvm
    INTEROP 101
    Graal
    JVM Compiler Interface
    JVM Runtime
    Truffle
    Drop in replacement for
    node.js (mostly compatible
    with existing libs)
    TLDR;

    View Slide

  7. TLDR;
    INTEROP 101
    Graal
    JVM Compiler Interface
    JVM Runtime
    Truffle
    Most cross language calls do
    not need conversions at
    runtime
    JVM interop enabled with
    --jvm
    Drop in replacement for
    node.js (mostly compatible
    with existing libs)

    View Slide

  8. hello-world.js
    const System =
    Java.type("java.lang.System");
    System.out.println("Hello world!");
    INTEROP 101
    Java
    Graal
    global scope
    JVM

    View Slide

  9. hello-world.js
    node --jvm hello-world.js
    > Hello world!
    const System =
    Java.type("java.lang.System");
    System.out.println("Hello world!");
    INTEROP 101
    run it!
    Java
    Graal
    global scope
    JVM

    View Slide

  10. How to use a JVM library
    in node.js
    Use case #1
    Sync
    interop

    View Slide

  11. SYNC
    INTEROP
    Use case #1
    bcrypt
    A CPU intensive hashing function

    View Slide

  12. Use case #1
    The Goal
    bcrypt
    SYNC
    INTEROP
    ▪ Use the JVM bcrypt library in node.js
    ▪ Integrate it into our server to handle user logins

    View Slide

  13. bcrypt
    SYNC
    INTEROP
    The Plan
    1. Tell the JVM where to find the jar
    2. Call it from node
    3. Use it in our application
    4. Go home early

    View Slide

  14. SYNC
    INTEROP
    1. Tell the JVM where to find the jar
    2. Call it from node
    3. Use it in our application
    4. Go home early
    --vm.cp Java.addToClasspath(path)
    OR

    View Slide

  15. SYNC
    INTEROP
    1. Tell the JVM where to find the jar
    2. Call it from node
    3. Use it in our application
    4. Go home early
    --vm.cp Java.addToClasspath(path)
    OR
    We’ll specify the classpath
    manually with --vm.cp

    View Slide

  16. SYNC
    INTEROP
    1. Tell the JVM where to find the jar
    2. Call it from node
    3. Use it in our application
    4. Go home early
    const BCrypt =
    Java.type("org.mindrot.jbcrypt.BCrypt");
    const salt =
    "$2a$10$sfMWTWROyUu5JxTEwm413u";
    const hash = BCrypt.hashpw("Hello", salt);
    console.log(hash);
    bcrypt.js

    View Slide

  17. SYNC
    INTEROP
    1. Tell the JVM where to find the jar
    2. Call it from node
    3. Use it in our application
    4. Go home early
    const BCrypt =
    Java.type("org.mindrot.jbcrypt.BCrypt");
    const salt =
    "$2a$10$sfMWTWROyUu5JxTEwm413u";
    const hash = BCrypt.hashpw("Hello", salt);
    console.log(hash);
    bcrypt.js
    FLAGS="\
    --jvm \
    --vm.cp=./jbcrypt-0.3m.jar"
    node $FLAGS bcrypt.js
    > $2a$1...ugoQ3u/Mjb/CYi
    run it!

    View Slide

  18. SYNC
    INTEROP
    1. Tell the JVM where to find the jar
    2. Call it from node
    3. Use it in our application
    4. Go home early
    const BCrypt =
    Java.type("org.mindrot.jbcrypt.BCrypt");
    const salt =
    "$2a$10$sfMWTWROyUu5JxTEwm413u";
    app.post('/login', (req, res) => {
    const pass = req.query.password;
    res.send(BCrypt.hashpw(pass, salt));
    });
    app.js

    View Slide

  19. Demo time!
    SYNC
    INTEROP

    View Slide

  20. Cool, ship it!
    SYNC
    INTEROP

    View Slide

  21. 1. Tell the JVM where to find the jar
    2. Call it from node
    3. Use it in our application
    4. Go home early
    We blocked the event loop
    SYNC
    INTEROP

    View Slide

  22. 1. Tell the JVM where to find the jar
    2. Call it from node
    3. Use it in our application
    4. Go home early
    We blocked the event loop
    fortunately...
    The node community
    knows how to deal with
    blocking workloads
    SYNC
    INTEROP

    View Slide

  23. 1. Tell the JVM where to find the jar
    2. Call it from node
    3. Use it in our application
    4. Go home early
    We blocked the event loop
    fortunately...
    The node community
    knows how to deal with
    blocking workloads
    so
    We can create a pool of workers
    and do the work there
    SYNC
    INTEROP

    View Slide

  24. How to use a JVM library
    in node.js
    Not so trivial,
    but it works
    Use case #2
    Sync
    interop
    Async
    interop

    View Slide

  25. Use case #2
    ASYNC
    INTEROP
    rabbitmq
    The Goal
    ▪ Consume messages from rabbitmq
    ▪ We want to use the rabbitmq “push API”

    View Slide

  26. ASYNC
    INTEROP
    rabbitmq
    The Plan
    1. Figure out how to do callbacks with Graal
    2. Define the Java API
    3. Handle a rabbitmq message in node

    View Slide

  27. ASYNC
    INTEROP
    callback.js
    1. Figure out how to do callbacks with Graal
    3. Handle a rabbitmq message in node
    2. Define the Java API
    const Arrays = Java.type("java.util.Arrays");
    const javaList = Arrays.asList(1, 2);
    javaList.stream()
    .map(v => `In node: ${v}`)
    .forEach(console.log);

    View Slide

  28. const Arrays = Java.type("java.util.Arrays");
    const javaList = Arrays.asList(1, 2);
    javaList.stream()
    .map(v => `In node: ${v}`)
    .forEach(console.log);
    callback.js
    run it!
    ASYNC
    INTEROP
    node --jvm callback.js
    > In node: 1
    > In node: 2
    1. Figure out how to do callbacks with Graal
    2. Define the Java API
    3. Handle a rabbitmq message in node

    View Slide

  29. ASYNC
    INTEROP
    interface MessageQueue {
    void init();
    void addConsumer(
    String queueName,
    Handler handler
    );
    interface Handler {
    void handle(String message);
    }
    }
    1. Figure out how to do callbacks with Graal
    2. Define the Java API
    3. Handle a rabbitmq message in node

    View Slide

  30. ASYNC
    INTEROP
    const RabbitMQ = Java.type("mq.RabbitMQ");
    const mq = new RabbitMQ();
    mq.init();
    const handle = console.log;
    mq.addConsumer("hello-rabbit", handle);
    1. Figure out how to do callbacks with Graal
    2. Define the Java API
    3. Handle a rabbitmq message in node

    View Slide

  31. Demo time!
    ASYNC
    INTEROP

    View Slide

  32. We can’t call JS from another thread
    Java
    function
    ASYNC
    INTEROP

    View Slide

  33. bindCallback
    Java
    function
    ASYNC
    INTEROP

    View Slide

  34. GraalJS does not provide that
    out of the box
    ASYNC
    INTEROP

    View Slide

  35. Let’s see what we can do about that
    GraalJS does not provide that
    out of the box
    ASYNC
    INTEROP

    View Slide

  36. InteropBus
    Async Java code
    pending
    complete( )
    ( , )
    We want to call with async results from java.
    node.js
    worker
    node.js
    Java land
    bindCallback
    on(‘message’)

    View Slide

  37. 4. implement handlers that can be called from a different thread
    ASYNC
    INTEROP
    1. Figure out how to do callbacks with Graal
    2. Define the Java API
    3. Handle a rabbitmq message in node

    View Slide

  38. class JSHandler implements MessageQueue.Handler {
    BoundJSCallback jsCallback;
    JSHandler(BoundJSCallback jsCallback) {
    this.jsCallback = jsCallback;
    }
    @Override
    void handle(String message) {
    jsCallback.apply(new Object[]{message});
    }
    }
    JSHandler.java
    ASYNC
    INTEROP

    View Slide

  39. const RabbitMQ = Java.type("mq.RabbitMQ");
    const mq = new RabbitMQ();
    mq.init();
    const JSHandler = Java.type("mq.JSHandler");
    const {bindCallback} = require("../../interop");
    const handle =
    new JSHandler(bindCallback(console.log));
    mq.addConsumer("hello-rabbit", handle);
    Take #2
    ASYNC
    INTEROP

    View Slide

  40. Demo time!
    ASYNC
    INTEROP

    View Slide

  41. Not so trivial,
    but it works
    Works, not straightforward,
    mostly infrastructure
    Sync
    interop
    Async
    interop

    View Slide

  42. Questions?
    laurynas[email protected] linkedin.com/in/laurynaslubys github.com/laurynasl-wix
    github.com/laurynasl-wix/graal-jvm-interop-talk

    View Slide

  43. Thank You
    laurynas[email protected] linkedin.com/in/laurynaslubys github.com/laurynasl-wix
    github.com/laurynasl-wix/graal-jvm-interop-talk

    View Slide