You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1092 lines
51 KiB
1092 lines
51 KiB
<?xml version="1.0" encoding="UTF-8"?> |
|
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
|
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"> |
|
|
|
<chapter id="dynamic-language"> |
|
<title>Dynamic language support</title> |
|
|
|
<section id="dynamic-language-introduction"> |
|
<title>Introduction</title> |
|
<sidebar> |
|
<title>Why only these languages?</title> |
|
<para> |
|
The supported languages were chosen because a) the languages |
|
have a lot of traction in the Java enterprise community, b) no requests were made |
|
for other languages within the Spring 2.0 development timeframe, and |
|
c) the Spring developers were most familiar with them. |
|
</para> |
|
<para> |
|
There is nothing stopping the inclusion of further languages though. If you want |
|
to see support for <<emphasis>insert your favourite dynamic language here</emphasis>>, |
|
you can always raise an issue on Spring's |
|
<ulink url="http://opensource.atlassian.com/projects/spring/secure/Dashboard.jspa">JIRA</ulink> |
|
page (or implement such support yourself). |
|
</para> |
|
</sidebar> |
|
<para>Spring 2.0 introduces comprehensive support for using classes and objects that have |
|
been defined using a dynamic language (such as JRuby) with Spring. |
|
This support allows you to write any number of classes in a supported dynamic language, |
|
and have the Spring container transparently instantiate, configure and dependency inject the |
|
resulting objects.</para> |
|
<para>The dynamic languages currently supported are:</para> |
|
<itemizedlist> |
|
<listitem> |
|
<para>JRuby 0.9 / 1.0</para> |
|
</listitem> |
|
<listitem> |
|
<para>Groovy 1.0 / 1.5</para> |
|
</listitem> |
|
<listitem> |
|
<para>BeanShell 2.0</para> |
|
</listitem> |
|
</itemizedlist> |
|
<para> |
|
Fully working examples of where this dynamic language support can be immediately useful |
|
are described in <xref linkend="dynamic-language-scenarios"/>. |
|
</para> |
|
<para> |
|
<emphasis>Note:</emphasis> Only the specific versions as listed above are supported |
|
in Spring 2.5. In particular, JRuby 1.1 (which introduced many incompatible API |
|
changes) is <emphasis>not</emphasis> supported at this point of time. |
|
</para> |
|
</section> |
|
|
|
<section id="dynamic-language-a-first-example"> |
|
<title>A first example</title> |
|
<para> |
|
This bulk of this chapter is concerned with describing the dynamic language support |
|
in detail. Before diving into all of the ins and outs of the dynamic language support, |
|
let's look at a quick example of a bean defined in a dynamic language. |
|
The dynamic language for this first bean is Groovy (the basis of this example |
|
was taken from the Spring test suite, so if you want to see equivalent examples |
|
in any of the other supported languages, take a look at the source code). |
|
</para> |
|
<para> |
|
Find below the <interfacename>Messenger</interfacename> interface that the |
|
Groovy bean is going to be implementing, and note that this interface is defined |
|
in plain Java. Dependent objects that are injected with a reference to the |
|
<interfacename>Messenger</interfacename> won't know that the underlying |
|
implementation is a Groovy script. |
|
</para> |
|
<programlisting source="java"><![CDATA[package org.springframework.scripting; |
|
|
|
public interface Messenger { |
|
|
|
String getMessage(); |
|
}]]></programlisting> |
|
<para> |
|
Here is the definition of a class that has a dependency on the |
|
<interfacename>Messenger</interfacename> interface. |
|
</para> |
|
<programlisting source="java"><![CDATA[package org.springframework.scripting; |
|
|
|
public class DefaultBookingService implements BookingService { |
|
|
|
private Messenger messenger; |
|
|
|
public void setMessenger(Messenger messenger) { |
|
this.messenger = messenger; |
|
} |
|
|
|
public void processBooking() { |
|
// use the injected Messenger object... |
|
} |
|
}]]></programlisting> |
|
<para>Here is an implementation of the <interfacename>Messenger</interfacename> interface |
|
in Groovy.</para> |
|
<programlisting source="java"><![CDATA[// from the file 'Messenger.groovy' |
|
package org.springframework.scripting.groovy; |
|
|
|
// import the Messenger interface (written in Java) that is to be implemented |
|
import org.springframework.scripting.Messenger |
|
|
|
// define the implementation in Groovy |
|
class GroovyMessenger implements Messenger { |
|
|
|
String message |
|
}]]></programlisting> |
|
<para> |
|
Finally, here are the bean definitions that will effect the injection of the |
|
Groovy-defined <interfacename>Messenger</interfacename> implementation into |
|
an instance of the <classname>DefaultBookingService</classname> class. |
|
</para> |
|
<note> |
|
<para> |
|
To use the custom dynamic language tags to define dynamic-language-backed beans, |
|
you need to have the XML Schema preamble at the top of your Spring XML |
|
configuration file. You also need to be using a Spring |
|
<interfacename>ApplicationContext</interfacename> implementation as your |
|
IoC container. Using the dynamic-language-backed beans with a plain |
|
<interfacename>BeanFactory</interfacename> implementation is supported, |
|
but you have to manage the plumbing of the Spring internals to do so. |
|
</para> |
|
<para>For more information on schema-based configuration, see <xref linkend="xsd-config"/>.</para> |
|
</note> |
|
<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:lang="http://www.springframework.org/schema/lang" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd |
|
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd"> |
|
|
|
]]><lineannotation><!-- this is the bean definition for the Groovy-backed <interfacename>Messenger</interfacename> implementation --></lineannotation><![CDATA[ |
|
<lang:groovy id="messenger" script-source="classpath:Messenger.groovy"> |
|
<lang:property name="message" value="I Can Do The Frug" /> |
|
</lang:groovy> |
|
|
|
]]><lineannotation><!-- an otherwise normal bean that will be injected by the Groovy-backed <interfacename>Messenger</interfacename> --></lineannotation><![CDATA[ |
|
<bean id="bookingService" class="x.y.DefaultBookingService"> |
|
<property name="messenger" ref="messenger" /> |
|
</bean> |
|
|
|
</beans>]]></programlisting> |
|
<para> |
|
The <literal>bookingService</literal> bean (a |
|
<classname>DefaultBookingService</classname>) can now use its private |
|
<literal>messenger</literal> member variable as normal because the |
|
<interfacename>Messenger</interfacename> instance that was injected |
|
into it <emphasis>is</emphasis> a <interfacename>Messenger</interfacename> |
|
instance. There is nothing special going on here, just plain Java and |
|
plain Groovy. |
|
</para> |
|
<para> |
|
Hopefully the above XML snippet is self-explanatory, but don't worry |
|
unduly if it isn't. Keep reading for the in-depth detail on the whys |
|
and wherefores of the above configuration. |
|
</para> |
|
</section> |
|
|
|
<section id="dynamic-language-beans"> |
|
<title>Defining beans that are backed by dynamic languages</title> |
|
<para> |
|
This section describes exactly how you define Spring managed beans in |
|
any of the supported dynamic languages. |
|
</para> |
|
<para> |
|
Please note that this chapter does not attempt to explain the syntax and |
|
idioms of the supported dynamic languages. For example, if you want to |
|
use Groovy to write certain of the classes in your application, then the |
|
assumption is that you already know Groovy. If you need further details |
|
about the dynamic languages themselves, please consult |
|
<xref linkend="dynamic-language-resources" /> at the end of this chapter. |
|
</para> |
|
|
|
<section id="dynamic-language-beans-concepts"> |
|
<title>Common concepts</title> |
|
<para>The steps involved in using dynamic-language-backed beans are as follows:</para> |
|
<orderedlist numeration="arabic"> |
|
<listitem> |
|
<para>Write the test for the dynamic language source code (naturally)</para> |
|
</listitem> |
|
<listitem> |
|
<para><emphasis>Then</emphasis> write the dynamic language source code itself :)</para> |
|
</listitem> |
|
<listitem> |
|
<para> |
|
Define your dynamic-language-backed beans using the appropriate |
|
<literal><lang:language/></literal> element in the XML |
|
configuration (you can of course define such beans programmatically |
|
using the Spring API - although you will have to consult the source |
|
code for directions on how to do this as this type of advanced |
|
configuration is not covered in this chapter). Note this is an iterative |
|
step. You will need at least one bean definition per dynamic |
|
language source file (although the same dynamic language source |
|
file can of course be referenced by multiple bean definitions). |
|
</para> |
|
</listitem> |
|
</orderedlist> |
|
<para> |
|
The first two steps (testing and writing your dynamic language source files) |
|
are beyond the scope of this chapter. Refer to the language specification |
|
and / or reference manual for your chosen dynamic language and crack on with |
|
developing your dynamic language source files. You <emphasis>will</emphasis> |
|
first want to read the rest of this chapter though, as Spring's dynamic language |
|
support does make some (small) assumptions about the contents of your dynamic |
|
language source files. |
|
</para> |
|
|
|
<section id="dynamic-language-beans-concepts-xml-language-element"> |
|
<title>The <literal><lang:language/></literal> element</title> |
|
<sidebar> |
|
<title>XML Schema</title> |
|
<para> |
|
All of the configuration examples in this chapter make use of the |
|
new XML Schema support that was added in Spring 2.0. |
|
</para> |
|
<para>It is possible to forego the use of XML Schema and stick with the old-style |
|
DTD based validation of your Spring XML files, but then you lose out |
|
on the convenience offered by the <literal><lang:language/></literal> |
|
element. See the Spring test suite for examples of the older style |
|
configuration that doesn't require XML Schema-based validation |
|
(it is quite verbose and doesn't hide any of the underlying Spring |
|
implementation from you).</para> |
|
</sidebar> |
|
<para> |
|
The final step involves defining dynamic-language-backed bean definitions, |
|
one for each bean that you want to configure (this is no different from |
|
normal JavaBean configuration). However, instead of specifying the |
|
fully qualified classname of the class that is to be instantiated and |
|
configured by the container, you use the <literal><lang:language/></literal> |
|
element to define the dynamic language-backed bean. |
|
</para> |
|
<para> |
|
Each of the supported languages has a corresponding |
|
<literal><lang:language/></literal> element: |
|
</para> |
|
<itemizedlist> |
|
<listitem> |
|
<para><literal><lang:jruby/></literal> (JRuby)</para> |
|
</listitem> |
|
<listitem> |
|
<para><literal><lang:groovy/></literal> (Groovy)</para> |
|
</listitem> |
|
<listitem> |
|
<para><literal><lang:bsh/></literal> (BeanShell)</para> |
|
</listitem> |
|
</itemizedlist> |
|
<para> |
|
The exact attributes and child elements that are available for |
|
configuration depends on exactly which language the bean has been |
|
defined in (the language-specific sections below provide the full |
|
lowdown on this). |
|
</para> |
|
</section> |
|
|
|
<section id="dynamic-language-refreshable-beans"> |
|
<title>Refreshable beans</title> |
|
<para> |
|
One of the (if not <emphasis>the</emphasis>) most compelling value adds |
|
of the dynamic language support in Spring is the |
|
<firstterm>'refreshable bean'</firstterm> feature. |
|
</para> |
|
<para> |
|
A refreshable bean is a dynamic-language-backed bean that with a small |
|
amount of configuration, a dynamic-language-backed bean can monitor |
|
changes in its underlying source file resource, and then reload itself |
|
when the dynamic language source file is changed (for example when a |
|
developer edits and saves changes to the file on the filesystem). |
|
</para> |
|
<para> |
|
This allows a developer to deploy any number of dynamic language source |
|
files as part of an application, configure the Spring container to create |
|
beans backed by dynamic language source files (using the mechanisms |
|
described in this chapter), and then later, as requirements change or |
|
some other external factor comes into play, simply edit a dynamic language |
|
source file and have any change they make reflected in the bean that is |
|
backed by the changed dynamic language source file. There is no need to |
|
shut down a running application (or redeploy in the case of a web application). |
|
The dynamic-language-backed bean so amended will pick up the new state |
|
and logic from the changed dynamic language source file. |
|
</para> |
|
<note> |
|
<para>Please note that this feature is <emphasis>off</emphasis> by default.</para> |
|
</note> |
|
<para> |
|
Let's take a look at an example to see just how easy it is to start using |
|
refreshable beans. To <emphasis>turn on</emphasis> the refreshable beans |
|
feature, you simply have to specify exactly <emphasis>one</emphasis> |
|
additional attribute on the <literal><lang:language/></literal> element |
|
of your bean definition. So if we stick with |
|
<link linkend="dynamic-language-a-first-example">the example</link> from earlier |
|
in this chapter, here's what we would change in the Spring XML configuration |
|
to effect refreshable beans: |
|
</para> |
|
<programlisting language="xml"><![CDATA[<beans> |
|
|
|
]]><lineannotation><!-- this bean is now 'refreshable' due to the presence of the 'refresh-check-delay' attribute --></lineannotation><![CDATA[ |
|
<lang:groovy id="messenger" |
|
refresh-check-delay="5000" ]]><lineannotation><!-- switches refreshing on with 5 seconds between checks --></lineannotation><![CDATA[ |
|
script-source="classpath:Messenger.groovy"> |
|
<lang:property name="message" value="I Can Do The Frug" /> |
|
</lang:groovy> |
|
|
|
<bean id="bookingService" class="x.y.DefaultBookingService"> |
|
<property name="messenger" ref="messenger" /> |
|
</bean> |
|
|
|
</beans>]]></programlisting> |
|
<para> |
|
That really is all you have to do. The <literal>'refresh-check-delay'</literal> |
|
attribute defined on the <literal>'messenger'</literal> bean definition |
|
is the number of milliseconds after which the bean will be refreshed with |
|
any changes made to the underlying dynamic language source file. |
|
You can turn off the refresh behavior by assigning a negative value |
|
to the <literal>'refresh-check-delay'</literal> attribute. |
|
Remember that, by default, the refresh behavior is disabled. If you don't |
|
want the refresh behavior, then simply don't define the attribute. |
|
</para> |
|
<para> |
|
If we then run the following application we can exercise the refreshable feature; |
|
please do excuse the <emphasis>'jumping-through-hoops-to-pause-the-execution'</emphasis> |
|
shenanigans in this next slice of code. The <literal>System.in.read()</literal> |
|
call is only there so that the execution of the program pauses while I (the author) |
|
go off and edit the underlying dynamic language source file so that the refresh will |
|
trigger on the dynamic-language-backed bean when the program resumes execution.</para> |
|
<programlisting source="java"><![CDATA[import org.springframework.context.ApplicationContext; |
|
import org.springframework.context.support.ClassPathXmlApplicationContext; |
|
import org.springframework.scripting.Messenger; |
|
|
|
public final class Boot { |
|
|
|
public static void main(final String[] args) throws Exception { |
|
|
|
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); |
|
Messenger messenger = (Messenger) ctx.getBean("messenger"); |
|
System.out.println(messenger.getMessage()); |
|
// pause execution while I go off and make changes to the source file... |
|
System.in.read(); |
|
System.out.println(messenger.getMessage()); |
|
} |
|
}]]></programlisting> |
|
<para> |
|
Let's assume then, for the purposes of this example, that all |
|
calls to the <literal>getMessage()</literal> method of |
|
<interfacename>Messenger</interfacename> implementations have to be |
|
changed such that the message is surrounded by quotes. |
|
Below are the changes that I (the author) make to the |
|
<filename>Messenger.groovy</filename> source file when the execution of |
|
the program is paused. |
|
</para> |
|
<programlisting source="java"><![CDATA[package org.springframework.scripting |
|
|
|
class GroovyMessenger implements Messenger { |
|
|
|
private String message = "Bingo" |
|
|
|
public String getMessage() { |
|
// change the implementation to surround the message in quotes |
|
return "'" + this.message + "'" |
|
} |
|
|
|
public void setMessage(String message) { |
|
this.message = message |
|
} |
|
}]]></programlisting> |
|
<para> |
|
When the program executes, the output before the input pause will be |
|
<computeroutput>I Can Do The Frug</computeroutput>. After the change |
|
to the source file is made and saved, and the program resumes execution, |
|
the result of calling the <literal>getMessage()</literal> method on the |
|
dynamic-language-backed <interfacename>Messenger</interfacename> implementation |
|
will be <computeroutput>'I Can Do The Frug'</computeroutput> (notice |
|
the inclusion of the additional quotes). |
|
</para> |
|
<para> |
|
It is important to understand that changes to a script will |
|
<emphasis>not</emphasis> trigger a refresh if the changes occur |
|
within the window of the <literal>'refresh-check-delay'</literal> value. |
|
It is equally important to understand that changes to the script are |
|
<emphasis>not</emphasis> actually 'picked up' until a method is called |
|
on the dynamic-language-backed bean. It is only when a method is called on a |
|
dynamic-language-backed bean that it checks to see if its underlying script |
|
source has changed. Any exceptions relating to refreshing the script |
|
(such as encountering a compilation error, or finding that the script |
|
file has been deleted) will result in a <emphasis>fatal</emphasis> |
|
exception being propagated to the calling code. |
|
</para> |
|
<para> |
|
The refreshable bean behavior described above does |
|
<emphasis>not</emphasis> apply to dynamic language source files |
|
defined using the <literal><lang:inline-script/></literal> element |
|
notation (see <xref linkend="dynamic-language-beans-inline" />). |
|
Additionally, it <emphasis>only</emphasis> applies to beans where |
|
changes to the underlying source file can actually be detected; |
|
for example, by code that checks the last modified date of a |
|
dynamic language source file that exists on the filesystem. |
|
</para> |
|
</section> |
|
|
|
<section id="dynamic-language-beans-inline"> |
|
<title>Inline dynamic language source files</title> |
|
<para> |
|
The dynamic language support can also cater for dynamic language |
|
source files that are embedded directly in Spring bean definitions. |
|
More specifically, the <literal><lang:inline-script/></literal> |
|
element allows you to define dynamic language source immediately |
|
inside a Spring configuration file. An example will perhaps make the |
|
inline script feature crystal clear: |
|
</para> |
|
<programlisting language="xml"><![CDATA[<lang:groovy id="messenger"> |
|
<lang:inline-script> |
|
package org.springframework.scripting.groovy; |
|
|
|
import org.springframework.scripting.Messenger |
|
|
|
class GroovyMessenger implements Messenger { |
|
|
|
String message |
|
} |
|
</lang:inline-script> |
|
<lang:property name="message" value="I Can Do The Frug" /> |
|
</lang:groovy>]]></programlisting> |
|
<para> |
|
If we put to one side the issues surrounding whether it is good practice |
|
to define dynamic language source inside a Spring configuration file, the |
|
<literal><lang:inline-script/></literal> element can be useful in |
|
some scenarios. For instance, we might want to quickly add a Spring |
|
<interfacename>Validator</interfacename> implementation to a Spring MVC |
|
<interfacename>Controller</interfacename>. This is but a moment's work |
|
using inline source. (See <xref linkend="dynamic-language-scenarios-validators" /> |
|
for such an example.) |
|
</para> |
|
<para> |
|
Find below an example of defining the source for a JRuby-based bean |
|
directly in a Spring XML configuration file using the |
|
<literal>inline:</literal> notation. (Notice the use of the <![CDATA[<]]> |
|
characters to denote a <literal>'<'</literal> character. In such a case |
|
surrounding the inline source in a <literal><![CDATA[]]></literal> region might be better.) |
|
</para> |
|
<programlisting language="xml"><![CDATA[<lang:jruby id="messenger" script-interfaces="org.springframework.scripting.Messenger"> |
|
<lang:inline-script> |
|
require 'java' |
|
|
|
include_class 'org.springframework.scripting.Messenger' |
|
|
|
class RubyMessenger < Messenger |
|
|
|
def setMessage(message) |
|
@@message = message |
|
end |
|
|
|
def getMessage |
|
@@message |
|
end |
|
|
|
end |
|
</lang:inline-script> |
|
<lang:property name="message" value="Hello World!" /> |
|
</lang:jruby>]]></programlisting> |
|
</section> |
|
|
|
<section id="dynamic-language-beans-ctor-injection"> |
|
<title>Understanding Constructor Injection in the context of dynamic-language-backed beans</title> |
|
<para> |
|
There is one <emphasis>very</emphasis> important thing to be aware of |
|
with regard to Spring's dynamic language support. Namely, it is not (currently) |
|
possible to supply constructor arguments to dynamic-language-backed beans (and hence |
|
constructor-injection is not available for dynamic-language-backed beans). |
|
In the interests of making this special handling of constructors and |
|
properties 100% clear, the following mixture of code and configuration |
|
will <emphasis>not</emphasis> work. |
|
</para> |
|
<programlisting source="java"><![CDATA[// from the file 'Messenger.groovy' |
|
package org.springframework.scripting.groovy; |
|
|
|
import org.springframework.scripting.Messenger |
|
|
|
class GroovyMessenger implements Messenger { |
|
|
|
GroovyMessenger() {} |
|
|
|
// this constructor is not available for Constructor Injection |
|
GroovyMessenger(String message) { |
|
this.message = message; |
|
} |
|
|
|
String message |
|
|
|
String anotherMessage |
|
}]]></programlisting> |
|
<programlisting language="xml"><![CDATA[<lang:groovy id="badMessenger" |
|
script-source="classpath:Messenger.groovy"> |
|
]]> |
|
<lineannotation><!-- this next constructor argument will *not* be injected into the <classname>GroovyMessenger</classname> --></lineannotation> |
|
<lineannotation><!-- in fact, this isn't even allowed according to the schema --></lineannotation><![CDATA[ |
|
<constructor-arg value="]]><lineannotation><emphasis role="bold">This will *not* work</emphasis></lineannotation><![CDATA[" />]]> |
|
|
|
<lineannotation><!-- <emphasis role="bold">only</emphasis> property values are injected into the dynamic-language-backed object --></lineannotation><![CDATA[ |
|
<lang:property name="anotherMessage" value="Passed straight through to the dynamic-language-backed object" /> |
|
|
|
</lang>]]></programlisting> |
|
<para> |
|
In practice this limitation is not as significant as it first appears since |
|
setter injection is the injection style favored by the overwhelming majority |
|
of developers anyway (let's leave the discussion as to whether that is a good |
|
thing to another day). |
|
</para> |
|
</section> |
|
</section> |
|
|
|
<section id="dynamic-language-beans-jruby"> |
|
<title>JRuby beans</title> |
|
|
|
<sidebar> |
|
<title>The JRuby library dependencies</title> |
|
<para> |
|
The JRuby scripting support in Spring requires the following |
|
libraries to be on the classpath of your application. |
|
(The versions listed just happen to be the versions that the |
|
Spring team used in the development of the JRuby scripting support; |
|
you may well be able to use another version of a specific library.) |
|
</para> |
|
<itemizedlist> |
|
<listitem> |
|
<para><filename>jruby.jar</filename></para> |
|
</listitem> |
|
<listitem> |
|
<para><filename>cglib-nodep-2.1_3.jar</filename></para> |
|
</listitem> |
|
</itemizedlist> |
|
</sidebar> |
|
<para>From the JRuby homepage...</para> |
|
<quote> |
|
<emphasis>JRuby is an 100% pure-Java implementation of the Ruby programming language.</emphasis> |
|
</quote> |
|
<para> |
|
In keeping with the Spring philosophy of offering choice, Spring's |
|
dynamic language support also supports beans defined in the JRuby |
|
language. The JRuby language is based on the quite intuitive |
|
Ruby language, and has support for inline regular expressions, blocks |
|
(closures), and a whole host of other features that do make solutions |
|
for some domain problems a whole lot easier to develop. |
|
</para> |
|
<para> |
|
The implementation of the JRuby dynamic language support in Spring is |
|
interesting in that what happens is this: Spring creates a JDK dynamic |
|
proxy implementing all of the interfaces that are specified in the |
|
<literal>'script-interfaces'</literal> attribute value of the |
|
<literal><lang:ruby></literal> element (this is why |
|
you <emphasis>must</emphasis> supply at least one interface in the value |
|
of the attribute, and (accordingly) program to interfaces when using |
|
JRuby-backed beans). |
|
</para> |
|
<para> |
|
Let us look at a fully working example of using a JRuby-based bean. Here is |
|
the JRuby implementation of the <interfacename>Messenger</interfacename> |
|
interface that was defined earlier in this chapter (for your convenience it |
|
is repeated below). |
|
</para> |
|
<programlisting source="ruby"><![CDATA[package org.springframework.scripting; |
|
|
|
public interface Messenger { |
|
|
|
String getMessage(); |
|
}]]></programlisting> |
|
<programlisting source="ruby"><![CDATA[require 'java' |
|
|
|
class RubyMessenger |
|
include org.springframework.scripting.Messenger |
|
|
|
def setMessage(message) |
|
@@message = message |
|
end |
|
|
|
def getMessage |
|
@@message |
|
end |
|
end |
|
|
|
# this last line is not essential (but see below) |
|
RubyMessenger.new]]></programlisting> |
|
<para> |
|
And here is the Spring XML that defines an instance of the |
|
<classname>RubyMessenger</classname> JRuby bean. |
|
</para> |
|
<programlisting language="xml"><![CDATA[<lang:jruby id="messageService" |
|
script-interfaces="org.springframework.scripting.Messenger" |
|
script-source="classpath:RubyMessenger.rb"> |
|
|
|
<lang:property name="message" value="Hello World!" /> |
|
|
|
</lang:jruby>]]></programlisting> |
|
<para> |
|
Take note of the last line of that JRuby source (<literal>'RubyMessenger.new'</literal>). |
|
When using JRuby in the context of Spring's dynamic language support, you are encouraged |
|
to instantiate and return a new instance of the JRuby class that you want to use as a |
|
dynamic-language-backed bean as the result of the execution of your JRuby source. You |
|
can achieve this by simply instantiating a new instance of your JRuby class on the last |
|
line of the source file like so: |
|
</para> |
|
<programlisting source="ruby"><![CDATA[require 'java' |
|
|
|
include_class 'org.springframework.scripting.Messenger' |
|
|
|
# class definition same as above... |
|
|
|
# instantiate and return a new instance of the RubyMessenger class |
|
RubyMessenger.new]]></programlisting> |
|
<para> |
|
If you forget to do this, it is not the end of the world; this will however result in |
|
Spring having to trawl (reflectively) through the type representation of your JRuby class |
|
looking for a class to instantiate. In the grand scheme of things this will be so fast |
|
that you'll never notice it, but it is something that can be avoided by simply |
|
having a line such as the one above as the last line of your JRuby script. If you don't |
|
supply such a line, or if Spring cannot find a JRuby class in your script to instantiate |
|
then an opaque <classname>ScriptCompilationException</classname> |
|
will be thrown immediately after the source is executed by the JRuby |
|
interpreter. The key text that identifies this as the root cause of an |
|
exception can be found immediately below (so if your Spring container |
|
throws the following exception when creating your dynamic-language-backed bean |
|
and the following text is there in the corresponding stacktrace, this will hopefully |
|
allow you to identify and then easily rectify the issue): |
|
</para> |
|
<computeroutput><![CDATA[org.springframework.scripting.ScriptCompilationException: Compilation of JRuby script returned '']]></computeroutput> |
|
<para> |
|
To rectify this, simply instantiate a new instance of whichever class |
|
you want to expose as a JRuby-dynamic-language-backed bean (as shown above). Please |
|
also note that you can actually define as many classes and objects |
|
as you want in your JRuby script; what is important is that the |
|
source file as a whole must return an object (for Spring to configure). |
|
</para> |
|
<para> |
|
See <xref linkend="dynamic-language-scenarios" /> for some |
|
scenarios where you might want to use JRuby-based beans. |
|
</para> |
|
</section> |
|
|
|
<section id="dynamic-language-beans-groovy"> |
|
<title>Groovy beans</title> |
|
<sidebar> |
|
<title>The Groovy library dependencies</title> |
|
<para> |
|
The Groovy scripting support in Spring requires the following |
|
libraries to be on the classpath of your application. |
|
</para> |
|
<itemizedlist> |
|
<listitem> |
|
<para><filename>groovy-1.5.5.jar</filename></para> |
|
</listitem> |
|
<listitem> |
|
<para><filename>asm-2.2.2.jar</filename></para> |
|
</listitem> |
|
<listitem> |
|
<para><filename>antlr-2.7.6.jar</filename></para> |
|
</listitem> |
|
</itemizedlist> |
|
</sidebar> |
|
<para>From the Groovy homepage...</para> |
|
<quote> |
|
<emphasis>Groovy is an agile dynamic language for the Java 2 Platform that has |
|
many of the features that people like so much in languages like Python, Ruby |
|
and Smalltalk, making them available to Java developers using a Java-like syntax. |
|
</emphasis> |
|
</quote> |
|
<para> |
|
If you have read this chapter straight from the top, you will already have |
|
<link linkend="dynamic-language-a-first-example">seen an example</link> of a |
|
Groovy-dynamic-language-backed bean. Let's look at another example (again |
|
using an example from the Spring test suite). |
|
</para> |
|
<programlisting source="java"><![CDATA[package org.springframework.scripting; |
|
|
|
public interface Calculator { |
|
|
|
int add(int x, int y); |
|
}]]></programlisting> |
|
<para> |
|
Here is an implementation of the <interfacename>Calculator</interfacename> |
|
interface in Groovy. |
|
</para> |
|
<programlisting source="java"><lineannotation>// from the file 'calculator.groovy'</lineannotation><![CDATA[ |
|
package org.springframework.scripting.groovy |
|
|
|
class GroovyCalculator implements Calculator { |
|
|
|
int add(int x, int y) { |
|
x + y |
|
} |
|
}]]></programlisting> |
|
<programlisting language="xml"><lineannotation><-- from the file 'beans.xml' --></lineannotation><![CDATA[ |
|
<beans> |
|
<lang:groovy id="calculator" script-source="classpath:calculator.groovy"/> |
|
</beans>]]></programlisting> |
|
<para> |
|
Lastly, here is a small application to exercise the above configuration. |
|
</para> |
|
<programlisting source="java"><![CDATA[package org.springframework.scripting; |
|
|
|
import org.springframework.context.ApplicationContext; |
|
import org.springframework.context.support.ClassPathXmlApplicationContext; |
|
|
|
public class Main { |
|
|
|
public static void Main(String[] args) { |
|
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); |
|
Calculator calc = (Calculator) ctx.getBean("calculator"); |
|
System.out.println(calc.add(2, 8)); |
|
} |
|
}]]></programlisting> |
|
<para> |
|
The resulting output from running the above program will be |
|
(unsurprisingly) <computeroutput>10</computeroutput>. |
|
(Exciting example, huh? Remember that the intent is to illustrate the |
|
concept. Please consult the dynamic language showcase project for a |
|
more complex example, or indeed <xref linkend="dynamic-language-scenarios" /> |
|
later in this chapter). |
|
</para> |
|
<para> |
|
It is important that you <emphasis>do not</emphasis> define more than one |
|
class per Groovy source file. While this is perfectly legal in Groovy, it |
|
is (arguably) a bad practice: in the interests of a consistent approach, |
|
you should (in the opinion of this author) respect the standard Java |
|
conventions of one (public) class per source file. |
|
</para> |
|
|
|
<section id="dynamic-language-beans-groovy-customizer"> |
|
<title>Customising Groovy objects via a callback</title> |
|
<para> |
|
The <interfacename>GroovyObjectCustomizer</interfacename> |
|
interface is a callback that allows you to hook additional |
|
creation logic into the process of creating a Groovy-backed bean. |
|
For example, implementations of this interface could invoke |
|
any required initialization method(s), or set some default property |
|
values, or specify a custom <classname>MetaClass</classname>. |
|
</para> |
|
<programlisting source="java"><![CDATA[public interface GroovyObjectCustomizer { |
|
|
|
void customize(GroovyObject goo); |
|
}]]></programlisting> |
|
<para> |
|
The Spring Framework will instantiate an instance of your Groovy-backed |
|
bean, and will then pass the created <interfacename>GroovyObject</interfacename> |
|
to the specified <interfacename>GroovyObjectCustomizer</interfacename> |
|
if one has been defined. You can do whatever you like with the supplied |
|
<interfacename>GroovyObject</interfacename> reference: it is expected |
|
that the setting of a custom <classname>MetaClass</classname> is what most |
|
folks will want to do with this callback, and you can see an example |
|
of doing that below. |
|
</para> |
|
<programlisting source="java"><![CDATA[public final class SimpleMethodTracingCustomizer implements GroovyObjectCustomizer { |
|
|
|
public void customize(GroovyObject goo) { |
|
DelegatingMetaClass metaClass = new DelegatingMetaClass(goo.getMetaClass()) { |
|
|
|
public Object invokeMethod(Object object, String methodName, Object[] arguments) { |
|
System.out.println("Invoking '" + methodName + "'."); |
|
return super.invokeMethod(object, methodName, arguments); |
|
} |
|
}; |
|
metaClass.initialize(); |
|
goo.setMetaClass(metaClass); |
|
} |
|
}]]></programlisting> |
|
<para> |
|
A full discussion of meta-programming in Groovy is beyond the scope of the |
|
Spring reference manual. Consult the relevant section of the Groovy |
|
reference manual, or do a search online: there are plenty of articles |
|
concerning this topic. |
|
Actually making use of a <interfacename>GroovyObjectCustomizer</interfacename> |
|
is easy if you are using the Spring 2.0 namespace support. |
|
</para> |
|
<programlisting language="xml"><lineannotation><!-- define the <interfacename>GroovyObjectCustomizer</interfacename> just like any other bean --></lineannotation> |
|
<![CDATA[<bean id="tracingCustomizer" class="example.SimpleMethodTracingCustomizer" /> |
|
|
|
]]><lineannotation><!-- ... and plug it into the desired Groovy bean via the '<literal>customizer-ref</literal>' attribute --></lineannotation><![CDATA[ |
|
<lang:groovy id="calculator" |
|
script-source="classpath:org/springframework/scripting/groovy/Calculator.groovy" |
|
customizer-ref="tracingCustomizer" />]]></programlisting> |
|
<para> |
|
If you are not using the Spring 2.0 namespace support, you can still |
|
use the <interfacename>GroovyObjectCustomizer</interfacename> functionality. |
|
</para> |
|
<programlisting language="xml"><![CDATA[<bean id="calculator" class="org.springframework.scripting.groovy.GroovyScriptFactory"> |
|
<constructor-arg value="classpath:org/springframework/scripting/groovy/Calculator.groovy"/> |
|
]]><lineannotation><!-- define the <interfacename>GroovyObjectCustomizer</interfacename> (as an inner bean) --></lineannotation><![CDATA[ |
|
<constructor-arg> |
|
<bean id="tracingCustomizer" class="example.SimpleMethodTracingCustomizer" /> |
|
</constructor-arg> |
|
</bean> |
|
|
|
<bean class="org.springframework.scripting.support.ScriptFactoryPostProcessor"/>]]></programlisting> |
|
</section> |
|
</section> |
|
|
|
<section id="dynamic-language-beans-bsh"> |
|
<title>BeanShell beans</title> |
|
<sidebar> |
|
<title>The BeanShell library dependencies</title> |
|
<para> |
|
The BeanShell scripting support in Spring requires the following |
|
libraries to be on the classpath of your application. |
|
</para> |
|
<itemizedlist> |
|
<listitem> |
|
<para><filename>bsh-2.0b4.jar</filename></para> |
|
</listitem> |
|
<listitem> |
|
<para><filename>cglib-nodep-2.1_3.jar</filename></para> |
|
</listitem> |
|
</itemizedlist> |
|
<para> |
|
All of these libraries are available in the Spring-with-dependencies |
|
distribution of Spring (in addition to also being freely available |
|
on the web). |
|
</para> |
|
</sidebar> |
|
<para>From the BeanShell homepage...</para> |
|
<quote> |
|
<emphasis>BeanShell is a small, free, embeddable Java source interpreter |
|
with dynamic language features, written in Java. BeanShell dynamically |
|
executes standard Java syntax and extends it with common scripting |
|
conveniences such as loose types, commands, and method closures like those |
|
in Perl and JavaScript.</emphasis> |
|
</quote> |
|
<para> |
|
In contrast to Groovy, BeanShell-backed bean definitions require some (small) |
|
additional configuration. The implementation of the BeanShell dynamic language |
|
support in Spring is interesting in that what happens is this: Spring creates |
|
a JDK dynamic proxy implementing all of the interfaces that are specified in the |
|
<literal>'script-interfaces'</literal> attribute value of the |
|
<literal><lang:bsh></literal> element (this is why |
|
you <emphasis>must</emphasis> supply at least one interface in the value |
|
of the attribute, and (accordingly) program to interfaces when using |
|
BeanShell-backed beans). This means that every method call on a BeanShell-backed |
|
object is going through the JDK dynamic proxy invocation mechanism. |
|
</para> |
|
<para> |
|
Let's look at a fully working example of using a BeanShell-based bean |
|
that implements the <interfacename>Messenger</interfacename> interface |
|
that was defined earlier in this chapter (repeated below for your |
|
convenience). |
|
</para> |
|
<programlisting source="java"><![CDATA[package org.springframework.scripting; |
|
|
|
public interface Messenger { |
|
|
|
String getMessage(); |
|
}]]></programlisting> |
|
<para>Here is the BeanShell 'implementation' (the term is used loosely here) of the |
|
<interfacename>Messenger</interfacename> interface.</para> |
|
<programlisting source="java"><![CDATA[String message; |
|
|
|
String getMessage() { |
|
return message; |
|
} |
|
|
|
void setMessage(String aMessage) { |
|
message = aMessage; |
|
}]]></programlisting> |
|
<para> |
|
And here is the Spring XML that defines an 'instance' of the above 'class' |
|
(again, the term is used very loosely here). |
|
</para> |
|
<programlisting language="xml"><![CDATA[<lang:bsh id="messageService" script-source="classpath:BshMessenger.bsh" |
|
script-interfaces="org.springframework.scripting.Messenger"> |
|
|
|
<lang:property name="message" value="Hello World!" /> |
|
</lang:bsh>]]></programlisting> |
|
<para>See <xref linkend="dynamic-language-scenarios" /> for some |
|
scenarios where you might want to use BeanShell-based beans.</para> |
|
</section> |
|
</section> |
|
|
|
<section id="dynamic-language-scenarios"> |
|
<title>Scenarios</title> |
|
<para> |
|
The possible scenarios where defining Spring managed beans in a scripting |
|
language would be beneficial are, of course, many and varied. This section |
|
describes two possible use cases for the dynamic language support in Spring. |
|
</para> |
|
|
|
<section id="dynamic-language-scenarios-controllers"> |
|
<title>Scripted Spring MVC Controllers</title> |
|
<para> |
|
One group of classes that may benefit from using dynamic-language-backed |
|
beans is that of Spring MVC controllers. In pure Spring MVC applications, |
|
the navigational flow through a web application is to a large extent |
|
determined by code encapsulated within your Spring MVC controllers. |
|
As the navigational flow and other presentation layer logic of a web |
|
application needs to be updated to respond to support issues or changing |
|
business requirements, it may well be easier to effect any such required |
|
changes by editing one or more dynamic language source files and seeing |
|
those changes being immediately reflected in the state of a running |
|
application. |
|
</para> |
|
<para> |
|
Remember that in the lightweight architectural model espoused by projects |
|
such as Spring, you are typically aiming to have a really |
|
<emphasis>thin</emphasis> presentation layer, with all the meaty business |
|
logic of an application being contained in the domain and service layer |
|
classes. Developing Spring MVC controllers as dynamic-language-backed beans |
|
allows you to change presentation layer logic by simply editing and saving |
|
text files; any changes to such dynamic language source files will (depending |
|
on the configuration) automatically be reflected in the beans that are backed |
|
by dynamic language source files. |
|
</para> |
|
<note> |
|
<para> |
|
In order to effect this automatic 'pickup' of any changes |
|
to dynamic-language-backed beans, you will have had to enable the |
|
'refreshable beans' functionality. See |
|
<xref linkend="dynamic-language-refreshable-beans"/> for a full treatment |
|
of this feature. |
|
</para> |
|
</note> |
|
<para> |
|
Find below an example of an |
|
<interfacename>org.springframework.web.servlet.mvc.Controller</interfacename> |
|
implemented using the Groovy dynamic language. |
|
</para> |
|
<programlisting source="java"><![CDATA[// from the file '/WEB-INF/groovy/FortuneController.groovy' |
|
package org.springframework.showcase.fortune.web |
|
|
|
import org.springframework.showcase.fortune.service.FortuneService |
|
import org.springframework.showcase.fortune.domain.Fortune |
|
import org.springframework.web.servlet.ModelAndView |
|
import org.springframework.web.servlet.mvc.Controller |
|
|
|
import javax.servlet.http.HttpServletRequest |
|
import javax.servlet.http.HttpServletResponse |
|
|
|
class FortuneController implements Controller { |
|
|
|
@Property FortuneService fortuneService |
|
|
|
ModelAndView handleRequest( |
|
HttpServletRequest request, HttpServletResponse httpServletResponse) { |
|
|
|
return new ModelAndView("tell", "fortune", this.fortuneService.tellFortune()) |
|
} |
|
}]]></programlisting> |
|
<programlisting language="xml"><![CDATA[<lang:groovy id="fortune" |
|
refresh-check-delay="3000" |
|
script-source="/WEB-INF/groovy/FortuneController.groovy"> |
|
<lang:property name="fortuneService" ref="fortuneService"/> |
|
</lang:groovy> |
|
]]></programlisting> |
|
</section> |
|
|
|
<section id="dynamic-language-scenarios-validators"> |
|
<title>Scripted Validators</title> |
|
<para> |
|
Another area of application development with Spring that may benefit |
|
from the flexibility afforded by dynamic-language-backed beans is that of |
|
validation. It <emphasis>may</emphasis> be easier to express complex validation |
|
logic using a loosely typed dynamic language (that may also have support |
|
for inline regular expressions) as opposed to regular Java. |
|
</para> |
|
<para> |
|
Again, developing validators as dynamic-language-backed beans allows you to change |
|
validation logic by simply editing and saving a simple text file; any such |
|
changes will (depending on the configuration) automatically be reflected |
|
in the execution of a running application and would not require the restart |
|
of an application. |
|
</para> |
|
<note> |
|
<para> |
|
Please note that in order to effect the automatic 'pickup' of any changes |
|
to dynamic-language-backed beans, you will have had to enable the |
|
'refreshable beans' feature. See |
|
<xref linkend="dynamic-language-refreshable-beans"/> for a full and |
|
detailed treatment of this feature. |
|
</para> |
|
</note> |
|
<para> |
|
Find below an example of a Spring |
|
<interfacename>org.springframework.validation.Validator</interfacename> |
|
implemented using the Groovy dynamic language. (See <xref linkend="validator"/> |
|
for a discussion of the <interfacename>Validator</interfacename> interface.) |
|
</para> |
|
<programlisting source="java"><![CDATA[import org.springframework.validation.Validator |
|
import org.springframework.validation.Errors |
|
import org.springframework.beans.TestBean |
|
|
|
class TestBeanValidator implements Validator { |
|
|
|
boolean supports(Class clazz) { |
|
return TestBean.class.isAssignableFrom(clazz) |
|
} |
|
|
|
void validate(Object bean, Errors errors) { |
|
if(bean.name?.trim()?.size() > 0) { |
|
return |
|
} |
|
errors.reject("whitespace", "Cannot be composed wholly of whitespace.") |
|
} |
|
}]]></programlisting> |
|
</section> |
|
</section> |
|
|
|
<section id="dynamic-language-final-notes"> |
|
<title>Bits and bobs</title> |
|
<para> |
|
This last section contains some bits and bobs related to the dynamic language |
|
support. |
|
</para> |
|
|
|
<section id="dynamic-language-final-notes-aop"> |
|
<title>AOP - advising scripted beans</title> |
|
<para> |
|
It is possible to use the Spring AOP framework to advise scripted beans. |
|
The Spring AOP framework actually is unaware that a bean that is being |
|
advised might be a scripted bean, so all of the AOP use cases and functionality |
|
that you may be using or aim to use will work with scripted beans. There is |
|
just one (small) thing that you need to be aware of when advising scripted |
|
beans... you cannot use class-based proxies, you must use |
|
<link linkend="aop-proxying">interface-based proxies</link>. |
|
</para> |
|
<para> |
|
You are of course not just limited to advising scripted beans... you can |
|
also write aspects themselves in a supported dynamic language and use such |
|
beans to advise other Spring beans. This really would be an advanced use of |
|
the dynamic language support though. |
|
</para> |
|
</section> |
|
|
|
<section id="dynamic-language-final-notes-scopes"> |
|
<title>Scoping</title> |
|
<para> |
|
In case it is not immediately obvious, scripted beans can of course be scoped |
|
just like any other bean. The <literal>scope</literal> attribute on the |
|
various <literal><lang:language/></literal> elements allows you to |
|
control the scope of the underlying scripted bean, just as it does with a |
|
regular bean. (The default scope is |
|
<link linkend="beans-factory-scopes-singleton">singleton</link>, just as it |
|
is with 'regular' beans.) |
|
</para> |
|
<para> |
|
Find below an example of using the <literal>scope</literal> attribute |
|
to define a Groovy bean scoped as a |
|
<link linkend="beans-factory-scopes-prototype">prototype</link>. |
|
</para> |
|
<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:lang="http://www.springframework.org/schema/lang" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd |
|
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd"> |
|
|
|
<lang:groovy id="messenger" script-source="classpath:Messenger.groovy" ]]><lineannotation><emphasis role="bold">scope="prototype"</emphasis></lineannotation><![CDATA[> |
|
<lang:property name="message" value="I Can Do The RoboCop" /> |
|
</lang:groovy> |
|
|
|
<bean id="bookingService" class="x.y.DefaultBookingService"> |
|
<property name="messenger" ref="messenger" /> |
|
</bean> |
|
|
|
</beans>]]></programlisting> |
|
<para> |
|
See <xref linkend="beans-factory-scopes"/> in <xref linkend="beans"/> |
|
for a fuller discussion of the scoping support in the Spring Framework. |
|
</para> |
|
</section> |
|
</section> |
|
|
|
<section id="dynamic-language-resources"> |
|
<title>Further Resources</title> |
|
<para> |
|
Find below links to further resources about the various dynamic languages described |
|
in this chapter. |
|
</para> |
|
<itemizedlist> |
|
<listitem> |
|
<para>The <ulink url="http://jruby.codehaus.org/">JRuby</ulink> homepage</para> |
|
</listitem> |
|
<listitem> |
|
<para>The <ulink url="http://groovy.codehaus.org/">Groovy</ulink> homepage</para> |
|
</listitem> |
|
<listitem> |
|
<para>The <ulink url="http://www.beanshell.org/">BeanShell</ulink> homepage</para> |
|
</listitem> |
|
</itemizedlist> |
|
<para> |
|
Some of the more active members of the Spring community have also added support for |
|
a number of additional dynamic languages above and beyond the ones covered in this |
|
chapter. While it is possible that such third party contributions may be added to the |
|
list of languages supported by the main Spring distribution, your best bet for seeing |
|
if your favourite scripting language is supported is the |
|
<ulink url="https://springmodules.dev.java.net/">Spring Modules project</ulink>. |
|
</para> |
|
</section> |
|
|
|
</chapter>
|
|
|