gembin

OSGi, Eclipse Equinox, ECF, Virgo, Gemini, Apache Felix, Karaf, Aires, Camel, Eclipse RCP

HBase, Hadoop, ZooKeeper, Cassandra

Flex4, AS3, Swiz framework, GraniteDS, BlazeDS etc.

There is nothing that software can't fix. Unfortunately, there is also nothing that software can't completely fuck up. That gap is called talent.

About Me

 

Using Flex with Scala

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 Books 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 Books. 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 vars, 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.

posted on 2010-06-25 09:51 gembin 阅读(970) 评论(0)  编辑  收藏 所属分类: FlexScala


只有注册用户登录后才能发表评论。


网站导航:
 

导航

统计

常用链接

留言簿(6)

随笔分类(440)

随笔档案(378)

文章档案(6)

新闻档案(1)

相册

收藏夹(9)

Adobe

Android

AS3

Blog-Links

Build

Design Pattern

Eclipse

Favorite Links

Flickr

Game Dev

HBase

Identity Management

IT resources

JEE

Language

OpenID

OSGi

SOA

Version Control

最新随笔

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜

free counters