Getting the Most out of Expresso 5.0

Java Development News:

Getting the Most out of Expresso 5.0

By Michael Rimov

01 Oct 2002 | TheServerSide.com

Introduction

Back in February of this year, an article by Peter Pilgrim was posted on TheServerSide describing best practices for Expresso using Struts. As with any fast-paced Open Source project, a lot has happened since the time of the article. For those that are unfamiliar with Expresso, it is an Apache-style open source web application framework hosted by JCorporate at http://www.jcorporate.com/ . Expresso uses many 3rd party libraries, and as of Expresso 4.0, it has merged its own MVC framework with the Apache Struts framework.

An overview of the Expresso framework is provided in Peter's article at /articles/article.tss?l=Expresso-Struts.

Although this release of Expresso boasts the usual items that one has come to expect in an open source project: many bug fixes, and a plethora of performance improvements, there are several more areas of Expresso that a programmer should be aware of to maximize the benefits of this framework. The goal of this article is to provide programmers with an idea of what's changed in Expresso and what's new and exciting in the framework. This article will start with the less interesting, but important aspects of what has changed, as far as the developer is concerned, that might impact his existing code design.


What happened to Expresso 4.1?

A lot of work and development has gone into Expresso since the last release. As a community, we decided, albeit late in the development cycle, that we should change the version number of this release to 5.0 to more appropriately reflect the number of changes that have gone into this release.


Do I need to change a lot of code to Migrate to 5.0?

Not at all! There are just a few minor API adjustments. In the Expresso 5 doc index, you will see a link to a change log that provides coverage of all API and Expresso schema adjustments. All such critical changes have been highlighted in red in the change log. To further allay your fears, we've had at least one person that migrated from 4.02 to 5.0 and didn't have to change one line of code to get it to compile and run.


Recommended New Practices

There have been several changes that have occurred in the Expresso framework. Although a thorough listing of them can be found in the Expresso distribution's change log, I will discuss here two of the more subtle code practice differences that should be used for reasons outlined below.


New Schema Definition Methods

The Expresso Schema Object is the central class the glues all your other components: the DBObjects, the Controllers, and the Jobs into a cohesive unit. In the past, a DBObject, Controller, or Job was added through statements like those below:


addDBObject("com.mycompany.myapp.dbobj.MyObj");
addController("com.mycompany.myapp.controller.MyController");
addController("com.mycompany.myapp.job.MyJob");

This worked great except for one problem: If you made a mistake typing, you didn't catch it until the Schema threw an exception because it tried to instantiate a Java class that didn't exist. To solve this, we've provided new method signatures that rely on class objects rather than Strings. This changes the above statements to:


addDBObject(com.mycompany.myapp.dbobj.MyObj.class);
addController(com.mycompany.myapp.controller.MyController.class);
addController(com.mycompany.myapp.job.MyJob.class);

By using the above code changes, we can now rely on the Java compiler to actively spot our typographical errors. There are many places in Expresso that now use this style of code and if any method provides a class method for defining a link to another class rather than a java.lang.String, we highly recommend that you use the class method.


New Factory Methods

Expresso Schemas and Controllers both have a couple of properties that have allowed us to optimize their usage:

  1. They are 'read only' in the sense that once created, they don't have any methods that change their state.
  2. They are very computationally expensive to create.

To alleviate this, we've added a ControllerFactory class and a SchemaFactory to manage instances of controllers and schemas respectively. We will now look at the details of their usage.


ControllerFactory Usage

The code to instantiate a new controller is quite simple. Here is an example:


import com.jcorporate.expresso.core.misc.ConfigManager;
import com.jcorporate.expresso.core.controller.*;

...

Controller myController = ConfigManager.getControllerFactory()
    .getController("com.mycompany.controller.MyController");

ConfigManager is responsible for determining what kind of controller factory is used. The default one caches the last 20 or so controller instances in memory. This is used for command line invocations of controllers. The one instantiated by the DefaultInit servlet is directly wired into the Struts Action Servlet [or more correctly, a class directly derived from the Struts Action Servlet], which returns the appropriate controller based upon what is already instantiated with Struts, thus preventing duplicate instances of Controllers, which have to be thread safe.


SchemaFactory Usage

The code to instantiate a SchemaFactory is similar, although there is only one SchemaFactory object currently defined, so you do not have to go through ConfigManager to get an instance. The code is shown below:

import com.jcorporate.expresso.core.dbobj.Schema;
import com.jcorporate.expresso.core.dbobj.SchemaFactory;
Schema mySchema = SchemaFactory.getInstance()
     .getSchema("mySchemaClassName");

Using this code, you can safely guarantee that there is only one instance of a Schema-derived object in memory at a time, providing significant speed improvements. Make sure, however, that your Schemas have 'read only' behavior once they are created. Otherwise, you could cause undefined behavior in other areas of the framework, such as caching, that are counting on all attributes of a Schema to remain constant throughout the lifetime of the object. If you need read/write behavior for your Schema, consider offloading the read/write behavior and attributes into an external class and referencing this class directly from your code so you do not cause any consistency problems with other objects in the framework that might refer to your Schema object.


New Features

Ok, so moving on from the dull and boring nit picks, let's get to the fun stuff that everybody likes to really read about. The new features included in this release have taken on many facets.


New Controller Extension Mechanisms

In the past, reusing code between controllers usually meant refactoring your objects so that the common code was in an external class. While this was certainly a sound approach, it also required a lot of work. With this release, our development team has introduced the ability to easily derive your controllers from one another, by only having to override the default behavior of each controller. You can also easily use this mechanism to provide your own UI behavior without rewriting state handlers, saving yourself literally hundreds of lines of duplicate code.


Controller State Handler Overview

Each controller has what are known as state handlers to accomplish its goal of modeling a finite state machine. These state handlers are defined in the constructor for the controller. They are all the functions you will find in Expresso controllers with the name patterns of:

run****State(ControllerRequest,ControllerResponse)

The Expresso framework finds these state handlers through the use of a Java reflection call to getDeclaredMethod(), as long as you follow the stated naming convention like what is listed above.

You might ask what is so special about this "new" extension mechanism since the Expresso state handlers using Java reflection have been in use for over a year since Expresso 4.0's release. The problem with the getDeclaredMethod() function is that it only provides details of the leaf class and doesn't walk through the object hierarchy to see if there is a method with the appropriate definition in the base classes. We have fixed this by iteratively traversing the object hierarchy, looking for appropriate state handlers to invoke.


Deriving your own Controller


UI Only:

To be able to create your own controller with it's unique security, unique UI and the same basic functionality as another controller, all you have to do, code-wise, is make a class the derives from an existing controller, and then add that controller to your Schema. Here's a quick example:

class MyController extends MyOtherController {
    MyController() {
        super();
    }
}

Adding the controller to the schema is left as an exercise to the reader. Once you have the code done, all you do is copy and paste the base class' entries in their struts-config based xml file, and paste it into your own app's config file. Change the mapping to whatever you want, change the type to point to MyController, and then change the action forwards to point to a new set of jsp pages to use. Voila, instant new look! What good is this? It significantly helps for two special reasons: One is where you're a consultant programmer and you wish to provide a base class for your work, so that you can just provide new skins for each of your clients; and the other reason is it allows you to use the existing Expresso controllers in your own app, provide your own look and feel, and not have to change a single line of Expresso's distribution. This helps you avoid the typical anti-pattern of modifying vendor supplied code.


Changing Behavior

To derive your own controller where you want changed behavior, follow the same steps for changing the UI. Then simply override the state handlers you wish to provide different behavior for. Interestingly, the Expresso development team's own experience has shown that this is a highly effective way of providing code reuse because since a Controller is essentially stateless, it is much more difficult to accidentally break obscure dependencies that happen so often in other derivation situations. Here is an example of a modified controller:

class MyController extends MyOtherController {
    MyController() {
        super();  //Super class has state 'myOutput'
    }
    
    protected ControllerResponse runMyOutputState(ControllerRequest
              request, ControllerResponse response)
              throws ControllerException {
        response.add(new Output("myOutput","I want this instead"));
        return response;

    }
}

The Expresso development team has successfully used this scheme in many of their private projects, which has allowed for good code reuse.


SSL Capabilities

Approximately six months ago, the developers at eBuilt, Inc. posted an SSL extension for the Struts framework to the Apache site. They provided some code to allow Struts to redirect between secure and non secure protocols while maintaining the state of the current user's session. The only problem with this extension was that this code required manual patching of the Struts ActionServlet to fully utilize its capabilities. So to allow for easier integration, we've taken this code and integrated it directly into the Controller object to allow state-by-state switching between SSL and non-SSL protocols. To use it is quite easy:

class MyController extends DBController {
    MyController() {
        State s = new State("mySSLState");
     s.setSecure(true);
        this.addState(s);
    }
}

The one line setSecure(true) is all that is needed to designate that a controller state should be run in a secure state. Expresso will take care of the rest for you.

What if in some installations the client wants SSL and the other client does not? Not a problem. SSL usage is turned on globally by the attribute useSSL in the expresso-config file. If it is set to true and Expresso comes across a secure state, then Expresso will redirect the browser to SSL. If it is set to false, then Expresso simply ignores the 'secure' attribute.


FastStringBuffer and Object Pooling

Nearly any book on performance will tell you that managing memory more precisely than the system garbage collector will significantly improve performance. Thanks to the Apache commons-pool project, this is the first release of Expresso to begin using object pooling. (This should not be confused with Connection Pooling, which Expresso has already used for many years).

Performance programming books will tell you that StringBuffer's speed can be enhanced if you pre-allocate the amount of memory you think you will need to use for your string appending routine. There is also mention of removing bounds checking and making custom StringBuffer-like objects that have their methods unsynchronized.

The class com.jcorporate.expresso.core.misc.FastStringBuffer takes all these issues into account. It utilizes the Apache object pool to pool a series of 1K pre-allocated reusable buffers. The team settled on 1K since most StringBuffers will fall into less than 1K of use, thus saving many memory reallocations/copies, while not hogging memory for several instances at the same time. FastStringBuffer also had a clear() method added that allows you to reuse an instance of a FastStringBuffer without reallocating a new object.

Example Usage:

//Get an object from the pool
FastStringBuffer fsb = FastStringBuffer.getInstance();
for (int i = 0; i < 1000; i++) {
   for (int j = 0; j < 1000; j++) {
       fsb.append("a");
   }
   System.out.println(fsb.toString());
   fsb.clear(); //Clear the String buffer for reuse.
}
fsb.release()  //Release it back into the pool

As you can see, the only real memory allocations that take place are in the first line if there isn't already a FastStringBuffer instance in the object pool, and the call to FastStringBuffer.toString() to allocate a new String for use by the rest of the system.

You will see several more instances of Object Pooling begin to appear in future versions of Expresso, but this will be discussed more fully at the end of the article.


Tiles Integration

Since Expresso uses its own Struts ActionServlet-derived servlet, it effectively blocked usage of the great Tiles library without modifying code. This has often been lamented in both our own mailing lists and the Struts-user list. So with this version, the development team took matters into their own hands and we included the Tiles library within our own code and changed ExpressoActionServlet to derive from the Tiles servlet, thus providing full integration capabilities. While people are still working out the best usage patterns to integrate Expresso Controllers with Tiles, there have been several projects that have reported successful usage of this integration.


Experimental BLOB and CLOB Support

One of the features lacking with Expresso has been support for arbitrarily long database fields, whether binary or character based. With this version of Expresso, we've provided the ability to add, update, and read long objects from an underlying data store. Before we go on, let's discuss the caveats in this feature:

We found that many JDBC drivers do not currently provide extensive BLOB support. For example, the InterClient 2.01 JDBC driver for the InterBase database, still does not provide JDBC 2 level BLOB support. As such, we've been forced to use the API calls that read the entire long object into memory. So if you are planning on using this feature, please be sure to keep your objects small in size, and don't consider Expresso DBObjects as your solution for storing DVD-length movies in your database.

Secondly, BLOB/CLOB support hasn't been fully tested on all database platforms. It has been successfully tested on MySQL, PostgreSQL, Hypersonic, and SAP DB. So far, we can get CLOBs to work on Firebird (and thus most likely InterBase) using InterClient 2.01, but BLOB tests have failed. We hope to have better results when the Firebird all-Java JDBC driver is released in a final state.

If, after reading this, you're interested in trying BLOB/CLOB support, then here's how you use the feature:


CLOBs

To use a CLOB, you simply have a field in your DBObject that maps to a CLOB database field. You then use:

myObj.setField("myField",myString);
myObj.update();

The usage is similar to a standard String field. In fact, CLOB support has blurred the lines between MySQL 'text' fields, and other database CLOB fields. The programmer's usage is identical.


BLOBs

BLOB Fields are accessed through the DBObject method: getBinaryField(). You call this method after you have already retrieved the data record via the usual methods such as searchAndRetrieveList(), find(), retrieve(); Any of them will work. Once you call getBinaryField(), you will be handed an InputStream from which you can read the binary contents.


Batch Adds / Batch Updates

DBObjects can now be added or updated in batches with the use of the following example code:

//This ArrayList contains DBObjects of identical type,
//that have all their fields set to what is wished for
//an add or update operation
ArrayList objectsToUpdate;

//myObj is a DBObject that should be initialized
//with the appropriate information.
DBObject myObj;

....  //Perform whatever code is needed here.

//Or: myObj.getExecutor().addBatch(objectsToUpdate);
myObj.getExecutor().updateBatch(objectsToUpdate);

Our tests indicate that for 20 or so updates at a time,, there is approximately a 30% performance improvement by using batch updates.


Other Miscellaneous New Features

There are several new features that will be briefly mentioned here.

SQL Injection protection has now been added at the DBObject level. Before a field is sent to the database, it will be automatically filtered and any special characters for the database will be escaped as per that database's rules. The development team so far has found two methods that databases use for escaping special strings. The first is escaping quotes with a backslash, in a 'C language' style, and the other is to double the quotation. Both cases have been defined in the Expresso library, and are referenced in the context definition of the expresso-config.xml file.

The MimeTypes table now has full JAF integration in that it can map file extensions to the appropriate mime types. It is recommended that you use this over what is usually set for your servlet engine context or the default JAF file types handler since the MimeTypes table currently has a database of over 75 different file formats.


Experimental Container Authentication Integration

If a user has authenticated against the application server, Expresso will attempt to look at its user database to match the authenticated user with what's known in its own database. If it can find a match, it will generate a login cookie based upon the container's authentication.


Low level Connection retrieval

If you need that extra performance out of your code, you can grab a low level database connection from the DBConnectionObject via the new getConnection() function. Once you have this connection, you can execute your own custom stored procedures, prepared statements or any other custom database-server side behavior that would normally be inaccessable from the framework level. You must be sure, however, to release all references to the java.sql.Connection object before releasing the DBConnection back into the DBConnectionPool. Otherwise, you will leave DBConnectionPool in an undefined state. Using this feature, in many ways, is analogous to writing inline assembly language code in a C++ program: in some cases, the performance improvements by executing the custom code can dramatically outweigh the risks involved with the lower-level connection usage.


Future Directions

As with any open source development effort, the direction that a project might take is often vague at best. However, we do have a few specific directions that have been emphasized.


Struts Integration

Continued Integration with Struts is Expresso's number one priority. As soon as Struts 1.1 final is released, we will be checking into the Expresso CVS the integration code necessary to fully stay in tune with Struts 1.1. We will be also be researching how to better integrate with new Struts features such as Validation and Workflow. For those that have been following both Expresso and Struts development, the one thing that I can be certain of is that the core development team considers Struts integration the best thing that ever happened to Expresso, and we're committed to follow its development to remain integrated.


Data Object Interface

Over the course of the various Expresso 5 releases, more and more of the DBObject code will be relegated to a series of Data Object interfaces. The goal of this is to provide a set of interfaces independent of the persistence mechanism, that has the full functionality that we've come to expect from DBObjects with CRUD (Create/Read/Update/Delete) support, transaction support, field validation, and aggregate queries. Examples of persistence mechanisms that Expresso's Data Object interfaces will provide a unified interface for include Entity EJBs, XML data files, Relational Databases, Comma Delimited files, JMS data sources, and any other persistence mechanism that the future may bring.


JSF, JSTL, JCache, and other working standards

Thanks to the Java Community Process, there are several interesting server-side technologies such as standardized caching, tag libraries and architectural patterns. We will continue to monitor these standards and adopt them as the community sees fit. The adoption rates vary, but the developer community often takes a conservative approach so as not to force our users to change their programming habits to a standard that doesn't get widely adopted.


Conclusion

Expresso has had many refinements and new features added since its last release. For those interested, they can download Expresso and check out the future development road map to get a better idea of where Expresso is going

It is hoped that this article will assist users in making the transition to a new version of Expresso. The transition is not overly painful, and the capabilities of the framework have been iteratively enhanced. With a very active and strong community, Expresso will continue to see growth and a steady stream of improvements and new features.



About The Author

Mike Rimov has been in the professional software development arena for over ten years He is Senior Developer for Centerline Computers, Inc. a company that provides many programming services for a wide variety of custom needs. You can find him hanging out on Jcorporate's OpenSource mailing list, or you can contact him directly at rimovm@centercomp.com


References: