emacs.d/clones/lisp/clojure-doc.org/articles/language/interop/index.html

617 lines
42 KiB
HTML

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta charset="utf-8"/>
<title>Clojure Guides: Clojure interoperability with Java</title>
<meta name="description" content="This guide covers:">
<meta property="og:description" content="This guide covers:">
<meta property="og:url" content="https://clojure-doc.github.io/articles/language/interop/" />
<meta property="og:title" content="Clojure interoperability with Java" />
<meta property="og:type" content="article" />
<link rel="canonical" href="https://clojure-doc.github.io/articles/language/interop/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
type="text/css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li ><a href="../../../index.html">Home</a></li>
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
</ul>
</div><!--/.nav-collapse -->
</div><!--/.container-fluid -->
</nav>
<div class="container">
<div class="row">
<div class="col-lg-9">
<div id="content">
<div id="custom-page">
<div id="page-header">
<h2>Clojure interoperability with Java</h2>
</div>
<p>This guide covers:</p><ul><li>How to instantiate Java classes</li><li>How to invoke Java methods</li><li>How to extend Java classes with proxy</li><li>How to implement Java interfaces with reify</li><li>How to generate Java classes with gen-class</li><li>Other topics related to interop</li></ul><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>
(including images &amp; stylesheets). The source is available <a href="https://github.com/clojure-doc/clojure-doc.github.io">on Github</a>.</p><h2 id="what-version-of-clojure-does-this-guide-cover">What Version of Clojure Does This Guide Cover?</h2><p>This guide covers Clojure 1.5.</p><h2 id="overview">Overview</h2><p>Clojure was designed to be a hosted language that directly interoperates with its host platform (JVM, CLR and so on).
Clojure code is compiled to JVM bytecode. For method calls on Java objects, Clojure compiler
will try to emit the same bytecode <code>javac</code> would produce.</p><p>It is possible to implement interfaces, extend and generate Java classes in Clojure.</p><p>Clojure also provides convenient functions and macros that make consuming of Java libraries
easier and often more concise than it would be in Java code.</p><h2 id="imports">Imports</h2><p>Java classes can be referenced either using their fully-qualified names (FQNs) such as
<code>java.util.Date</code> or be <em>imported</em> in the current Clojure namespace using <code>clojure.core/import</code> and
referenced by short names:</p><pre><code class="clojure">java.util.Date ; ⇒ java.util.Date
</code></pre><pre><code class="clojure">(import java.util.Date)
Date ; ⇒ java.util.Date
</code></pre><p><code>ns</code> macro supports imports, too:</p><pre><code class="clojure">(ns myservice.main
(:import java.util.Date))
</code></pre><p>More about the <code>ns</code> macro can be found in the article on <a href="../namespaces/index.html">Clojure namespaces</a>.</p><p>Dynamic (at runtime) imports are usually only used in the REPL and cases when there are multiple implementations of a particular
protocol/service/feature and it is not possible to tell which one should be used until run time.</p><h3 id="automatic-imports-for-javalang">Automatic Imports For java.lang.*</h3><p>Classes from the <code>java.lang</code> package are automatically imported. For example, you can use <code>String</code> or <code>Math</code>
without explicitly importing them:</p><pre><code class="clojure">(defn http-uri?
[^String uri]
(.startsWith (.toLowerCase uri) "http"))
(Math/round 0.7886)
</code></pre><h3 id="inner-nested-classes">Inner (Nested) Classes</h3><p>In Java, classes can be nested inside other classes. They are called <em>inner classes</em> and by convention,
separated from their outer class by a dollar sign (<code>$</code>):</p><pre><code class="clojure">(import java.util.Map$Entry)
Map$Entry ; ⇒ java.util.Map$Entry
;; this example assumes RabbitMQ Java client is on classpath
(import com.rabbitmq.client.AMQP$BasicProperties)
AMQP$BasicProperties ; ⇒ com.rabbitmq.client.AMQP$BasicProperties
</code></pre><p>Note that if you need to use both a class and one or more of its inner classes, they all need to be imported separately.
As far as JVM is concerned, they are all separate classes, there is no "imports hierarchy".</p><h2 id="how-to-instantiate-java-classes">How to Instantiate Java Classes</h2><p>Java classes are instantiated using the <code>new</code> special form:</p><pre><code class="clojure">(new java.util.Date) ; ⇒ #inst "2012-10-09T21:23:57.278-00:00"
</code></pre><p>However, the Clojure reader provides a bit of syntactic sugar and you are much more likely
to see this:</p><pre><code class="clojure">(java.util.Date.) ; ⇒ #inst "2012-10-09T21:24:43.878-00:00"
</code></pre><p>It is possible to use fully qualified names (e.g. <code>java.util.Date</code>) or short names with imports:</p><pre><code class="clojure">(import java.util.Date)
(Date.) ; ⇒ #inst "2012-10-09T21:24:27.229-00:00"
</code></pre><p>An example with constructor arguments:</p><pre><code class="clojure">(java.net.URI. "http://clojure.org") ; ⇒ #&lt;URI http://clojure.org&gt;
</code></pre><h2 id="how-to-invoke-java-methods">How to Invoke Java Methods</h2><h3 id="instance-methods">Instance Methods</h3><p>Instance methods are invoked using the <code>.</code> special form:</p><pre><code class="clojure">(let [d (java.util.Date.)]
(. d getTime)) ; ⇒ 1349819873183
</code></pre><p>Just like with object instantiation, it is much more common to see an alternative version:</p><pre><code class="clojure">(let [d (java.util.Date.)]
(.getTime d)) ; ⇒ 1349819873183
</code></pre><h3 id="static-methods">Static Methods</h3><p>Static methods can be invoked with the same <code>.</code> special form:</p><pre><code class="clojure">(. Math floor 5.677) ; ⇒ 5.0
</code></pre><p>or (typically) to sugared version, <code>ClassName/methodName</code>:</p><pre><code class="clojure">(Math/floor 5.677) ; ⇒ 5.0
(Boolean/valueOf "false") ; ⇒ false
(Boolean/valueOf "true") ; ⇒ true
</code></pre><h3 id="chained-calls-with-the-double-dot-form">Chained Calls With The Double Dot Form</h3><p>It is possible to chain method calls using the <code>..</code> special form:</p><pre><code class="clojure">(.. (java.util.Date.) getTime toString) ; ⇒ "1349821993809"
</code></pre><h3 id="multiple-calls-on-the-same-object">Multiple Calls On the Same Object</h3><p>If you need to call a bunch of methods on a mutable object, you
can use the <code>doto</code> macro:</p><pre><code class="clojure">(doto (java.util.Stack.)
(.push 42)
(.push 13)
(.push 7)) ; ⇒ #&lt;Stack [42, 13, 7]&gt;
(let [pt (Point. 0 0)]
(doto pt
(.move 10 0))) ; ⇒ #&lt;Point java.awt.Point[x=10, y=0]
(let [pt (Point. 0 0)]
(doto pt
(.move 10 0)
(.translate 0 10))) ; ⇒ #&lt;Point java.awt.point[x=10,y=10]
</code></pre><p>The <code>doto</code> macro returns its first argument as a result.</p><h2 id="how-to-access-java-fields">How to Access Java Fields</h2><p>Public mutable fields are not common in Java libraries but sometimes you need to access them.
It's done with the same dot special form:</p><pre><code class="clojure">(import java.awt.Point)
(let [pt (Point. 0 10)]
(. pt x)) ; ⇒ 0
(let [pt (Point. 0 10)]
(. pt y)) ; ⇒ 10
</code></pre><p>and just like with instance methods, it is much more common to see the following version:</p><pre><code class="clojure">(import java.awt.Point)
(let [pt (Point. 0 10)]
(.x pt)) ; ⇒ 0
(let [pt (Point. 0 10)]
(.y pt)) ; ⇒ 10
</code></pre><h2 id="how-to-set-java-fields">How to Set Java Fields</h2><p>To set a public mutable field, use <code>clojure.core/set!</code> that takes a field in the dot notation
demonstrated earlier and a new value:</p><pre><code class="clojure">(import java.awt.Point)
(let [pt (Point. 0 10)]
(set! (.y pt) 100)
(.y pt)) ; ⇒ 100
</code></pre><p>Fortunately, mutable public fields are rare to meet in the JVM ecosystem so you won't need
to do this often.</p><h2 id="how-to-work-with-enums">How To Work With Enums</h2><p><a href="http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html">Enums (enumeration) type</a> values are accessed
the same way as fields, except on enum classes and not objects:</p><pre><code class="clojure">java.util.concurrent.TimeUnit/MILLISECONDS ; ⇒ #&lt; MILLISECONDS&gt;
</code></pre><h2 id="determining-classes-of-java-objects">Determining Classes of Java Objects</h2><p>To get class of a particular value, pass it to <code>clojure.core/class</code>:</p><pre><code class="clojure">(class 1) ; ⇒ java.lang.Long
(class 1.0) ; ⇒ java.lang.Double
(class "docs") ; ⇒ java.lang.String
(class (java.net.URI. "https://github.com")) ; ⇒ java.net.URI
</code></pre><p>As this example demonstrates, Clojure strings are JVM strings, integer literals are compiled
as longs and floating point literals are compiled as doubles.</p><p>You can also use <code>clojure.core/type</code> to return either the class of the
Java object, or the <code>:type</code> metadata if it exists:</p><pre><code class="clojure">(def foo (with-meta [1 2 3] {:type :bar}))
(type foo)
;; ⇒ :bar
(type [1 2 3])
;; ⇒ clojure.lang.PersistentVector
</code></pre><h2 id="how-to-get-a-java-class-reference-by-name">How To Get a Java Class Reference By Name</h2><p>To obtain a class reference by its string name (fully qualified), use <code>Class/forName</code> via Java interop:</p><pre><code class="clojure">(Class/forName "java.util.Date") ; ⇒ java.util.Date
</code></pre><h3 id="array-types-primitives">Array Types, Primitives</h3><p>JVM has what is called <strong>primitive types</strong> (numerics, chars, booleans) that are not "real" objects.
In addition, array types have pretty obscure internal names. If you need to obtain a reference to
an array of longs, for example, pass <code>"[[J"</code> to <code>Class/forName</code>. Below is the full table:</p><table class="table-striped table-bordered table"><thead><tr><th>Internal JVM class name</th><th>Array of ? (type)</th></tr></thead><tbody><tr><td><pre>"[[S"</pre></td><td>short</td></tr><tr><td><pre>"[[I"</pre></td><td>integer</td></tr><tr><td><pre>"[[J"</pre></td><td>long</td></tr><tr><td><pre>"[[F"</pre></td><td>float</td></tr><tr><td><pre>"[[D"</pre></td><td>double</td></tr><tr><td><pre>"[[B"</pre></td><td>byte</td></tr><tr><td><pre>"[[C"</pre></td><td>char</td></tr><tr><td><pre>"[[Z"</pre></td><td>boolean</td></tr></tbody></table><p>If this does not make much sense, don't worry. Just remember to come
back to this guide when you need to extend a protocol for an array of
primitives.</p><h2 id="implementing-java-interfaces-with-reify">Implementing Java Interfaces With reify</h2><p>It is possible to implement Java interfaces in Clojure. It is
typically needed to interact with Java libraries that take arguments
implementing a particular interface.</p><p>Interfaces are implemented using the <code>reify</code> special form.</p><p>Given the following Java interface:</p><pre><code class="java">public
interface FilenameFilter {
/**
* Tests if a specified file should be included in a file list.
*
* @param dir the directory in which the file was found.
* @param name the name of the file.
* @return &lt;code&gt;true&lt;/code&gt; if and only if the name should be
* included in the file list; &lt;code&gt;false&lt;/code&gt; otherwise.
*/
boolean accept(File dir, String name);
}
</code></pre><p>here is how to implement it in Clojure:</p><pre><code class="clojure">;; a FileFilter implementation that accepts everything
(reify java.io.FilenameFilter
(accept [this dir name]
true))
</code></pre><p><code>reify</code> takes an interface (fully-qualified name or short name) and one or more
method implementations that mimic function definitions without the <code>defn</code> and with
<em>this</em> (as in Java, JavaScript or <em>self</em> in Ruby, Python) reference being the first argument:</p><pre><code class="clojure">(accept [this dir name]
true)
</code></pre><p>With <code>reify</code>, generally there is no need to add type hints on arguments: Clojure
compiler typically will detect the best matching method (by name and number of arguments).</p><p><code>reify</code> returns a <em>Java class instance</em>. Clojure compiler will generate a class that implements
the interface and instantiate it. To demonstrate that reified objects indeed implement
the interface:</p><pre><code class="clojure">(let [ff (reify java.io.FilenameFilter
(accept [this dir name]
true))]
(instance? java.io.FileFilter ff)) ; ⇒ true
</code></pre><p><code>reify</code> can be used to implement multiple interfaces at once:</p><pre><code class="clojure">(let [ff (reify java.io.FilenameFilter
(accept [this dir name]
true)
java.io.FileFilter
(accept [this dir]
true))]
(instance? java.io.FileFilter ff)) ; ⇒ true
</code></pre><h3 id="reify-parameter-destructuring-and-varargs">reify, Parameter Destructuring and Varargs</h3><p><code>reify</code> does not support destructuring or variadic number of arguments in method signatures.
For example, the following will not work and won't even compile in Clojure 1.5:</p><pre><code class="clojure">(reify com.megacorp.api.AnInterface
(aMethod [a [b c]]
(comment ...))
(anotherMethod [a &amp; rest]
(comment ...)))
</code></pre><h3 id="example-1">Example 1</h3><p>The following example demonstrates how instances created with <code>reify</code> are passed around
as regular Java objects:</p><pre><code class="clojure">(import java.io.File)
;; a file filter implementation that keeps only .clj files
(let [ff (reify java.io.FilenameFilter
(accept [this dir name]
(.endsWith name ".clj")))
dir (File. "/Users/antares/Development/ClojureWerkz/neocons.git/")]
(into [] (.listFiles dir ff)))
;; ⇒ [#&lt;File /Users/antares/Development/ClojureWerkz/neocons.git/project.clj&gt;]
</code></pre><p><code>reify</code> forms a closure: it will capture locals in its scope. This can be used to make implemented
methods delegate to Clojure functions. The same example, rewritten with delegation:</p><pre><code class="clojure">user&gt; (import java.io.File)
;; a file filter implementation that keeps only .clj files
(let [f (fn [^File dir ^String name]
(.endsWith name ".clj"))
ff (reify java.io.FilenameFilter
(accept [this dir name]
(f dir name)))
dir (File. "/Users/antares/Development/ClojureWerkz/neocons.git/")]
(into [] (.listFiles dir ff)))
;; ⇒ [#&lt;File /Users/antares/Development/ClojureWerkz/neocons.git/project.clj&gt;]
</code></pre><p>Note that unlike in the "inline" implementation, Clojure compiler cannot infer types of
<code>dir</code> and <code>name</code> parameters in the function that does the filtering, so we added type hints
to avoid reflective calls. When methods are implemented "inline", types can be inferred from
method signatures in the interface.</p><h2 id="extending-java-classes-with-proxy">Extending Java Classes With proxy</h2><p><code>proxy</code> is one of two ways to generate instances of anonymous classes in Clojure.
<code>proxy</code> takes two vectors: one listing its superclass and (optional) interfaces, another constructor signatures, as well as
method implementations. Method implementations are basically identical to <code>reify</code> except that the <code>this</code> argument is
not necessary.</p><p>A very minimalistic example, we instantiate an anonymous class that extends <code>java.lang.Object</code>, implements no
interfaces, has no explictly defined constructors and overrides <code>#toString</code>:</p><pre><code class="clojure">(proxy [Object] []
(toString []
"I am an instance of an anonymous class generated via proxy"))
;; ⇒ #&lt;Object$0 I am an instance of an anonymous class generated via proxy&gt;
</code></pre><p>Clojure compiler will generate an anonymous class for this <code>proxy</code> and at runtime, the cost of
a <code>proxy</code> call is the cost of instantiating this class (the class is not generated anew on every single call).</p><p>A slightly more complex example where the generated class also implements <code>java.lang.Runnable</code> (runnable objects
are commonly used with threads and <code>java.util.concurrent</code> classes) which defines one method, <code>#run</code>:</p><pre><code class="clojure">;; extends java.lang.Object, implements java.lang.Runnable
(let [runnable (proxy [Object Runnable] []
(toString []
"I am an instance of an anonymous class generated via proxy")
(run []
(println "Run, proxy, run")))]
(.run runnable)) ; ⇒ nil
;; outputs "Run, proxy, run"
</code></pre><p><code>proxy</code> forms a closure: it will capture locals in its scope. This is very often used to create an instance
that delegates to a Clojure function:</p><pre><code class="clojure">(let [f (fn [] (println "Executed from a function"))
obj (proxy [Object Runnable] []
(run []
(f)))]
(.run obj)) ; ⇒ nil
;; outputs "Executed from a function"
</code></pre><p>TBD: more realistic examples | <a href="https://github.com/clojure-doc/clojure-doc.github.io#how-to-contribute">How to Contribute</a></p><h2 id="clojure-functions-implement-runnable-and-callable">Clojure Functions Implement Runnable and Callable</h2><p>Note that Clojure functions implement <code>java.lang.Runnable</code> and
<code>java.util.concurrent.Callable</code> directly so you can pass functions to
methods found in various classes from the <code>java.util.concurrent</code> package.</p><p>For example, to run a function in a new thread:</p><pre><code class="clojure">(let [t (Thread. (fn []
(println "I am running in a separate thread")))]
(.start t))
</code></pre><p>Or submit a function for execution to a thread pool (in JDK terms: an execution service):</p><pre><code class="clojure">(import '[java.util.concurrent Executors ExecutorService Callable])
(let [^ExecutorService pool (Executors/newFixedThreadPool 16)
^Callable clbl (cast Callable (fn []
(reduce + (range 0 10000))))
task (.submit pool clbl)]
(.get task))
;; ⇒ 49995000
</code></pre><p>Note that without the cast, Clojure compiler would not be able to determine
which exact version of the method we intend to invoke, because <code>java.util.concurrent.ExecutionService/submit</code>
has two versions, one for <code>Runnable</code> and one for <code>Callable</code>. They work very much the same but return
slightly different results (<code>Callable</code> produces a value while <code>Runnable</code> always returns nil when
executed).</p><p>The exception we would get without the cast is</p><pre><code>CompilerException java.lang.IllegalArgumentException: More than one matching method found: submit, compiling:(NO_SOURCE_PATH:2)
</code></pre><h2 id="gen-class-and-how-to-implement-java-classes-in-clojure">gen-class and How to Implement Java Classes in Clojure</h2><h3 id="overview-1">Overview</h3><p><code>gen-class</code> is a Clojure feature for implementing Java classes in Clojure. It is relatively
rarely used compared to <code>proxy</code> and <code>reify</code> but is needed to implement executable classes
(that <code>java</code> runner and IDEs can as program entry points).</p><p>Unlike <code>proxy</code> and <code>reify</code>, <code>gen-class</code> defines named classes. They can be passed to Java
APIs that expect class references. Classes defined with <code>gen-class</code> can extend
base classes, implement any number of Java interfaces, define any number of constructors
and define both instance and static methods.</p><h3 id="aot">AOT</h3><p><code>gen-class</code> requires <em>ahead-of-time</em> (AOT) compilation. It means that
before using the classes defined with <code>gen-class</code>, the Clojure
compiler needs to produce <code>.class</code> files from <code>gen-class</code> definitions.</p><h3 id="class-definition-with-clojurecoregen-class">Class Definition With clojure.core/gen-class</h3><p><code>clojure.core/gen-class</code> is a macro that uses a DSL for defining class
methods, base class, implemented interfaces and so on.</p><p>It takes a number of options:</p><ul><li><code>:name</code> (a symbol): defines generated class name</li><li><code>:extends</code> (a symbol): name of the base class</li><li><code>:implements</code> (a collection): interfaces the class implements</li><li><code>:constructors</code> (a map): constructor signatures</li><li><code>:methods</code> (a collection): lists methods that will be implemented</li><li><code>:init</code> (symbol): defines a function that will be invoked with constructor arguments</li><li><code>:post-init</code> (symbol): defines a function that will be called with a constructed instance as its first argument</li><li><code>:state</code> (symbol): if supplied, a public final instance field with the given name will be created. Only makes sense when
used with <code>:init</code>. State field value should be an atom or other ref type to allow state mutation.</li><li><code>:prefix</code> (string, default: <code>"-"</code>): methods will call functions named as <code>(str prefix method-name)</code>, e.g. <code>-getName</code> for <code>getName</code>.</li><li><code>:main</code> (boolean): if <code>true</code>, a public static main method will be generated for the class. It will delegate
to a function named main with the prefix (<code>(str prefix "main")</code>), <code>-main</code> by default</li><li><code>:exposes</code>: TBD</li><li><code>:exposes-methods</code>: TBD</li><li><code>:factory</code>: TBD</li><li><code>:load-impl-ns</code>: TBD</li><li><code>:impl-ns</code>: TBD</li></ul><h4 id="the-name-option">The :name Option</h4><p>TBD</p><h4 id="the-extends-option">The :extends Option</h4><p>TBD</p><h4 id="the-implements-option">The :implements Option</h4><p>TBD</p><h4 id="the-constructors-option">The :constructors Option</h4><p>TBD</p><h4 id="the-methods-option">The :methods Option</h4><p>TBD</p><h4 id="the-init-option">The :init Option</h4><p>TBD</p><h4 id="the-post-init-option">The :post-init Option</h4><p>TBD</p><h4 id="the-state-option">The :state Option</h4><p>TBD</p><h4 id="the-prefix-option">The :prefix Option</h4><p>TBD</p><h4 id="the-main-option">The :main Option</h4><p>TBD</p><h4 id="the-exposes-option">The :exposes Option</h4><p>TBD</p><h4 id="the-exposes-methods-option">The :exposes-methods Option</h4><p>TBD</p><h4 id="the-factory-option">The :factory Option</h4><p>TBD</p><h4 id="the-load-impl-ns-option">The :load-impl-ns Option</h4><p>TBD</p><h4 id="the-impl-ns-option">The :impl-ns Option</h4><p>TBD</p><h3 id="gen-class-in-the-ns-macro">gen-class In The ns Macro</h3><p><code>gen-class</code> can be used with existing namespaces by adding <code>(:gen-class)</code> to the
<code>ns</code> macro. Here is a "hello, world" example command line app that uses <code>gen-class</code>
to generate a class that JVM launcher (<code>java</code>) can run:</p><pre><code class="clojure">(ns genclassy.core
(:gen-class))
(defn -main
[&amp; args]
(println "Hello, World!"))
</code></pre><p>This will use the name of the namespace for class name and use the namespace for method
implementation (see the <code>:impl-ns</code> option above).</p><h3 id="examples">Examples</h3><p>A medium size example taken from an open source library:</p><pre><code class="clojure">(ns clojurewerkz.quartzite.listeners.amqp.PublishingSchedulerListener
(:gen-class :implements [org.quartz.SchedulerListener]
:init init
:state state
:constructors {[com.rabbitmq.client.Channel String String] []})
(:require [langohr.basic :as lhb]
[clojure.data.json :as json])
(:use [clojurewerkz.quartzite.conversion])
(:import [org.quartz SchedulerListener SchedulerException Trigger TriggerKey JobDetail JobKey]
[com.rabbitmq.client Channel]
[java.util Date]
[clojurewerkz.quartzite.listeners.amqp PublishingSchedulerListener]))
(defn publish
[^PublishingSchedulerListener this payload ^String type]
(let [{ :keys [channel exchange routing-key] } @(.state this)
payload (json/json-str payload)]
(lhb/publish channel exchange routing-key payload :type type)))
(defn -init
[^Channel ch ^String exchange ^String routing-key]
[[] (atom { :channel ch :exchange exchange :routing-key routing-key })])
(defmacro payloadless-publisher
[method-name message-type]
`(defn ~method-name
[this#]
(publish this# (json/json-str {}) ~message-type)))
(payloadless-publisher -schedulerStarted "quartz.scheduler.started")
(payloadless-publisher -schedulerInStandbyMode "quartz.scheduler.standby")
(payloadless-publisher -schedulingDataCleared "quartz.scheduler.cleared")
(payloadless-publisher -schedulerShuttingDown "quartz.scheduler.shutdown")
(defn -schedulerError
[this ^String msg ^SchedulerException cause]
(publish this (json/json-str { :message msg :cause (str cause) }) "quartz.scheduler.error"))
(defn -jobScheduled
[this ^Trigger trigger]
(publish this (json/json-str { :group (-&gt; trigger .getKey .getGroup) :key (-&gt; trigger .getKey .getName) :description (.getDescription trigger) }) "quartz.scheduler.job-scheduled"))
(defn -jobUnscheduled
[this ^TriggerKey key]
(publish this (json/json-str { :group (.getGroup key) :key (.getName key) }) "quartz.scheduler.job-unscheduled"))
(defn -triggerFinalized
[this ^Trigger trigger]
(publish this (json/json-str { :group (-&gt; trigger .getKey .getGroup) :key (-&gt; trigger .getKey .getName) :description (.getDescription trigger) }) "quartz.scheduler.trigger-finalized"))
(defn -triggerPaused
[this ^TriggerKey key]
(publish this (json/json-str { :group (.getGroup key) :key (.getName key) }) "quartz.scheduler.trigger-paused"))
(defn -triggersPaused
[this ^String trigger-group]
(publish this (json/json-str { :group trigger-group }) "quartz.scheduler.triggers-paused"))
(defn -triggerResumed
[this ^TriggerKey key]
(publish this (json/json-str { :group (.getGroup key) :key (.getName key) }) "quartz.scheduler.trigger-resumed"))
(defn -triggersResumed
[this ^String trigger-group]
(publish this (json/json-str { :group trigger-group }) "quartz.scheduler.triggers-resumed"))
(defn -jobAdded
[this ^JobDetail detail]
(publish this (json/json-str { :job-detail (from-job-data (.getJobDataMap detail)) :description (.getDescription detail) }) "quartz.scheduler.job-added"))
(defn -jobDeleted
[this ^JobKey key]
(publish this (json/json-str { :group (.getGroup key) :key (.getName key) }) "quartz.scheduler.job-deleted"))
(defn -jobPaused
[this ^JobKey key]
(publish this (json/json-str { :group (.getGroup key) :key (.getName key) }) "quartz.scheduler.job-paused"))
(defn -jobsPaused
[this ^String job-group]
(publish this (json/json-str { :group job-group }) "quartz.scheduler.jobs-paused"))
(defn -jobResumed
[this ^JobKey key]
(publish this (json/json-str { :group (.getGroup key) :key (.getName key) }) "quartz.scheduler.job-resumed"))
(defn -jobsResumed
[this ^String job-group]
(publish this (json/json-str { :group job-group }) "quartz.scheduler.jobs-resumed"))
</code></pre><h3 id="inspecting-class-signatures">Inspecting Class Signatures</h3><p>When using <code>gen-class</code> for interoperability purposes, sometimes it is necessary to inspect the API
of the class generated by <code>gen-class</code>.</p><p>It can be inspected
using <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javap.html">javap</a>. Given the
following Clojure namespace:</p><pre><code class="clojure">(ns genclassy.core
(:gen-class))
(defn -main
[&amp; args]
(println "Hello, World!"))
</code></pre><p>We can inspect the produced class like so:</p><pre><code># from target/classes, default .class files location used by Leiningen
javap genclassy.core
</code></pre><p>will output</p><pre><code class="java">public class genclassy.core {
public static {};
public genclassy.core();
public java.lang.Object clone();
public int hashCode();
public java.lang.String toString();
public boolean equals(java.lang.Object);
public static void main(java.lang.String[]);
}
</code></pre><h2 id="how-to-extend-protocols-to-java-classes">How To Extend Protocols to Java Classes</h2><p>Clojure protocols can be extended to any java class (including
Clojure's internal types) very easily using <code>extend</code>:</p><p>Using the example of a json library, we can define our goal as getting
to the point where the following works:</p><pre><code class="clojure">(json-encode (java.util.UUID/randomUUID))
</code></pre><p>First, let's start with the protocol for json encoding an object:</p><pre><code class="clojure">(defprotocol JSONable
(json-encode [obj]))
</code></pre><p>So, everything that is "JSONable" implements a <code>json-encode</code> method.</p><p>Next, let's define a dummy method to do the "encoding" (in this
example, it just prints to standard out instead, it doesn't actually
do any json encoding):</p><pre><code class="clojure">(defn encode-fn
[x]
(prn x))
</code></pre><p>Now, define a method that will encode java objects by calling <code>bean</code>
on them, then making each value of the bean map a string:</p><pre><code class="clojure">(defn encode-java-thing
[obj]
(encode-fn
(into {}
(map (fn [m]
[(key m) (str (val m))])
(bean obj)))))
</code></pre><p>Let's try it on an example object, a UUID:</p><pre><code class="clojure">(encode-java-thing (java.util.UUID/randomUUID))
;; ⇒ {:mostSignificantBits "-6060053801408705927",
;; :leastSignificantBits "-7978739947533933755",
;; :class "class java.util.UUID"}
</code></pre><p>The next step is to extend the protocol to the java type, telling
clojure which java type to extend, the protocol to implement and the
method to use for the <code>json-encode</code> method:</p><pre><code class="clojure">(extend java.util.UUID
JSONable
{:json-encode encode-java-thing})
</code></pre><p>Alternatively, you could use the <code>extend-type</code> macro, which actually
expands into calls to <code>extend</code>:</p><pre><code class="clojure">(extend-type java.util.UUID
JSONable
(json-encode [obj] (encode-java-thing obj)))
</code></pre><p>Now we can use <code>json-encode</code> for the object we've extended:</p><pre><code class="clojure">(json-encode (java.util.UUID/randomUUID))
;; ⇒ {:mostSignificantBits "3097485598740136901",
;; :leastSignificantBits "-9000234678473924364",
;; :class "class java.util.UUID"}
</code></pre><p>You could also write the function inline in the extend block, for
example, extending <code>nil</code> to return a warning string:</p><pre><code class="clojure">(extend nil
JSONable
{:json-encode (fn [x] "x is nil!")})
(json-encode nil)
;; ⇒ "x is nil!"
</code></pre><p>The <code>encode-java-thing</code> method can also be reused for other Java types
we may want to encode:</p><pre><code class="clojure">(extend java.net.URL
JSONable
{:json-encode encode-java-thing})
(json-encode (java.net.URL. "http://aoeu.com"))
;; ⇒ {:path "",
;; :protocol "http",
;; :authority "aoeu.com",
;; :host "aoeu.com",
;; :ref "",
;; :content "sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@4ecac02f",
;; :class "class java.net.URL",
;; :defaultPort "80",
;; :port "-1",
;; :query "",
;; :file "",
;; :userInfo ""}
</code></pre><h2 id="using-intrinsic-locks-synchronized-in-clojure">Using Intrinsic Locks ("synchronized") in Clojure</h2><p>Every object on the JVM has an <em>intrinsic lock</em> (also referred to as <em>monitor lock</em>
or simply <em>monitor</em>). While very rarely necessary, Clojure provides support for
operations that acquire intrinsic lock of a mutable Java object.</p><p>This is covered in the <a href="../concurrency_and_parallelism/index.html#using_intrinsic_locks_synchronized_in_clojure">Concurrency and Parallelism guide</a>.</p><h2 id="wrapping-up">Wrapping Up</h2><p>TBD: <a href="https://github.com/clojure-doc/clojure-doc.github.io#how-to-contribute">How to Contribute</a></p><h2 id="contributors">Contributors</h2><p>Michael Klishin <a href="mailto:michael@defprotocol.org">michael@defprotocol.org</a> (original author)
Lee Hinman <a href="mailto:lee@writequit.org">lee@writequit.org</a>
gsnewmark <a href="mailto:gsnewmark@meta.ua">gsnewmark@meta.ua</a></p>
<div id="prev-next">
<a href="../laziness/index.html">&laquo; Laziness in Clojure</a>
||
<a href="../macros/index.html">Clojure Macros and Metaprogramming &raquo;</a>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div id="sidebar">
<h3>Links</h3>
<ul id="links">
<li><a href="../../about/index.html">About</a></li>
<li><a href="../../content/index.html">Table of Contents</a></li>
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
<li><a href="../core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
<li><a href="../namespaces/index.html">Clojure Namespaces and Vars</a></li>
<li><a href="../collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
<li><a href="../functions/index.html">Functions in Clojure</a></li>
<li><a href="../laziness/index.html">Laziness in Clojure</a></li>
<li><a href="index.html">Clojure interoperability with Java</a></li>
<li><a href="../macros/index.html">Clojure Macros and Metaprogramming</a></li>
<li><a href="../polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
<li><a href="../concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
<li><a href="../glossary/index.html">Clojure Terminology Guide</a></li>
<li><a href="../../ecosystem/libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
<li><a href="../../ecosystem/libraries_authoring/index.html">Library Development and Distribution</a></li>
<li><a href="../../ecosystem/generating_documentation/index.html">Generating Documentation</a></li>
<li><a href="../../ecosystem/data_processing/index.html">Data Processing (Help Wanted)</a></li>
<li><a href="../../ecosystem/web_development/index.html">Web Development (Overview)</a></li>
<li><a href="../../ecosystem/maven/index.html">How to use Maven to build Clojure projects</a></li>
<li><a href="../../ecosystem/community/index.html">Clojure Community</a></li>
<li><a href="../../ecosystem/user_groups/index.html">Clojure User Groups</a></li>
<li><a href="../../ecosystem/running_cljug/index.html">Running a Clojure User Group</a></li>
<li><a href="../../ecosystem/books/index.html">Books about Clojure and ClojureScript</a></li>
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
<li><a href="../../ecosystem/java_jdbc/home.html">java.jdbc - Getting Started</a></li>
<li><a href="../../ecosystem/java_jdbc/using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
<li><a href="../../ecosystem/java_jdbc/using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
<li><a href="../../ecosystem/java_jdbc/reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
<li><a href="../../ecosystem/core_typed/home/index.html">core.typed - User Documentation Home</a></li>
<li><a href="../../ecosystem/core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
<li><a href="../../ecosystem/core_typed/rationale/index.html">core.typed - Rationale</a></li>
<li><a href="../../ecosystem/core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
<li><a href="../../ecosystem/core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
<li><a href="../../ecosystem/core_typed/types/index.html">core.typed - Types</a></li>
<li><a href="../../ecosystem/core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
<li><a href="../../ecosystem/core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
<li><a href="../../ecosystem/core_typed/filters/index.html">core.typed - Filters</a></li>
<li><a href="../../ecosystem/core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
<li><a href="../../ecosystem/core_typed/loops/index.html">core.typed - Looping constructs</a></li>
<li><a href="../../ecosystem/core_typed/function_types/index.html">core.typed - Functions</a></li>
<li><a href="../../ecosystem/core_typed/limitations/index.html">core.typed - Limitations</a></li>
</ul>
</div>
</div>
</div>
<footer>Copyright &copy; 2021 Multiple Authors
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
</div>
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
<script>hljs.initHighlightingOnLoad();</script>
</body>
</html>