-
-
Notifications
You must be signed in to change notification settings - Fork 5
Java 9 Modules
As of version 0.2.1, insn supports generating Java 9 module definitions.
For this demonstration, we recreate the examples from the Project Jigsaw quick start.
First, we'll create our deps.edn file. Since the ASM version bundled with Clojure (as of version 1.9) is too old for our purposes, we must add a dependency on a newer version:
{:deps {insn {:mvn/version "0.2.1"}
org.ow2.asm/asm {:mvn/version "6.0"}}}(Note: since 0.3.0, the ASM dependency is no longer necessary.)
Next, we start a REPL with our above dependencies on the class path:
$ clj -rLike with class types, modules are just simple data:
(def greetings-module-data
{:id :com.greetings
:requires [:org.astro]})
(def astro-module-data
{:id :org.astro
:exports [:org.astro]})We need some types that will reside in our separate modules:
(def world-class-data
{:name 'org.astro.World
:methods [{:flags [:public :static]
:name "name"
:desc [String]
:emit [[:ldc "World"]
[:areturn]]}]})
(def main-class-data
{:name 'com.greetings.Main
:methods [{:flags [:public :static]
:name "main"
:desc [[String] :void]
:emit [[:getstatic System "out"]
[:ldc "Greetings %s!%n"]
[:ldc 1]
[:anewarray Object]
[:dup]
[:ldc 0]
[:invokestatic 'org.astro.World "name" [String]]
[:aastore]
[:invokevirtual java.io.PrintStream "format" 2]
[:return]]}]})Now we write our two simple modules named com.greetings and org.astro to the current directory:
(require '[insn.objectweb-asm] ;; load the external version
'[insn.core :as type]
'[insn.module :as module])
(set! *compile-path* "mods/com.greetings") ;; used by `insn.core/write`
(-> (module/visit greetings-module-data)
type/write) ;; to ./mods/com.greetings/module-info.class
(-> (type/visit main-class-data)
type/write) ;; to ./mods/com.greetings/com/greetings/Main.class
(set! *compile-path* "mods/org.astro")
(-> (module/visit astro-module-data)
type/write) ;; to ./mods/org.astro/module-info.class
(-> (type/visit world-class-data)
type/write) ;; to ./mods/org.astro/org/astro/World.classWe can run our main class from within the com.greetings module using Java 9:
$ java -version
# => java version "9.0.1"
$ java --module-path mods -m com.greetings/com.greetings.Main
# => Greetings world!The final Jigsaw example deals with module services. We simplify it slightly here for brevity, but it is essentially the same. Continuing in our REPL session from above; first the module defs:
(def greetings2-module-data
{:id :com.greetings2
:requires [:com.socket]
:uses ['com.socket.spi.NetworkSocketProvider]})
(def socket-module-data
{:id :com.socket
:exports [:com.socket, :com.socket.spi]})
(def fastsocket-module-data
{:id :org.fastsocket
:requires [:com.socket]
:provides {'com.socket.spi.NetworkSocketProvider
'org.fastsocket.FastNetworkSocketProvider}})Next, the interface and class types:
(def isocket-class-data
{:name 'com.socket.NetworkSocket
:flags [:public :interface]})
(def iprovider-class-data
{:name 'com.socket.spi.NetworkSocketProvider
:flags [:public :interface]
:methods [{:flags [:public :abstract]
:name "openNetworkSocket"
:desc ['com.socket.NetworkSocket]}]})
(def socket-class-data
{:name 'org.fastsocket.FastNetworkSocket
:flags [:public]
:interfaces ['com.socket.NetworkSocket]})
(def provider-class-data
{:name 'org.fastsocket.FastNetworkSocketProvider
:flags [:public]
:interfaces ['com.socket.spi.NetworkSocketProvider]
:methods [{:flags [:public]
:name "openNetworkSocket"
:desc ['com.socket.NetworkSocket]
:emit [[:new 'org.fastsocket.FastNetworkSocket]
[:dup]
[:invokespecial 'org.fastsocket.FastNetworkSocket :init [:void]]
[:areturn]]}]})
(def main-class2-data
{:name 'com.greetings.Main
:methods [{:flags [:public :static]
:name "main"
:desc [[String] :void]
:emit [[:getstatic System "out"]
[:ldc 'com.socket.spi.NetworkSocketProvider]
[:invokestatic java.util.ServiceLoader "load" 1]
[:invokeinterface Iterable "iterator"]
[:invokeinterface java.util.Iterator "next"]
[:checkcast 'com.socket.spi.NetworkSocketProvider]
[:invokeinterface 'com.socket.spi.NetworkSocketProvider
"openNetworkSocket" ['com.socket.NetworkSocket]]
[:invokevirtual Object "getClass"]
[:invokevirtual java.io.PrintStream "println" [Object :void]]
[:return]]}]})Finally, we emit our .class files to disk like before:
(set! *compile-path* "mods/com.greetings2")
(run! type/write [(module/visit greetings2-module-data)
(type/visit main-class2-data)])
(set! *compile-path* "mods/com.socket")
(run! type/write [(module/visit socket-module-data)
(type/visit isocket-class-data)
(type/visit iprovider-class-data)])
(set! *compile-path* "mods/org.fastsocket")
(run! type/write [(module/visit fastsocket-module-data)
(type/visit socket-class-data)
(type/visit provider-class-data)])Now we run our main class from within the com.greetings2 module that uses the socket service:
$ java -p mods -m com.greetings2/com.greetings.Main
# => class org.fastsocket.FastNetworkSocketFor more on ASM's module support see the javadocs.