Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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 [email protected] linkedin.com/in/laurynaslubys github.com/laurynasl-wix
  2. INTEROP 101 Graal JVM Compiler Interface JVM Runtime Truffle Drop

    in replacement for node.js (mostly compatible with existing libs) TLDR;
  3. 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;
  4. 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)
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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!
  12. 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
  13. 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
  14. 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
  15. 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
  16. How to use a JVM library in node.js Not so

    trivial, but it works Use case #2 Sync interop Async interop
  17. Use case #2 ASYNC INTEROP rabbitmq The Goal ▪ Consume

    messages from rabbitmq ▪ We want to use the rabbitmq “push API”
  18. 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
  19. 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);
  20. 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
  21. 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
  22. 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
  23. Let’s see what we can do about that GraalJS does

    not provide that out of the box ASYNC INTEROP
  24. 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’)
  25. 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
  26. 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
  27. 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
  28. Not so trivial, but it works Works, not straightforward, mostly

    infrastructure Sync interop Async interop