How to use a JVM library in node.js

How to use a JVM library in node.js

01b8149f78caac2977f68facd6f0e432?s=128

Laurynas Lubys

May 16, 2019
Tweet

Transcript

  1. Laurynas Lubys, backend developer @ Wix How to use a

    JVM library in node.js laurynasl@wix.com linkedin.com/in/laurynaslubys github.com/laurynasl-wix
  2. AGENDA Interop 101 Sync interop Async interop

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

  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

  5. INTEROP 101 Graal JVM Compiler Interface JVM Runtime Truffle Drop

    in replacement for node.js (mostly compatible with existing libs) TLDR;
  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;
  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)
  8. hello-world.js const System = Java.type("java.lang.System"); System.out.println("Hello world!"); INTEROP 101 Java

    Graal global scope JVM
  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
  10. How to use a JVM library in node.js Use case

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

    function
  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
  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
  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
  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
  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
  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!
  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
  19. Demo time! SYNC INTEROP

  20. Cool, ship it! SYNC INTEROP

  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
  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
  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
  24. How to use a JVM library in node.js Not so

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

    messages from rabbitmq ▪ We want to use the rabbitmq “push API”
  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
  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);
  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
  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
  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
  31. Demo time! ASYNC INTEROP

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

    INTEROP
  33. bindCallback Java function ASYNC INTEROP

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

    INTEROP
  35. Let’s see what we can do about that GraalJS does

    not provide that out of the box ASYNC INTEROP
  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’)
  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
  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
  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
  40. Demo time! ASYNC INTEROP

  41. Not so trivial, but it works Works, not straightforward, mostly

    infrastructure Sync interop Async interop
  42. Questions? laurynasl@wix.com linkedin.com/in/laurynaslubys github.com/laurynasl-wix github.com/laurynasl-wix/graal-jvm-interop-talk

  43. Thank You laurynasl@wix.com linkedin.com/in/laurynaslubys github.com/laurynasl-wix github.com/laurynasl-wix/graal-jvm-interop-talk