1094 lines
52 KiB
XML
1094 lines
52 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
|
"http://www.oasis-open.org/docbook/xml/4.4/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 the section entitled <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 the section
|
|
entitled <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 to
|
|
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 the section entitled <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 the section entitled
|
|
<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 the section entitled <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 the section entitled
|
|
<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 the section entitled <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 the section entitle
|
|
<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 the section entitled
|
|
<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 the section entitled
|
|
<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 the section entitled <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>
|