This is a Clojure library for Clojure/Spring integration.
Well, it is not really a library because there is not that much code. I'll show how to define Spring beans in Clojure in a way that enables you to use Clojure in a Java-based Spring application without touching either the Java code nor the (existing) Spring bean definitions.
For the examples below I assume that the Java-based Spring application uses XML bean definition files to define the Spring application context. If I find the time I will supply examples for other definition strategies (like via annotations) as well.
I'll use leiningen to run the code. You should be able to adopt the code and the examples to cases where you don't use leiningen (as it is the standard case for Java-based applications). Using leiningen makes some tasks a lot easier (like setting up the classpath, running tests, etc.) but it does hide details that you have to know about when you're not using it. So I'll try to give plain Java examples as well.
We'll need a driver app to run our code. We have one in Clojure and one in Java.
You can run the driver (spring-break.core/-main
)
via lein
(see :main spring-break.core
in project.clj
). It will build a Spring application context from
XML definitions files -- i.e. resources -- (first arg) and then
retrieve Spring beans by their id
(remaining optional args) from
the application context and print those to stdout.
Try this:
lein run spring-config-empty.xml
The XML definitions files will be loaded via classloader/classpath
which is ./resources/spring-config-empty.xml
in this case.
This run doesn't do much. It is just there to ensure that everything is set up right. We'll load Clojure-based Spring beans in just a minute.
When using Spring it's usually quite clear how to start the Spring based application but many wonder how to shut it down correctly.
Shutting down a Spring application context means
that org.springframework.context.ConfigurableApplicationContext.close()
is called. Calling close()
on a closed (or [con]currently
closing) Spring context is a no-op.
There are several ways the call to close()
can be initiated:
-
web-app: In this case you're using one of the
org.springframework.web.context.WebApplicationContext
implementations. These classes will hook into the shutdown of theServletContext
and callclose()
when theServletContext
shuts down. -
(active) tool-app: In this case the tool's main thread will create the Spring application context, retrieve beans from it via
getBean()
and use them for whatever the tool is supposed to do. So the main thread drives the business logic in this case. When the tool is done the tool's main thread callsclose()
and then exists. The call toclose()
may be wrapped in afinally
so that the shutdown is performed even in case of a non-handled exception. -
(passive) server-app: In this case the server's main thread will only create (but not use) the Spring application context. Typically some of the beans will be made available to remote clients (through beans within the Spring application context itself -- not the driver code -- e.g. RMI, HTTP, JMX). So in this case the business logic is driven by calling clients.
The shutdown must then be initiated externally --- e.g. by a client call through a bean that eventually calls
close()
. The server's main thread just waits for the Spring application context having shut down. Note that theclose()
may or may not be executed concurrently to the client's calling thread depending on your implementation (so the client may be waiting until the shutdown has completed or not). The thread that initiates theclose()
(either synchronuously or concurrently) must not use any of the closing Spring application context's functionality after having initiated theclose()
. -
Using registerShutdownHook for shutdown (not)
There is the option to hook the Spring shutdown into the JVM shutdown (see
src/main/clojure/spring_break/core.clj
):(let [sac (proxy [org.springframework.context.support.ClassPathXmlApplicationContext] [conf] [...])] (.registerShutdownHook sac) [...]
By doing so one tries to have the Spring application context being shutdown even if the JVM goes down for some other reason than mentioned above (i.e. cases where you do not control/initiate the termination of the JVM by your application code). You could even choose to not call
close()
at all in your application code and just rely on the shutdown-hook being called when the JVM goes down.But in these cases your code may not be executed completely (or not at all; SIGKILL) because the JVM will give your code only a limited amount of time for completion. Then again there is the chance of not-exiting (SIGHUP) if the shutdown-hook thread runs into an endless loop.
So IMHO it's not a good idea to depend the gracefull shutdown on
registerShutdownHook
and JVM shutdown. Instead I'm using a controlled call toclose()
. Nevertheless I'm using the shutdown hook for cases where my code does not control the shutdown.
The call to close()
will be done either within the driver
(tool-app) or by business code (server-app). See Shutting down
Spring via JMX below.
The driver I'm using for this project can be used as an active tool as well as a passive server (or both at the same time).
This is the relevant part of the driver code:
(defn -main [conf & bean-names]
(let [closed (promise)
sac (proxy [org.springframework.context.support.ClassPathXmlApplicationContext][conf]
(close []
(try
(proxy-super close)
(finally
(deliver closed :doesnt-matter)))))]
(log "Getting beans: [%s]" bean-names)
(dorun
(for [bean-id bean-names
:let [bean (.getBean sac bean-id)]]
(log "bean '%s' = '%s' (%s)"
bean-id
bean
(when bean (.getClass bean)))))
(if (System/getProperty "wait-for-sac-close")
@closed
(.close sac))
(when (System/getProperty "do-system-exit-0")
(System/exit 0))))
-
The driver has a tool-app part (within the
(dorun)
). -
The driver can be used as a server-app (by setting the system property
wait-for-sac-close
) -
When run as a tool-app the Spring application context gets shut down via the driver's
(.close sac)
(notfinally
wrapped here). -
When run as a server-app the driver's main threads waits on a
@(promise)
(that's a lot easier than await
for anotify
which often leads to missed wake-ups if you not really know what you're doing).ClassPathXmlApplicationContext
has an empty methodonClose()
which derived classes are supposed to override. But Spring'sclose()
implementation does not wrap the call toonClose()
in afinally
so I rather overrideclose()
. In addition you can check viaisActive()
if the shutdown has succeeded or not.
You can run the driver as a server-app like this:
lein trampoline with-profile server-app run spring-config-empty.xml
Since there is no business code that closes the Spring application context in this case the driver's main thread will wait forever and the JVM will not exit. You have to kill (ctrl-c) this process.
Note: I'm using (spring-break.core/log)
instead of Clojure's
print-functions because they use clojure.core/*out*
which is a
buffering wrapper around System/out
. We'll be using Clojure
and Java stuff (Spring!) at the same time. So in order to see the
Clojure-output in sync with the Java-output we shouldn't buffer either
of them.
Spring
offers org.springframework.context.support.AbstractApplicationContext.registerShutdownHook()
which registers a shutdown-hook (i.e. Thread
instance) in your
current Runtime
. This shutdown-hook will call doClose()
on
the Spring application context (if it has not been closed already).
N.B.: That's why I override doClose
and not close
in the
driver. This way the main driver thread (which is waiting on
@closed
) will be woken up when the close
/doClose
has
completed (initiated either via shutdown-hook or via a call to
close()
from business code). This may or may not be what you
want. Just remember that the JVM will halt as soon as all
shutdown-hook threads have completed and thus the main driver thread
may run to completion or it may be halted. Instead of putting the
registration into the driver you could refactor it into a Spring
bean.
The JVM will call all shutdown-hooks when it is about to exit. There are several reasons for the JVM to exit:
-
(a)
System/exit
has been called -
(b) there are only daemon-threads running
-
(c) a signal has been received (e.g. ctrl-c,
kill -HUP
)
Note that there are cases when the JVM will terminate but will not call shutdown-hooks:
-
(d)
kill -KILL
-
(e)
System/halt
The JVM will run all shutdown-hooks (i.e. Thread
s) to completion
before exiting. If any of those threads does not complete
(i.e. terminate) the JVM will keep on running forever (unless
halted).
Any other running threads keep on running concurrently to the shutdown-hook threads.
Once all shutdown-hook threads have terminated the JVM will exit. Even if there are any non-daemon-threads running.
If you want to play around with this, have a look at
src/main/clojure/no-namespace-scripts/shutdown-hook.clj
. This
does not use Spring - it's just there to have a mini-app to try out.
You can use registerShutdownHook()
in order to shut-down the
Spring application context for cases where your own code did not get a
chance to close()
it explicitly. I would not use it as the
standard way to close down your application though.
The driver from above does use it. Notice though that there are some unexpected things that may happen.
-
the driver's main thread may not be able to print
done.
after the shutdown-hook thread has completed. Usually you will seedone.
but there is no guarantee. -
the code that is run by the shutdown-hook thread may start-up things again after they had been closed down (e.g. Clojure's agent threadpool). Since non-daemon-threads will not keep the JVM up after the shutdown-hooks have run, this may not be a problem. But still it may come as a surprise in some situations.
The driver from above will run the main thread to completion. If this was the only non-daemon thread the JVM would exit after this. But there may be cases when the business code (i.e. Spring beans) starts non-daemon threads (like swank and Clojure's agent threadpool). These have to be shut-down when the Spring application context is shut-down.
Below you find some examples for this. If you want to make sure your
application exists even if you're using mis-behaved Spring beans you
can use -Ddo-system-exit-0
so that the driver terminates with
(System/exit 0)
.
The first time you run the example above, leiningen will download all
the required JARs to ./local-m2/
(see :dependencies
in project.clj
) so you'll need an internet connection. I
put :local-repo "local-m2"
in there so that I can easily track what
JARs we need for the integration case.
You can use
lein deps
to just resolve dependencies and to update your repository.
Run this:
lein classpath
This will give you the classpath for your project.
You'll need your project's classpath if you want to run our code
without lein
. Try:
CP=`lein classpath`
java -cp ${CP} clojure.main -m spring-break.core spring-config-empty.xml
This will save you the start-up time of lein
(assuming that you
execute the java
call more than once) but you have to update
your ${CP}
when you change the project dependencies (which will
not be that often once your project has stablized).
You run the driver as a server-app like this:
java -cp ${CP} -Dwait-for-sac-close clojure.main -m spring-break.core spring-config-empty.xml
This needs to be worked over
When you don't have an internet connection, you want to use
lein trampoline with-profile production run spring-config-empty.xml
TODO: introduce a profile offline for this
In this case leiningen will not try to check and download any dependencies.
Since I want to show how to integrate Clojure- and Java-based Spring
beans, I need some Java code as well.
In ./src/main/java/javastuff/Driver.java
you'll find the driver
from above implemented in Java. We won't need it that much.
Compile:
lein javac -verbose
Run:
CP=`lein classpath`
java -cp ${CP} javastuff.Driver spring-config-empty.xml
TODO: port Clojure driver to Java driver incl. recent enhancements
You can use lein
to create an uberjar (aka shaded
jar). Usually you do this when delivering your application to end
users:
lein uberjar
java -cp target/spring-break-0.1.0-SNAPSHOT-standalone.jar javastuff.Driver spring-config-empty.xml
java -cp target/spring-break-0.1.0-SNAPSHOT-standalone.jar clojure.main -m spring-break.core spring-config-empty.xml
This builds an uberjar (which contains all classes and resources of all of the JARs and resources in you project dependencies -- i.e. classpath) and then runs our Java-based driver and the Clojure-based driver.
There are is a gotcha when using an uberjar: each file can only
exist once in the uberjar (or in any jar/zip). So if we have a
resource spring-config.xml
in two of the JARs/resource folders
we can only put one of them into the uberjar. lein will warn us in
this case.
Try this:
-
create dir
./dummy-data/
-
copy
./resources/spring-config-empty.xml
to./dummy-data/
-
expand the project's classpath in
project.clj
like this::resource-paths ["resources" "dummy-data"]
-
then build the uberjar again:
lein uberjar Warning: skipped duplicate file: spring-config-empty.xml Created /home/user/spring-break/target/spring-break-0.1.0-SNAPSHOT.jar Created /home/user/spring-break/target/spring-break-0.1.0-SNAPSHOT-standalone.jar
You will be warned about the duplicate. If you want to take care of
this case automatically, you can tell leiningen how to process the
duplicate files: in this case I deciced to just throw an exception
(see project.clj
)
:uberjar-merge-with { #".*" [fail-fn fail-fn fail-fn]}
TODO: this does not work like expected/described: seems the fail-fn is not executed!
There seems to be a bug in lein. I get:
lein uberjar
Warning: skipped duplicate file: spring-config-empty.xml
Created /home/user/spring-break/target/spring-break-0.1.0-SNAPSHOT.jar
java.util.zip.ZipException: ZIP file must have at least one entry
at java.util.zip.ZipOutputStream.finish(ZipOutputStream.java:321)
at java.util.zip.DeflaterOutputStream.close(DeflaterOutputStream.java:163)
at java.util.zip.ZipOutputStream.close(ZipOutputStream.java:338)
at leiningen.uberjar$uberjar$fn__3657.invoke(uberjar.clj:137)
at leiningen.uberjar$uberjar.invoke(uberjar.clj:137)
at leiningen.uberjar$uberjar.invoke(uberjar.clj:149)
[...]
Since the namespace spring-break.core
is AOT-compiled and has
a (:gen-class)
directive, we can run
(see spring-break-0.1.0-SNAPSHOT-standalone.jar!/meta-inf/manifest.mf
):
java -jar target/spring-break-0.1.0-SNAPSHOT-standalone.jar spring-config-empty.xml
Spring has several built-in instantiation strategies for beans. One of
them lets you name a class
, an (instance) factory-method
and any number of constructor-arg
which again may be Spring
beans (nested bean
element or referenced by ref
attribute)
or of any of the built-in value types (e.g. java.lang.String
).
So we may do this:
<bean id="hello_world"
class="clojure.lang.Compiler"
factory-method="load">
<constructor-arg>
<bean class="java.io.StringReader">
<constructor-arg value='"Hello world!"' />
</bean>
</constructor-arg>
</bean>
Run:
lein run spring-config-hello-world.xml hello_world
Now try the Java-based driver:
CP=`lein classpath`
java -cp ${CP} javastuff.Driver spring-config-hello-world.xml hello_world
This will give you a java.lang.NullPointerException
:
[...]
Caused by: java.lang.ExceptionInInitializerError
at clojure.lang.Compiler.<clinit>(Compiler.java:47)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:160)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:592)
... 14 more
Caused by: java.lang.NullPointerException
at clojure.lang.RT.baseLoader(RT.java:2043)
at clojure.lang.RT.load(RT.java:417)
at clojure.lang.RT.load(RT.java:411)
at clojure.lang.RT.doInit(RT.java:447)
at clojure.lang.RT.<clinit>(RT.java:329)
... 21 more
The cause is that we first load the class clojure.lang.Compiler
which in turn loads clojure.lang.RT
. Usually when using Clojure
(e.g. the Clojure-based driver above) clojure.lang.RT
is
loaded first and there seems to be some ordering dependency within the
static class initializers (clojure.lang.Compiler
probably is not
part of the Clojure API so that's fine). So we have to make the JVM
load clojure.lang.RT
before Spring creates our hello_world
bean --- like this:
<bean id="load_clojure_lang_rt" class="clojure.lang.RT" factory-method="nextID" />
<bean id="hello_world"
class="clojure.lang.Compiler"
depends-on="load_clojure_lang_rt"
factory-method="load">
<constructor-arg>
<bean class="java.io.StringReader">
<constructor-arg value='"Hello world!"' />
</bean>
</constructor-arg>
</bean>
Here we just call some static method in clojure.lang.RT
to
make Spring load the class.
Run:
CP=`lein classpath`
java -cp ${CP} javastuff.Driver spring-config-hello-world-for-java-driver.xml hello_world
You can load Clojure script files via clojure.lang.Compiler/loadFile
:
<bean id="load_script_code"
class="clojure.lang.Compiler"
factory-method="loadFile">
<constructor-arg value="src/main/clojure/no-namespace-scripts/script-code.clj" />
</bean>
Now run this:
lein run spring-config-load-script.xml
You'll see the output of script-code.clj
. Notice that
evalutation starts in namespace user
. We are not loading a
namespace but a plain script. (I use dashes here in the directory
name - so this cannot be a namespace!)
Loading a script file will usually not create any instances that you use as Spring beans. You load this script for its side-effects (i.e. defines or just the output to stdout in this simple example).
Try this:
lein run spring-config-load-script.xml load_script_code
You'll see that the Spring bean load_script_code
has the
value null
--- that is the value of the last form that is
evaluated. Try putting this
into src/main/clojure/no-namespace-scripts/script-code.clj
(.print System/out (format "+++ loading %s in namespace '%s'\n" *file* *ns*))
"foobar"
and re-run.
TODO: do this via classloader instead of plain file IO, since that would work with uberjar as well
You should get into the habit of using id
attributes to name
your spring beans. If you don't, you may run into situations where you
have more than one Spring bean defined.
This happens when using classpath*:
and Spring finds your Spring
bean definition files more than one time (e.g. when your classpath has
more than one entry to a JAR containing the resource). If you
use id
Spring will judge this case as a bean redefinition instead
of the definition of two separate beans. This can make a big
difference!
Try this:
-
Remove the
id="load_script_code"
fromresources/spring-config-load-script.xml
-
build the uberjar:
lein uberjar
-
Run the driver app with
classpath\*:spring-config-load-script.xml
(this tells Spring to load all occurences of the named resource) and an extended classpath (:./resources/
). This way we have the resourcespring-config-load-script.xml
twice in our classpath:java -cp target/spring-break-0.1.0-SNAPSHOT-standalone.jar:./resources/ \ clojure.main -m spring-break.core classpath\*:spring-config-load-script.xml
You should see the output of script-code.clj
twice. Depending on
what the code (i.e. the loaded Spring bean) does, it may make a
difference. Now insert id="load_script_code"
back in and re-run
(don't forget lein uberjar
!). See?
In resources/spring-config-load-script-with-sideeffect.xml
I
put a second bean definition that calls the function foo/foobar
which gets def
ined
in src/main/clojure/no-namespace-scripts/script-code-with-function.clj
. Note
that we have to explicitly name the namespace foo
because
this code is eval
uated in namespace user
.
<bean id="foobar"
class="clojure.lang.Compiler"
depends-on="load_script_code"
factory-method="load">
<constructor-arg>
<bean class="java.io.StringReader">
<constructor-arg value='(foo/foobar "bar")' />
</bean>
</constructor-arg>
</bean>
<bean id="load_script_code"
class="clojure.lang.Compiler"
factory-method="loadFile">
<constructor-arg
value="src/main/clojure/no-namespace-scripts/script-code-with-function.clj" />
</bean>
Note that I put depends-on="load_script_code"
in the bean
definition. This way we tell Spring that this bean needs another
bean to be instanciated before itself can be instanciated. In this
simple example we could have fixed the problem by just reverting the
order of the bean definitions. But using depends-on
will work
even in complex bean definition set-ups.
And here's the Clojure code for reference.
(printf "+++ loading %s in namespace '%s'\n" *file* *ns*)
(ns foo)
(defn foobar [& args]
(printf "+++ Calling (%s %s)\n" foobar args)
(vec args))
Run the example:
lein run spring-config-load-script-with-sideeffect.xml foobar
To load a namespace we can just require
it:
<bean id="load_namespace"
class="clojure.lang.Compiler"
factory-method="load">
<constructor-arg>
<bean class="java.io.StringReader">
<constructor-arg value="(require 'spring-break.the-code)" />
</bean>
</constructor-arg>
</bean>
Run:
lein run spring-config-load-namespace.xml load_namespace
Try the multiple load example from above. There will be two bean definition, but Clojure will load the namespace only once!
Since require
always returns nil
you will see null
regardless of what is in the namespace. But still the last evaluated
form gets returned. And of course you can put it into the XML
defintion as well --- i.e. you can feed more than one form
to clojure.lang.Compiler/load
--- like this:
<constructor-arg value="(require 'spring-break.the-code) :foobar" />
The nested <bean><constructor-arg><bean><constructor-arg>
XML
definition is a bit too clumsy. I would like to just use one
non-nested structure.
So let's define our first usefull Spring Bean. The bean will be
whatever we put into spring-break.factories/clojure-object-factory
<bean id="clojure_factory"
class="clojure.lang.Compiler"
depends-on="load_clojure_lang_rt"
factory-method="load">
<constructor-arg>
<bean class="java.io.StringReader">
<constructor-arg
value="(require 'spring-break.factories)
spring-break.factories/clojure-object-factory" />
</bean>
</constructor-arg>
</bean>
In src/main/clojure/spring_break/factories.clj
I put
-
the factory function
(defn compiler-load [s] (clojure.lang.Compiler/load (java.io.StringReader. s)))
-
a protocol so that we have a named method that we can tell Spring to call
(defprotocol object-factory (new-instance [this s]))
-
and an object of the protocol type which will be our Spring bean
(def clojure-object-factory (reify object-factory (new-instance [this s] (compiler-load s))))
Now we can do this (note that we have to use the Java-name of the method)
<bean id="a_clojure_bean"
factory-bean="clojure_factory"
factory-method="new_instance">
<constructor-arg
value=":it-works" />
</bean>
There is one more tweak: I wanted to DRY out the XML definition a
little more, so I defined a abstract parent. Now all the
Clojure-based beans just have to name their id
, parent
and
their code.
<bean id="clojure_fact"
abstract="true"
factory-bean="clojure_factory"
factory-method="new_instance">
</bean>
<bean id="a_clojure_bean"
parent="clojure_fact">
<constructor-arg
value=":it-works" />
</bean>
I set up an XML file for this. So you may try:
lein run spring-config-factories.xml a_clojure_bean
Note that I put the load_clojure_lang_rt
bean definition in
there too. So we can run this via the Java-based driver as well:
CP=`lein classpath`
java -cp ${CP} javastuff.Driver spring-config-factories.xml a_clojure_bean
There is one more bean definition that I put in here because we will need it for all the remaining examples:
<bean id="shutdown_agents"
parent="clojure_fact">
<constructor-arg
value="
(require 'spring-break.shutdown-agents)
(spring-break.shutdown-agents/make-shutdown-agent-bean)
" />
</bean>
This bean will participate in the lifecycle of the Spring application context and will shut-down the agent threadpool when Spring is shut-down.
As a special side-effect you can start swank:
<import resource="spring-config-factories.xml" />
<bean id="run_swank"
parent="clojure_fact">
<constructor-arg
value='
(ns user
(:use swank.swank))
(println "+++ Starting swank server")
(future
(start-server :port 4007))
' />
</bean>
Here we re-use the clojure_fact
Spring bean from above and start
the swank server. We use a future
to do this in a separate
thread so that we will not slow down the boot of the Spring
application context and to drop any exception that may come out of
that call (e.g. if the port is already in use).
lein run spring-config-swank.xml
Note that the swank server keeps the JVM from exiting even though the Spring application context has been shut-down and the main thread has completed. This is because there are non-daemon threads running for swank.
In src/main/clojure/spring_break/swank.clj
you find an example
of how to start and gracefully shut-down swank. In this example I
still tried to start swank concurrently via future
. Since the
driver is shut-down right after start-up we run the danger of a
race-condition: the call to (stop)
happens before swank has
started so calling (stop-server)
will do nothing. So I
introduced an explicit synchronization via a (promise)
. So only
after swank did start-up will stop-server
be called.
Such race-conditions may pop-up in other circumstances as well.
(defn swank-starter []
(let [state (atom {})
started (promise)]
(reify
org.springframework.context.SmartLifecycle
(getPhase [this] 0)
(isAutoStartup [this]
true)
(isRunning [this]
(boolean (:running @state)))
(start [this]
(swap! state assoc :running true)
(future
(start-server :port 4007)
(deliver started :started)))
(stop [this runnable]
@started
(stop-server)
(swap! state assoc :running false)
(.run runnable)))))
The bean looks like this:
<bean id="run_swank"
parent="clojure_fact">
<constructor-arg
value="
(require 'spring-break.swank)
(spring-break.swank/swank-starter)
" />
</bean>
And run:
java -cp ${CP} clojure.main -m spring-break.core spring-config-gracefull-swank.xml
Starting an nrepl server is just as easy:
<import resource="spring-config-factories.xml" />
<bean id="run_nrepl"
parent="clojure_fact">
<constructor-arg
value='
(ns user
(:require [clojure.tools.nrepl.server]))
(println "+++ Starting nrepl server ...")
(def server (clojure.tools.nrepl.server/start-server :port 7888))
(println "+++ nrepl server started.")
' />
</bean>
And run like this:
lein run spring-config-nrepl.xml
To connect to this nrepl server use:
lein repl :connect 7888
Note that this won't work:
<import resource="spring-config-factories.xml" />
<bean id="run_swank"
parent="clojure_fact">
<constructor-arg
value='
(ns user
(:use swank.swank))
(println "+++ Starting swank server")
;; Start swank
(future
(start-server :port 4007))
' />
</bean>
The XML text content will be given to the Compiler/load
function
as a one line String with no line-breaks. So the comment will
comment out everything after the ;; Start swank
. Use #_
and (comment)
instead.
One of the use-cases of the Clojure/Spring integration is to wrap Java-based Spring beans with your Clojure code (proxying). Since this use-case is not limited to Clojure but is omni-present in Spring AOP, there exist some Spring classes to support this.
For the following examples I will use some Java classes as a placeholder for the Java-based application and then show how to wire the Clojure code into that.
Assume that you have a Java-based Spring bean some_bean
which is
used by (and injected into) a second Java-based bean some_bean_user
(both being of class javastuff.AppCode$SomeBusinessImpl
). This is the standard
case for Java-based applications since that is what most people use
Spring for: wiring Java instances.
<bean id="some_bean" class="javastuff.AppCode$SomeBusinessImpl" />
<bean id="some_bean_user" class="javastuff.AppCode$SomeBusinessImpl">
<property name="other" ref="some_bean" />
</bean>
And you have some Clojure code that you want to wrap around some_bean
.
We put that into bean my_interceptor
.
<bean id="my_interceptor"
parent="clojure_fact">
<constructor-arg value="(require 'spring-break.proxying)
spring-break.proxying/my-interceptor" />
</bean>
Here's the code for reference:
TODO: unroll the InvocationTargetException before re-throwing
(def my-interceptor
(proxy [org.aopalliance.intercept.MethodInterceptor][]
(invoke [invctn]
(let [m (.getMethod invctn)
t (.getThis invctn)
a (vec (.getArguments invctn))
_ (printf "+++ Calling method %s on %s with %s\n" m t a)
res (try
{:res (.proceed invctn)}
(catch Throwable t {:ex t}))]
(printf "+++ DONE: Calling method %s on %s with %s %s\n"
m t a
(if-let [r (:res res)]
(format "returns '%s'" r)
(format "fails due to %s" (:ex res))))
(if-let [r (:res res)]
r
(throw (:ex res)))))))
The easiest way to do this is with a BeanNameAutoProxyCreator
:
<bean id="creator"
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="some_bean"/>
<property name="interceptorNames">
<list>
<value>my_interceptor</value>
</list>
</property>
</bean>
And run:
lein run spring-config-with-BeanNameAutoProxyCreator.xml some_bean
In this example we implemented an around advice --- i.e. we supplied code that runs before the proxied code and after the proxied code. This is the most general case of wrapping. We could have decided to not delegate to the proxied code at all, effectively replacing the code/bean. Or we could have done something with the parameters/arguments (i.e. changed/replaced) before passing them to the proxied code or we could have changed the returned value or mapped the exception in case one was thrown or whatever.
Spring has a lot more ways of specifying how to apply some code/beans to some other code/beans (e.g. by type, annotation, regular expressions, etc) --- see the Spring AOP documentation for details.
TODO: apply proxying without using an interface.
We can effectively replace a Java-based Spring bean via proxying (see above) but there is a much simpler way: just redefine the bean. Redefining a bean just means: defining a bean with the id of the bean you want to redefine (target bean) after the target bean has been defined.
One way to control this order is to use an import
.
<bean id="some_bean_user" class="javastuff.AppCode$SomeBusinessImpl">
<property name="other" ref="some_bean" />
</bean>
<bean id="some_bean" class="javastuff.AppCode$SomeBusinessImpl" />
<import resource="spring-config-redefine-some-bean.xml" />
Here we import
the redefinition of bean some_bean
:
<bean id="some_bean" parent="clojure_fact">
<constructor-arg value='(proxy [javastuff.AppCode$SomeBusinessInterface][]
(someMethod [p]
(printf "+++ Calling someMethod(%s)\n" p)
"foo"))
' />
</bean>
Try running
lein run spring-config-with-redefine.xml some_bean
and then comment out the import
and re-run. Note that the output
of the Java-based Spring bean is out-of-order compared to the output
of the Clojure-based one (don't know why yet).
Of course the Clojure-based bean must implement any client view
(i.e. all the classes and interfaces) through which the replaced bean
may be accessed by its users. Sometimes this may be impossible
(e.g. in case of final
classes).
Until now we have wired the Spring beans via nested property
elements within the bean
definition elements. But some
applications may use autowiring. In this case there will be
annotations in the Java code which mark the
injection/wiring-points. Spring will then search the application
context to find matching beans and autowire them. You may supply
explicit injection via property
to override but that is
optional.
The Java code may look like this:
@org.springframework.beans.factory.annotation.Autowired
private SomeCode m_autoWired;
And the Spring bean definition (spring-config-autowiring.xml
)
<context:annotation-config/>
<import resource="spring-config-factories.xml" />
<bean id="an_auto_wire_candidate"
parent="clojure_fact">
<constructor-arg value='(proxy [javastuff.AppCode$SomeCode][])' />
</bean>
<bean id="some_bean" scope="singleton" class="javastuff.AppCode$SomeBusinessImpl" />
And run:
lein run spring-config-autowiring.xml some_bean
Try changing the order of the bean definitions
(spring-config-autowiring-gotcha.xml
)
<bean id="some_bean" class="javastuff.AppCode$SomeBusinessImpl" />
<bean id="an_auto_wire_candidate"
parent="clojure_fact">
<constructor-arg value='(proxy [javastuff.AppCode$SomeCode][])' />
</bean>
And run:
lein run spring-config-autowiring-gotcha.xml some_bean
This will fail. When Spring tries to instanciate and autowire some_bean
it
has no class/type information for an_auto_wire_candidate
because that
bean is created by a factory
and since it is a singleton and has not been created yet, Spring does
not know about its type. Adding class="javastuff.AppCode.SomeCode"
to an_auto_wire_candidate
won't help --- Spring will not interpret
this as being the type of the bean.
You can fix this by letting some_bean
depend on an_auto_wire_candidate
(this is the most natural but also the
most invasive way of controlling bean instanciation order):
<bean id="some_bean" depends-on="an_auto_wire_candidate" class="javastuff.AppCode$SomeBusinessImpl" />
But there are other (more implicit) ways:
Make some_bean
a prototype
:
<bean id="some_bean" scope="prototype" class="javastuff.AppCode$SomeBusinessImpl" />
Or you make it lazy-init
:
<bean id="some_bean" lazy-init="true" class="javastuff.AppCode$SomeBusinessImpl" />
All of these fixes introduce an ordering so that some_bean
is
created after an_auto_wire_candidate
.
If you want to autowire your Clojure beans into an existing Java-based Spring application it is usually no option to change the (existing) Spring bean definitions.
Sometime you may be able to insert this into one of the bean definition files that get loaded early enough so that the beans that need those autowiring candidates come after that:
<import resource="classpath*:spring-config-clojure.xml" />
In this case Spring will try to load all occurances
of spring-config-clojure.xml
from the classpath but won't fail when
it can't find any (classpath*:
). This introduces the option of
placing such a file where the classloader can pick it up just when you
need it.
But still this solution has the drawback of depending on an ordering that may be hard to control.
The most elegant solution is this: we just use a dummy bean that is
a org.springframework.beans.factory.config.BeanFactoryPostProcessor
and let that depend on our Clojure-based beans. We cannot supply such
a bean via Clojure since Spring needs the class information for this
(and again that will be available too late). I just use one of the
Spring utility classes as a no-op just to have a hook that will
pull-in the Clojure-based beans.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
id="pull_in_clojure_beans"
depends-on="an_auto_wire_candidate">
<property name="placeholderPrefix" value="$$$do-not-use$$$" />
</bean>
Usually the beans that use autowiring will be normal business beans
that won't hook into the Spring application context lifecycle. So in
most cases we do not have to worry about if
our BeanFactoryPostProcessor
comes before any
other BeanFactoryPostProcessor
that may carry @Autowired
members
that should receive our Clojure-based beans.
If you run into such a situation please contact me and tell me how you got out of that :-)
Spring beans have a lifecycle:
-
construction (optionally with parameters)
-
property setting
-
initialization
-
shutdown
The following sections will show how Clojure-based Spring beans can participate in this lifecycle.
You find the complete code example
in src/main/clojure/spring_break/lifecycle.clj
and resources/spring-config-lifecycle.xml
:
<bean id="some_bean"
parent="clojure_fact">
<constructor-arg value="(require 'spring-break.lifecycle)
spring-break.lifecycle/some-bean" />
<property name="foo" value="FOO" />
<property name="bar" value="BAR" />
</bean>
And run:
lein run spring-config-lifecycle.xml some_bean
Using parameters with construction
via spring-break.factories/clojure-object-factory
is tricky and
clumsy. I haven't come up with a good solution for that.
Even doing this with Java-based beans is tricky, since in some cases
you have to resolve arity ambiguities via index
attribute values
in the constructor-arg
elements.
So we just won't use this and stick to property settings.
Spring uses Java beans setter methods to set bean properties. Before
Spring 3.1 these methods had to be void
(see [1] [2]).
The examples will only work with Spring 3.1 and above, because I
use defprotocol
to define the setter methods and those methods
return Object
. In case you want to use older Spring versions you
have to use AOT ([3]) or the ApplicationContextAware
alternative
shown below.
(defprotocol some-bean-java-bean
(setFoo [this v])
(setBar [this v]))
Now we implement that:
(def some-bean
(let [state (atom {})]
(reify
some-bean-java-bean
(toString [this] (str @state))
(setFoo [this v]
(swap! state assoc :foo v)
(core/log "after setFoo : %s" this))
(setBar [this v]
(swap! state assoc :bar v)
(core/log "after setBar : %s" this)))))
Note that there is nothing Spring-specific in this code (no Spring classes/interfaces).
TODO: For the boiler-plate code you should use a macro.
[1] http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/new-in-3.1.html
[2] https://jira.spring.io/browse/SPR-8079
[3] http://stackoverflow.com/questions/10634189/spring-bean-initialization-clojure
Spring lets you define your own init methods but you may use the
marker interface org.springframework.beans.factory.InitializingBean
as well:
(def some-bean
(let [state (atom {})]
(reify
org.springframework.beans.factory.InitializingBean
(toString [this] (str @state))
(afterPropertiesSet [this]
(core/log "afterPropertiesSet : %s" this)))))
Using the interfaces binds your code to Spring. You could
use init-method="<method>"
instead but then you have to define
a protocol
for that.
Note that in this case afterPropertiesSet
will be called ---
you cannot turn it off in the XML bean definition file.
Using init-method="<method>"
instead gives you that option.
The cleanup is similar:
(def some-bean
(let [state (atom {})]
(reify
org.springframework.beans.factory.DisposableBean
(toString [this] (str @state))
(destroy [this]
(core/log "destroy : %s" this)))))
Again you could use destroy-method="<method>"
and
a protocol
instead of the invasive use of the Spring interface.
Spring offers (Spring-specific) callback interfaces/methods for asynchronuous startup and shutdown. So your beans can participate in those phases of the lifecycle. See the Spring documentation for details. I put in the code just for completeness:
(def some-bean
(let [state (atom {})]
(reify
org.springframework.context.SmartLifecycle
(toString [this] (str @state))
(getPhase [this] 0)
(isAutoStartup [this]
(core/log "isAutoStartup : %s" this)
true)
(isRunning [this]
(core/log "isRunning : %s" this)
(boolean (:running @state)))
(start [this]
(core/log "start : %s" this)
(swap! state assoc :running true))
(stop [this runnable]
(core/log "stop : %s" this)
(.run runnable)))))
The Spring IoC container lets you define your beans and wire them. But Spring also lets you inject your own code into the Spring infrastructure. This way your code can participate in the lifycycle of the Spring application context (beyond the lifecycle of each bean; see above).
One way to inject your code is via Spring beans (of course!). Spring
will inspect all beans of the application context and will detect
those beans that can participate in the lifecycle (like
the org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
above). Once those beans are identified their callback methods will
be called by Spring during the appropriate lifecycle phase.
Sometimes you need programatic access to other Spring beans/bean
definitions. Spring will inject the application context into your bean
if the bean implements ApplicationContextAware
. Here I use it as
an alternative to properties setting (see above). This way we do not
need to define a protocol
.
In this bean definition we use a closure over the args
argument (the bean names we want to access) and then retrieve them
from the Spring application context in afterPropertiesSet
:
<bean id="some_bean"
parent="clojure_fact">
<constructor-arg value="
(require 'spring-break.application-context-aware)
(spring-break.application-context-aware/make-some-bean
:foo :a_bean
:bar :b_bean)" />
</bean>
Here's the (relevant part of the) Clojure code:
(defn consume-args [sac args]
(reduce-kv #(assoc %1 %2 (.getBean sac (name %3)))
{}
(apply hash-map args)))
(defn make-some-bean [& args]
(let [state (atom {})]
(reify
org.springframework.context.ApplicationContextAware
org.springframework.beans.factory.InitializingBean
(toString [this] (str @state))
(setApplicationContext [this v]
(swap! state assoc :sac v))
(afterPropertiesSet [this]
(swap! state merge (consume-args (:sac @state) args))))))
You could put a lot more functionality into consume-args
but
this should do for now.
Run like:
lein run spring-config-application-context-aware.xml some_bean
If you define a Spring bean of
type org.springframework.beans.factory.config.BeanPostProcessor
Spring will call-back on this bean for every bean instance that is
created.
For example:
<bean id="some_bean"
parent="clojure_fact">
<constructor-arg value='
(proxy [org.springframework.beans.factory.config.BeanPostProcessor][]
(postProcessAfterInitialization [o s]
(printf "+++ postProcessAfterInitialization on [%s] named [%s]\n" o s))
(postProcessBeforeInitialization [o s]
(printf "+++ postProcessBeforeInitialization on [%s] named [%s]\n" o s)))' />
</bean>
But again Spring will not detect (early enough) that this is
a BeanPostProcessor
because this bean is created by a factory method. We
have to pull it in early by adding (see above):
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
id="pull_in_clojure_beans"
depends-on="some_bean">
<property name="placeholderPrefix" value="$$$do-not-use$$$" />
</bean>
And run:
lein run spring-config-bean-post-processor.xml
TODO
TODO
TODO
TODO
Spring lets you register any Spring bean with a JMX MBeanServer (see
the Spring documentation for details). Here I want to show how to
register Clojure functions as JMX operations and Clojure's
mutable reference types (refs, atoms, vars) as JMX attribute. This
lets you call any of those funtions and change mutable state via
jconsole (or any other JMX client, e.g. org.clojure/java.jmx
).
The org.springframework.jmx.export.MBeanExporter
will drive
the registration/publishing. It iterates over all beans and calls-back
on our Clojure-based mbean-info-assembler
(nested Spring bean).
In this example we will publish any bean whose name matches
#"clj_.*"
to JMX.
Each bean will be published with a JMX ObjectName
of (format "clojure-beans:name=%s" bean-key)
(see namingStrategy
). This
will show-up in jconsole as the MBean name.
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="namingStrategy">
<bean parent="clojure_fact">
<constructor-arg value='
(proxy [org.springframework.jmx.export.naming.ObjectNamingStrategy][]
(getObjectName [bean-obj bean-key]
(javax.management.ObjectName.
(format "clojure-beans:name=%s" bean-key))))
' />
</bean>
</property>
<property name="assembler">
<bean parent="clojure_fact">
<constructor-arg value='
(require (symbol "spring-break.jmx"))
(spring-break.jmx/mbean-info-assembler
#(boolean (re-matches #"clj_.*" %)))
' />
</bean>
</property>
</bean>
The mbean-info-assembler
serves two purposes:
(1) It decides which beans will be included/published (via the
passed-in pred
)
(2) For functons it delivers the JMX view of the included
bean. For the reference types the bean itself is a
javax.management.DynamicMBean
which will be published as is to
JMX by Spring.
Here's the code: (more details below)
(defn mbean-info-assembler [pred]
(proxy [org.springframework.jmx.export.assembler.AutodetectCapableMBeanInfoAssembler] []
(includeBean [bean-class bean-name]
(let [incl? (pred bean-name)]
incl?))
(getMBeanInfo [bean-obj bean-name]
(make-model-mbean-info bean-obj bean-name))))
Now we can define the first Clojure function JMX bean:
<bean id="clj_echo" parent="clojure_fact">
<constructor-arg value="
(require 'spring-break.jmx)
(spring-break.jmx/fn-wrapper-of ^{:info :echo} (fn [a] a))
" />
</bean>
We have some function ((fn [a] a)
in this case)
and create the JMX view via fn-wrapper-of
.
TODO: do this via auto-proxying
TODO: change this to make-mbean
as for JMX attributes
The relevant part of the code looks like this:
(defn fn-wrapper-of [a-fn]
(proxy [java.text.Format] []
(parseObject [a-str]
(with-exception-mapping "Calling %s with [%s]" [a-fn a-str]
(let [arg (make-value-from-input (format "[%s]" a-str))
res (pr-str (apply a-fn arg))]
res)))))
There are some things to note:
-
jconsole can handle
java.lang.String
typed method paramters and build the GUI so that you can enter those parameter values. It won't let you enter anything forjava.lang.Object
typed parameters.TODO: what if we just claim that it a of type String even if it is not?
-
Since you may publish variadic functions, we may have to enter multiple parameter values and we don't know in advance how many that may be. That's something jconsole does not support (means: I haven't found out how to do it yet; of course jonsole supports entering more than one parameter value, but this number has to be fixed at JMX registration time).
So I'm using a wrapper method with just one
java.lang.String
parameter. The String input is then parsed via(read-string (format "[%s]" string-input))
. So you can enter multiple forms in the jconsole GUI text field which will be wrapped in avector
.Your function then gets called via
(apply <your-fn> <parsed-input>)
.Note that I'm using
read-string
so the user may call arbitrary code via#=(<code>)
reader macro before your function is in control. -
Your function's returned value will be deserialized over to a remote calling client. In order to prevent classloading problems the wrapper method transforms the returned value via
(pr-str)
into aString
. If your function throws anException
the wrapper method will create and throw aRuntimeException
instead and copy the originalException
's stacktrace into that (it actually copies the complete exception cause-chain that way). So you'll lose the exception's concrete type but you still get the stacktrace and message.In the end a remote calling client will either receive a
java.lang.String
(as of(pr-str)
) or aRuntimeException
both of which can be deserialized in any case. -
In order for JMX to be able to call the wrapper method it has to be a named method of the form
<some-type> <method-name>(String)
. Since I wanted to get by without using AOT I looked for some JDK interface with such a signature and foundString parseObject(String)
injava.text.Format
.That's why
fn-wrapper-of
returns a(proxy [java.text.Format])
withparseObject
being the wrapper method around your Clojure function. When you run jconsole you will seeparseObject
as the operation's name in the GUI.TODO: is there a way to display a different lable?
This time we use the driver as a server-app (see above):
lein trampoline with-profile server-app run spring-config-jmx.xml
or:
CP=$(JAVA_CMD=`which java` lein classpath)
java -cp ${CP} -Dwait-for-sac-close clojure.main -m spring-break.core spring-config-jmx.xml
Start jconsole, connect to the JVM and go to the MBeans tab. You
should see a clojure-beans
JMX domain and clj_echo
(besides others). In the text field you may enter (+ 1 2)
and
submit via parseObject
button. The returned value should
be (+ 1 2)
(hence the name of this bean ;-) Now try #=(+ 1 2)
. This should give you 3
.
Note that the Clojure-app will print log messages to the console.
Try using the JMX bean clj_eval
.
In the example above we connected locally to the JVM. Now we want to access the JMX Clojure functions programmatically via RMI (i.e. remotely).
In the project.clj
I put a :jmx-server-app
profile that
includes :server-app
and defines
some com.sun.management.jmxremote.*
system properties. When you run
this, the JMX port will be opened at 127.0.0.1:9999
:
lein trampoline with-profile jmx-server-app run spring-config-jmx.xml
or:
CP=$(JAVA_CMD=`which java` lein classpath)
java -cp ${CP} -Dwait-for-sac-close \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=9999 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
clojure.main -m spring-break.core spring-config-jmx.xml
Now you can run the client:
lein run -m spring-break.jmx invoke clojure-beans:name=clj_echo "#=(rand)"
or:
CP=$(JAVA_CMD=`which java` lein classpath)
java -cp ${CP} clojure.main -m spring-break.jmx invoke clojure-beans:name=clj_echo "#=(rand)"
And for just playing around:
java -cp ${CP} clojure.main -e "(use 'spring-break.jmx) (jmx-invoke \"clojure-beans:name=clj_echo\" \"#=(+ 1 2)\")"
When the driver is run as a server-app we have to shut-down Spring
via a client JMX call. For this we define clj_close_sac
. In
order to get access to the Spring application context we have to
implement ApplicationContextAware
. Our fn-wrapper-of
does
not implement this interface so we have to define two beans:
close_sac_factory
will receive the Spring application context
and then call
is used as the factory for clj_close_sac
:
<bean id="close_sac_factory"
parent="clojure_fact">
<constructor-arg value="
(require 'spring-break.jmx)
(let [sac (atom nil)]
(reify
org.springframework.context.ApplicationContextAware
java.util.concurrent.Callable
(setApplicationContext [this v]
(swap! sac (constantly v)))
(call [this]
(spring-break.jmx/fn-wrapper-of ^{:info :close-sac}
(fn [arg]
(.close @sac))))))
" />
</bean>
<bean id="clj_close_sac"
factory-bean="close_sac_factory"
factory-method="call" />
Just to validate that the shut-down works I added run_swank
from above:
<bean id="run_swank"
parent="clojure_fact">
<constructor-arg
value="
(require 'spring-break.swank)
(spring-break.swank/swank-starter)
" />
</bean>
Now we can start the server-app:
lein trampoline with-profile jmx-server-app run spring-config-jmx.xml
And shut it down like this:
lein run -m spring-break.jmx invoke clojure-beans:name=clj_close_sac x
When Spring detects a bean that is a JMX MBean it will publish it to
JMX. spring-break.jmx/make-mbean
may be used to create such an
MBean which in turn publishes Clojure's atom
, ref
and
var
as mutable/settable JMX attributes.
<bean name="clj_states" parent="clojure_fact">
<constructor-arg value="
(require 'spring-break.jmx)
(spring-break.jmx/make-mbean
:my_mbean #_ description
(atom 42
:validator number?
:meta {:attr-name (name :an_atom)
:attr-description (name :a_description) })
(spring-break.jmx/get-a-var)
(ref (str 'ref)
:validator string?
:meta {:attr-name (name :a_ref)
:attr-description (name :a_description) }))
" />
</bean>
Again exceptions are mapped/copied to circumvent problems with deserialization:
(defn make-mbean [mbean-description & states]
(let [attrs (zipmap (map name-of states) states)]
(reify javax.management.DynamicMBean
(getMBeanInfo [this]
(make-mbean-info this (str mbean-description) states))
(setAttribute [this attr]
(with-exception-mapping "(setAttribute %s %s)" [attr attrs]
(set-attribute attr attrs)))
(getAttribute [this attr-name]
(with-exception-mapping "(getAttribute %s %s)" [attr-name attrs]
(get-attribute attr-name attrs)))
(getAttributes [this attr-names]
(with-exception-mapping "(getAttributes %s %s)" [(vec attr-names) attrs]
(get-attributes attr-names attrs))))))
Now read a value:
lein run -m spring-break.jmx read an_atom
java -cp ${CP} clojure.main -m spring-break.jmx read an_atom
And write it:
java -cp ${CP} clojure.main -m spring-break.jmx write an_atom 21
java -cp ${CP} clojure.main -m spring-break.jmx write a_ref \"foo\"
java -cp ${CP} clojure.main -m spring-break.jmx write a_var :foobar
Now try the validators:
lein run -m spring-break.jmx write a_ref 1
OK, this is it for today.