Dynamic Languages and Java/Expression Languages

From JVMLanguages

Table of contents

Groovy's Expression Language

XPath

JSTL-EL

Spring

In addition frameworks like BSF and JSR-223 whose primary purpose is to provide an interface between Java and various scripting languages, there are frameworks with other purposes that have support for scripting languages. Spring, for example, is a framework that implements the Inversion of Control (or Dependency Injection) pattern. The primary goal of this type of framework is to hook objects together through some form of metadata so that they do not need to have explicit code-level dependencies on one another. Each object needs to implement some well-known interface that the other object or objects do depend on, but the actual implementation that is used and its method of construction do not need to be specified in code.

There are two main types of IoC frameworks: constructor-based and method-based. Constructor-based frameworks assume that all objects are immutable (at least with respect to how objects are hooked up) and pass all sub-objects into the constructor. Method-based frameworks, on the other hand, instantiate the objects first and then call set methods for each property or sub-object. Spring supports both of these paradigms.

IoC frameworks are particularly good at determining the correct in which objects need to be instantiated in order to satisfy their dependencies. For example, let's assume that you want to create an object graph that instantiates the following class diagram:

Figure 1. Class Diagram of Plane Component

In this case, if these objects did not have set-methods and required all sub-objects to be passed into the constructor, you would need to instantiate them in the following order:

   Engine e1 = new Engine();
   FuelLine fl1 = new FuelLine(e1);

   Engine e2 = new Engine();
   FuelLine fl2 = new FuelLine(e2);

   FuelTank f = new FuelTank(new FuelLine[] {fl1, fl2});

   Plane p = new BiPlane(f, e1, e2);

In Spring, this would be represented in an XML file as follows:

   <beans>
     <bean id="plane" class="airplane.BiPlane">
       <property name="engine1">
         <bean id="engine1" class="airplane.Engine"/>
       </property>
       <property name="engine2">
         <bean id="engine2" class="airplane.Engine"/>
       </property>
       <property name="fuelTank">
         <bean id="fuelTank" class="airplane.FuelTank">
           <property name="fuelLines">
             <list>
               <bean class="airplane.FuelLine">
                 <property name="source">
                   <ref local="fuelTank"/>
                 </property>
                 <property name="dest">
                   <ref local="engine1"/>
                 </property>
               </bean>
               <bean class="airplane.FuelLine">
                 <property name="source">
                   <ref local="fuelTank"/>
                 </property>
                 <property name="dest">
                   <ref local="engine2"/>
                 </property>
               </bean>
             </list>
           </property>
         </bean>
       </property>
     </bean>
   </beans>

One of the things that makes Spring interesting beyond simply its ability to hook up objects and resolve complex webs of dependencies is its concept of FactoryBeans. FactoryBeans are classes that can be declared in much the same way as normal objects, but which are responsible for building objects to be inserted into the object graph rather than being objects themselves. For example, a relatively common use of FactoryBeans is to retrieve objects from a database or remote server.

   Example of querying an object from Spring? 

But FactoryBean's can also be used to insert bits of interpreted logic into the building of your object graph. In particular, a BSF-aware FactoryBean could execute custom code in an arbitrary language from within Spring and return a complex object which could then be inserted into your object graph.

   Example of calling BSF from Spring. 

Spring also has a concept called "auto-wiring", where objects will be matched up, either by class or by property name, and Spring will attempt to automatically populate any fields that match.

Jelly

Jelly can also be thought of as an Inversion of Control framework, but really it is much more than that. Jelly is an executable language based on XML, where each XML tag represents some action that must be taken and whose return value will be passed into the parent action. In comparison to Spring, Jelly brings the concept of a FactoryBean to the forefront and makes this the default behavior. Every XML tag is assumed to be some action that is invoked on the arguments that the tag contains. Unlike Spring, Jelly's actions can be defined within Jelly itself.

To evaluate a Jelly XML file, simply obtain a JellyContext and use its runScript method.

   JellyContext context = new JellyContext();
   context.setVariable("name", object);
   context.runScript( "foo.jelly", file );

One of the FactoryBean-like tags that Jelly includes is the <script> tag, which allows Jelly to evaluate code in one or more languages supported by BSF.

   <plane xmlns:bsf="jelly:bsf">
     <engine>
       <bsf:script engine="org.jruby.javasupport.bsf.JRubyEngine">
         module Plane
           attr :engine

           def fly
             # ...
           end
         end
       </bsf:script>
     </engine>
   </plane>

Jelly also has native support for many of the expression languages that we will discuss in Chapter 8.

   There don't seem to be a BSFEngine available for Jelly. This seems trivial to write. Should I write one? Would it help the case study?