Client-Side Developer Productivity
Recent advances in rich-client programming environments make
developers more productive in part by providing languages and APIs that
help reduce the amount of code needed to implement sophisticated UI
features. Adobe's Flex, an open-source toolkit targeting the Flash
Player, allows a developer to use MXML, a UI-specific domain language,
as well as ActionScript, a derivative of JavaScript, to develop rich
user interfaces.
MXML simplifies UI development by letting layout code mimic the
typically hierarchical, containment-based structure of user interfaces.
And ActionScript's properties and higher-order functions are helpful
when working with the event-based programming model typical of rich
clients. The Flex APIs leverage these language features by providing
concise expressions to specify, for example, UI effects, transitions,
animation, and even interaction with remote, server-hosted resources.
Flex is not alone in aiming to boost UI developers' effectiveness.
Swing developers frequently complained that the Swing APIs, as well as
the Java language, required them to code up boilerplate constructs over
and over again, especially when implementing rich UI features, such as
animations and advanced graphics display. To address those concerns, Sun
created JavaFX, a new JVM-hosted language that helps UI developers
become more effective by eliminating repetitive code that was typical of
Swing applications. Similarly, Microsoft developed Silverlight with the
goal of bolstering rich-client developers' productivity on the .NET
runtime.
The benefit of having to write less code is hardly the sole domain of UI
developers, however. Among the most important trends in recent years is
the proliferation of languages hosted on the Java VM and on the .NET
runtime. No longer mere curiosities, many enterprises now embrace such
alternative languages as a way to improve their development teams'
productivity. JRuby, Groovy, and Jython, for instance, provide dynamic
alternatives to Java's static type system on the JVM. Scala, by
contrast,
improves on Java's type system, while providing a concise,
boilerplate-free syntax for server-side development.
This article aims to illustrate that taking advantage of newer
languages on both client and server have a cumulative effect on
productivity when building rich-client applications that interact with
server-side resources. Defining the UI in Flex allows one to use the
concise syntax of MXML and ActionScript on the client. And, in this
example, using Scala on the server means less boilerplate code when
processing incoming requests from the client.
The article draws on the Artima tutorial on Integrating Flex
with a Java EE Application, Part I,
and Part
II. In those articles, a Flex client remotely
invokes methods on sever-side Java objects. Data between Flex and Java
are marshalled via the Action Message Format (AMF) binary
protocol, and the open-source BlazeDS middleware helps expose Java
objects'
methods for remote invocation from the Flex client.
Figure 1. The BookInventory application's UI embedded
in an HTML page.
This article further develops that example by replacing Java with
Scala on the server. AMF and BlazeDS play similar roles as before. In
addition to switching to Scala from Java, the server-side
code will use the Java Persistence API (JPA) from Scala to provide
database access.
You don't need to be familiar with Scala to follow these
examples. Although complete, the examples in this article are meant to
show that, if you are open to using newer JVM languages, such as Scala,
very little code is needed to implement an end-to-end rich-client
application hosted in a Java EE environment.
Scala and Flex
Scala is a relatively new language for the JVM. It supports a static
type system, and sports many advanced features, such as type inference,
higher-order functions, and properties. Scala has a rich API, but it is
also compatible with existing Java classes: Any Java API can be used
from Scala code.
ActionScript, the main Flex programming language, shares many
similarities with Scala. For example, on a syntactic level, variables
and method types in both languages are indicated with a :
character after the variable or method name, followed by the type. The
following is both valid ActionScript and valid Scala code:
var a: String = "hello, there" // Valid Scala and ActionScript
Note that semicolons after a statement are inferred by the compiler in
both languages.
Methods are indicated by function
in ActionScript, and by
def
in Scala:
class AnActionScriptClass { // ActionScript
public function anActionScriptMethod(): String {
return "hello, there"
}
}
Note that Scala methods don't require a return
statement:
class AScalaClass { // Scala
def aScalaMethod(): String = {
"hello, there"
}
}
More important than these syntactic similarities, both
languages support the ability to pass functions as values, or
higher-order functions. A common ActionScript idiom is to write a
function as a handler for an event, and then simply assign that function
as a property value to an object, often via MXML, Flex's UI layout
language:
private function buttonHandler(event:MouseEvent): void { // ActionScript
trace("Someone clicked me")
}
...
<mx:Button label="Click me" click="buttonHandler"/> // MXML
You can also pass a function as a parameter to another function in
ActionScript:
private function doSomething(): String { // ActionScript
return "I'm doing something"
}
private function doItNow(f: Function): void {
var result: String = String(f())
trace(result)
}
You can achieve similar functionality in Scala as well:
def doSomething() = "I'm doing something" // Scala
def doItNow(block: => Any) = {
val result = block
println(result)
}
The block: => Any
notation in Scala specifies
block
as a by-name parameter, or code that will be
executed later, inside the doItNow()
method.
Any
sits at the top of Scala's type hierarchy, indicating
that the block can result in any type.
In addition to similarities in syntax and concepts, developing
with Scala and Flex is also made easier by the fact that Adobe chose
Eclipse as the platform for its FlexBuilder IDE. FlexBuilder can be
downloaded both as a standalone product, as well as an Eclipse
plug-in.
Because Eclipse also supports Scala development, it is possible to
use the same tool for developing an application in which the client uses
Flex and the server uses Scala. The concluding section of this
article includes detailed instructions on setting up Eclipse to
effectively work with Scala and Flex.
The Flex Client
The tutorial on Integrating Flex with a Java EE Application
relied on the open-source BlazeDS tool to make server-side Java objects
available for remote method calls from Flex. One of the most productive
features of working with BlazeDS is that changing the implementation
language on the server has very little impact on client-side code.
That's because BlazeDS requires only that the server provide Java
objects at the binary level—BlazeDS does not care what language
you use to define those objects.
Figure 2. Layers of invocation in BlazeDS
The example book inventory application consists of two domain
objects: Book
describes a book title, such as For Whom
the
Bell Tolls or Programming in Scala;
Publisher
, in turn, describes a book's publisher.
Since the domain model stays the same regardless of server-side
implementation language, we can re-use the ActionScript classes from the
earlier implementation of the book inventory application. The only
difference is that we add an id
property to each class;
that property is used by the Java Persistence API on the server to
maintain object identity. Note the RemoteClass
annotation
on each class: The alias
parameter for that annotation
refers to the fully qualified name of the server-side class:
package scalaflex {
[RemoteClass(alias="scalaflex.Publisher")]
public class Publisher {
public var id:Number;
public var name:String;
}
}
package scalaflex {
[RemoteClass(alias="scalaflex.Book")]
class Book {
public var id:Number;
public var title:String;
public var authors:String;
public var year:Int;
public var price:Number;
public var stock:Int;
public var publisher:Publisher;
}
}
Listing 1: Client-side ActionScript object model for
the book inventory
To obtain the current inventory listing from the server, we need to
arrange for the Flex client to invoke the
getCurrentInventory()
method on the remote
InventoryManager
object on the server. Just as the domain
classes, the ActionScript code to perform the remote call is also
agnostic to the server-side implementation language: We define on the
client a RemoteObject
destination, set listeners on that
remote destination, and then invoke the
getCurrentInventory()
method:
[Bindable]
private var books:ArrayCollection;
private function onCreationComplete():void {
var inventoryManager:RemoteObject = new RemoteObject();
inventoryManager.destination = "inventorymanager"; // Remote destination name
inventoryManager.addEventListener(ResultEvent.RESULT,
function(event:ResultEvent):void {
books = event.result as ArrayCollection
});
inventoryManager.addEventListener(FaultEvent.FAULT, ...
inventoryManager.getCurrentInventory();
}
Listing 2: Remote method call to a server-side
object from Flex
When the remote call returns, Flash Player on the client deserializes
the return value of the method into an ActionScript
ArrayCollection
. Since the domain classes are annotated
with the remote class' names, Flash Player ensures that each element of
that collection is an instance of the ActionScript
Book
class defined above and, further, that each book's
publisher
field refers to an instance of a
Publisher
object. This demonstrates one of BlazeDS's
biggest productivity boosts: It automates serialization to and from
server-side objects and their client-side equivalents.
Scala on the Server
In order to enable the service for remote method invocation, BlazeDS
requires that a Java class be available to BlazeDS's class loader with
the method names and parameters specified by the client. That class must
be registered in BlazeDS's configuration, and associated with a remote
destination name. The following snippet accomplishes this registration
in BlazeDS's remoting-config.xml
file:
<destination id="inventorymanager">
<properties>
<source>scalaflex.InventoryManager</source>
<scope>application</scope>
</properties>
</destination>
Listing 3: BlazeDS remote object configuration
Based on this configuration, BlazeDS creates an instance of
scalaflex.InventoryManager
, and associates that instance
with the inventorymanager
remote destination name.
scalaflex.InventoryManager
can be implemented in any
JVM-based programming language, as long as BlazeDS's class loader can
locate that class and instantiate it via a no-arg
constructor.
The following code example illustrates a possible Scala
implementation of InventoryManager
, using the Java
Persistence API to obtain results from a database. The
getCurrentInventory()
method performs a Java Persistence
API
query, and returns a list of Book
s whose inventory count is
greater than 0.
package scalaflex
import InTransaction._
class InventoryManager {
def getCurrentInventory() = {
txn[java.util.List[Book]] {
val q = EmThreadLocal.get().createQuery("from Book as bk where bk.stock > 0")
q.getResultList.asInstanceOf[java.util.List[Book]]
}
}
}
Listing 4: Implementation of
InventoryManager
in Scala, using a Java Persistence API
query
The first thing to note about this example is that the method's
return
type, java.util.List[Book]
, is inferred by the Scala
compiler, and thus can be left out of the source code.
You may also notice the txn {...}
construct that
surrounds the JPA query. Although txn {...}
looks like a
control structure in the language, similar to if
or
while
, it is actually a method defined in another class,
InTransaction
. In Scala, when a method has only one
argument, you can use curly braces instead of parenthesis to surround
that argument. In this case, txn
takes a block of code,
ensuring that that code is executed in the context of a JPA
transaction.
Members of InTransaction
are imported at the top of the
source file: the _
is a Scala wildcard character, meaning
"import all visible members of InTransaction
." The
following
shows the implementation of the txn
method:
package scalaflex
object InTransaction {
def txn[T](block: => T): T = {
val entityMan = getEntityManager() // Obtain the JPA Entity Manager
// The code is not shown.
EmThreadLocal.set(entityMan) // Set the entity manager in
// a threadlocal variable so that
// it is available to the code
// performed under the transaction.
val txn = entityMan.getTransaction()
try {
txn.begin()
val res = block // Execute the block passed in as
txn.commit() // a parameter.
res // Return the results
}
finally {
if (entityMan.getTransaction().isActive())
entityMan.getTransaction().rollback()
if (entityMan.isOpen())
entityMan.close()
EmThreadLocal.remove() // Remove the entity manager from
// the threadlocal.
}
}
}
Listing 5: JPA Scala helper class
Note that the block passed to the txn
method may or may
not return some results. In the case of
getCurrentInventory()
it returns a list of
Book
s. When invoking txn
, we specify the type
of return value we're expecting from the method:
txn[java.util.List[Book]] { ... }
This may seem like an unnecessarily verbose construct but, in fact,
specifying this value is an important step in making the application
reliable. Note that in Listing 2, the Flex client casts the result from
the remote method call to an ArrayCollection
consisting of
ActionScript Book
instances:
private var books:ArrayCollection;
...
function(event:ResultEvent):void {
books = event.result as ArrayCollection
}
By explicitly specifying the result type of the code performed under
the JPA transaction on the server, we can be confident that the client
can always perform this cast, and that the books
collection
will always consist of Book
objects.
Entity Classes
There are two requirements on the entity classes on the server:
First, BlazeDS must be able to properly serialize instances of such
classes in a way that the Flash Player can then deserialize that data
into typed ActionScript classes. Second, the entity classes
must be written such that they can be persisted via the Java Persistence
API.
Both requirements can be satisfied by making the entity classes
follow the JavaBeans pattern of getter and setter methods, and by
providing a no-arg constructor. Unlike Java, but similarly to
ActionScript, Scala supports properties, and thus getters and setters
would not be needed, if not for BlazeDS's requirement. Tailor-made for
just such a requirement, Scala's BeanProperty
annotation
instructs the Scala compiler to generate getter and setter methods for
class fields:
package scalaflex
import scala.reflect.BeanProperty
import java.math.BigDecimal
import javax.persistence.{Id, GeneratedValue, ManyToOne, Entity}
@Entity case class Publisher(
@Id @GeneratedValue @BeanProperty var id: Long,
@BeanProperty var name: String) {
def this() = (0, null)
}
@Entity case class Book(
@Id @GeneratedValue @BeanProperty var id: Long,
@BeanProperty var authors: String,
@BeanProperty var year: Int,
@BeanProperty var price: BigDecimal,
@BeanProperty var stock: Int,
@ManyToOne @BeanProperty var publisher: Publisher) {
def this() = (0, null, 0, null, 0, null)
}
Listing 6: JPA entity classes in Scala
In Scala, a class's properties can be specified as part of the class
declaration. By defining the bean properties properties as
var
s, the compiler generates both getter and setter
methods. Note also that JPA annotations can be used on Scala entity
classes just the way you'd use them on Java classes (see the note at the
end of the article for an important limitation).
Finally, note the class declarations are prefaced with the
case
keyword. Scala's case classes are another example of
the Scala compiler generating meaningful boilerplate code. For a case
class, the compiler creates a factory method, such as
Publisher(id: Long, name: String)
. It also ensures that the
fields specified in the class declaration are accessible in the class;
finally, the compiler also provides sensible implementations of
toString()
, equals()
, and
hashCode()
.
Putting It All Together
The final requirement for this example to work is a
persistence.xml
file. Mandated by the JPA specification,
the file contains configuration information needed to create the
persistence context:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence "
http://java .sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="MileageCommander" transaction-type="RESOURCE_LOCAL">
<non-jta-data-source>java:comp/env/scalaflex</non-jta-data-source>
<class>scalaflex.Publisher</class>
<class>scalaflex.Book</class>
<properties>
<property name="hibernate.dialect"
value="org.hibernate.dialect.PostgreSQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
Listing 7: persistence.xml
configuration
file
Listing 7 specifies a non-JTA data source called scalaflex
,
as well as the persistent entity classes. Finally, property values are
provided for the Hibernate JPA entity manager implementation. The
non-JTA
data source is suitable for use in a standalone application, or inside
Tomcat.
As in the Java version of this application, you will need to ensure
that the BlazeDS configuration files are available to the Web
server. In addition to the persistence provider JAR files, you will also
need to place the Scala JAR file, scala-library.jar
, to the
Web application's lib
directory. The outline of
your Web application's directory structure may look as follows:
ScalaFlexWebApp
|-- HTML and JSP files
|-- Compiled SWF files
|-- WEB-INF
|-- web.xml
|-- lib
|-- scala-library.jar, hibernate-entitymanager.jar, ...
|-- flex
|-- services-config.xml
|-- remoting-config.xml
In addition to the configuration files in the the flex
directory, you also need to reference BlazeDS's messaging servlet in
web.xml
:
<servlet>
<servlet-name>MessageBrokerServlet</servlet-name>
<servlet-class>flex.messaging.MessageBrokerServlet</servlet-class>
<init-param>
<param-name>services.configuration.file</param-name>
<param-value>/WEB-INF/flex/services-config.xml</param-value>
</init-param>
<init-param>
<param-name>flex.write.path</param-name>
<param-value>/WEB-INF/flex</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Finally, for completeness' sake, if you're using Tomcat to run this
example, you can define the scalaflex
resource referenced
in persistence.xml
inside Tomcat's
TOMCAT_HOME/conf/context.xml
file. This resource provides
information to the JPA implementation to connect to a database. Other
Web servers or application servers will likely use different means of
specifying this sort of data:
<Context>
<Resource name="scalaflex" type="javax.sql.DataSource"
auth="Container" driverClassName="org.postgresql.Driver"
username="staircase" password="lambda"
url="jdbc:postgresql://localhost/scalaflexdb"/>
</Context>
Summary
This article demonstrated that relatively sophisticated functionality
can be provided by using Flex on the client, Scala on the server, and
BlazeDS as an intermediary to facilitate remote calls and object
serialization.
Configuring Eclipse to Work with Scala and Flex
The following steps describe one way of working with Scala and Flex
from the same Eclipse IDE. Note that Adobe's Flex Builder is a mature,
commercial Eclipse plug-in, and you will likely encounter very few, if
any, issues in getting it to install and work in any recent Eclipse
version. The Scala IDE for Flex is also rapidly becoming more mature,
but you may need to follow these steps to ensure you can effectively
work with the current version:
- Make sure you have an up-to-date, 3.4 version of Eclipse. If you
don't already have such a version, download "Eclipse Classic 3.4" or
higher from http://www.eclipse.org/downloads
- Download the FlexBuilder plug-in from http://www.adobe.com/products/flex/features/flex_builder/,
and follow the installation instructions. A 60-day free trial is
available.
- Download the Scala IDE for Eclipse from http://www.scala-lang.org/node/94,
and follow the installation instructions. In particular, use the
Eclipse Update center for the install.
- At this point, create a new Scala project first. Inside that
project, create the Web application directory structure described in
this article: A directory for Web contents, and inside that directory a
WEB-INF subdirectory. Inside WEB-INF, create the BlazeDS flex directory,
and follow the instructions about setting up the BlazeDS configuration
files from http://opensource.adobe.com/wiki/display/blazeds/Installation+Guide.
- Next, create a new Flex project. When FlexBuilder asks whether you
want to use Flex Data Services, answer yes, and choose Java EE as the
server-side technology used. FlexBuilder will then ask about your Web
application's directory. Specify the Web contents directory created in
the previous step. In particular, make sure the flex subdirectory inside
WEB-INF exists, and that it has the
services-config.xml
file described in the BlazeDS documentation. This step accomplishes two
things: First, it will instruct Flex Builder to add the path to services-config.xml
to the Flex compiler as the services
parameter; and it
will tell Flex Builder to output its compiled files into your Web
application's output folder.
- At this point, you are ready to develop both the Scala and Flex
parts of the application. To run the app, you can point Tomcat (or some
other Web server) to the Web contents directory you created in step 4.
- Some of these steps can be automated by creating an Eclipse Web
Tools Platform (WTP) project, instead of a Scala project. However, your
mileage in that case may vary. The Eclipse Scala IDE is making rapid
progress, so make sure you check for updates frequently.
Using JPA Annotations on Scala Classes
In general, any Java annotation can be used with Scala classes. Such
annotations are left untouched by the Scala compiler and are compiled
into the binary Java classes, if the annotations have the appropriate
retention.
In the current, 2.7.4. version of Scala, however, there are some
limitations to the kinds annotations you can use. There are specifically
two problem areas: First, annotations with variable optional arguments
may not be processed properly by the Scala compiler. javax.persistence.OneToMany
is an example of such an annotation, with 4 optional arguments. Second,
nested annotations may also not be processed correctly, and result in
compiler errors.
The upcoming, 2.8 version, of Scala will fix those issues. In the
meanwhile, if you need to use such annotations in a JPA application, you
can opt instead for specifying entity relationships inside an orm.xml
file. In any case, although the JPA spec focuses a great deal on
annotations, you may find that specifying persistence-related
information in a separate file leaves your source code cleaner.