devxlogo

Taking a Java-Based App Offline Using Flash Builder, LiveCycle Data Services

Taking a Java-Based App Offline Using Flash Builder, LiveCycle Data Services

There are many benefits to taking an enterprise application offline, especially in today’s world, when everyone is seemingly connected to the Internet. Enterprise users often find themselves outside their corporate firewall. But rather than dealing with a slow-to-respond IT department and less-than-straightforward security concerns, they want their applications ready to deal with such situations. In the case of a complex application, a subset of the application’s functionality should be available if the application’s main server is not reachable for one reason or another.

 

The article builds a sample application that can seamlessly transition between online and offline connectivity status. While offline, users will still be able to use the application until connectivity is restored and the changes can be pushed back to the server.

 

Enterprise Considerations

 

Identifying the offline use cases is of course a core business concern. This article exemplifies a fairly simple use case in which an application needs to be taken offline and users should be able to comment, annotate, and rate field results for an existing project. This seems like a minimal use case for offline, but you soon realize that taking a feature offline presents many challenges, such as how to handle basic create, read, update, delete (CRUD) operations; how to deal with existing object relationships and synchronization issues caused by concurrent access to the same data; conflict resolution; and the list does not end there.

 

Improving a software product’s usability should be a major factor here, so features like detection of online or offline status and seamlessly switching between the two, allowing the server to push updated data back to online clients, would constitute some nice touches to the application, consequently making it less prone to concurrent conflicts. In addition, such features prevent users from taking extra actions like manually starting synchronizations (the now-inactive Google Gears project comes to mind).

 

Glancing at the Adobe documentation, you quickly come to the realization that running the application in a browser (compiled as an Adobe Flash plug-in) would give the application little space for the local cache, which leaves you with one choice: running the offline application on top of the Adobe AIR runtime (which uses an embedded SQL-based database). One nice way of dealing with adaptation is to automatically redirect the user to the Adobe AIR application when the network status changes to offline and, if the runtime is not installed, informing the user that offline capabilities are not available and presenting the steps required perform the installation. You can address some of these challenges immediately by properly modeling your Document Object Model (DOM) and structuring the application with reusable components and application programming interfaces (APIs).

 

The Environment

 

Here are the minimum requirements for setting up this project:

  •  

       

      * Java 1.5.

       

      * Apache Maven 2.

       

      * Apache Tomcat 6 (or any other modern servlet container). In the context of this article, you’re actually going to use the embedded version of Tomcat that ships with LCDS samples containing all the other dependent libraries and a memory-based HyperSQL (HSQL) database ready for use.

       

      * The Eclipse integrated development environment (IDE), as Adobe Flash Builder is also built as an Eclipse plug-in.

       

      * Adobe Flash Builder 4 beta 2, along with the Adobe Data Services 3 plug-in, as you are going to use the Fiber’s code-generation capabilities. You manage integrating the two components and breaking the different online-offline states in the application life cycle with LCDS. Figure 1 shows the environment structure.

  • * The back end consists of a Java web application based on Adobe LiveCycle Data Services (LCDS), with simple plain old Java objects (POJOs) persisted using Hibernate. For this application, you need:

     

    * The front end is built using Flash Builder. For this application, you need:

 

 

Figure 1. The environment back end and front end structure

 

Back-end Code

 

Set up a simplistic back-end service consisting of a project model containing comments and ratings objects. You’re going to use Hibernate for persisting objects into a database; for simplicity’s sake, you’ll annotate the POJOs using Java Persistence Architecture API (JPA) annotations that LCDS will make good use of later on.

 

The simplistic plain old Java domain objects are Project, which has names and a list of enclosing Comment objects. The Comment objects have subject, comment, and rating fields, each with a numeric value:

@Entity@Table(name="projects")@NamedQueries({	@NamedQuery(name="project.all",query="from Project"),	@NamedQuery(name="project.byId",query="from Project p where p.id = :id")})public class Project {@Id @GeneratedValue@Column(name="projectId")private Long id;@Column(name="name")private String name;@OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)private List<Comment> comments;…public class Comment {@Id @GeneratedValue@Column(name="commentId")private Long id;@Column(name="subject")private String subject;        	@Column(name="comment")private String comment;@Column(name="rating")private int rating;

 

You’ve probably noticed the one-to-many association between the Project and Comment objects, having Hibernate cascading and lazy fetching. I also omitted the accessor methods for these POJOs; but by using Eclipse’s built-in Generate Getters and Setters feature, you can easily generate them automatically, as shown in Figure 2.

 

 

Figure 2. Eclipse can automatically generate getter and setter methods.

 

Reference these getters and setters in your Hibernate mapping file (hibernate.cfg.xml):

   "-//Hibernate/Hibernate Configuration DTD 3.0//EN"     "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">                        org.hsqldb.jdbcDriver                      jdbc:hsqldb:hsql://localhost:9002/offline                  sa                            1                   org.hibernate.dialect.HSQLDialect                   thread                                org.hibernate.cache.NoCacheProvider                             true                   create                                  

For convenience, I generated the database schema using the hibernate3-maven-plugin, which has the smarts to create it using the previous JPA annotations. A simple Maven package command creates a file located at target/hibernate3/sql/schema.ddl by using the following Maven declaration in the build phase:

	org.codehaus.mojo	hibernate3-maven-plugin	2.2						process-classes							hbm2ddl																hbm2ddl				jpaconfiguration										Default			schema.ddl			false			true			false			true			

And this is how the schema looks after feeding it into the LCDS HSQLDB utility:

CREATE MEMORY TABLE COMMENTS(COMMENTID INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1) NOT
NULL PRIMARY KEY,COMMENT VARCHAR(255),RATING INTEGER,SUBJECT VARCHAR(255))
CREATE MEMORY TABLE PROJECTS(PROJECTID INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1) NOT NULL
PRIMARY KEY,NAME VARCHAR(255))
CREATE MEMORY TABLE PROJECTS_COMMENTS(PROJECTS_PROJECTID INTEGER NOT NULL,COMMENTS_COMMENTID
INTEGER NOT NULL,CONSTRAINT SYS_CT_50 UNIQUE(COMMENTS_COMMENTID),CONSTRAINT FK62BEE019928A30BE
FOREIGN KEY(COMMENTS_COMMENTID) REFERENCES COMMENTS(COMMENTID),CONSTRAINT FK62BEE019CE90EEB8
FOREIGN KEY(PROJECTS_PROJECTID) REFERENCES PROJECTS(PROJECTID))

To avoid creating separate assemblers for domain objects, use the LCDS-standard HibernateAnnotationsAssembler, because your POJOs already have Hibernate annotations. (You might want to implement the assembler interface if you need a custom solution or if you are architecting an application that uses data transfer objects that are different from your domain model, usually to address performance or scalability issues.) Here is the LCDS configuration file containing the destinations directives using the HibernateAnnotationsAssembler (data-management-config.xml):

				true		true		flex.data.assemblers.HibernateAnnotationsAssembler		application									example.offline.Project																fill				java.util.List										false				true										true		flex.data.assemblers.HibernateAnnotationsAssembler		application		example.offline.Comment	

The auto-sync-enabled option is helpful if you want to have the server push changes made on the server side to the client. Be careful with this option, however, because it can generate a lot of network traffic and might have a significant performance impact. Depending on the number of concurrent users, an increase in the server resource specifications might be warranted.

 

Note: The one-to-many association from the Hibernate mapping is present in the project destination definition.

 

Just to get things rolling, start the HSQLDB database using the supplied startup script, copy the class files and hibernate.cfg.xml in the WEB-INF/classes directory, and add the two destinations to the LCDS sample directory (typically, {lcds}/tomcat/webapps/lcds-samples/WEB-INF/flex).

 

Front-end Code

 

On the Flash Builder side, you can bring the model object described earlier over automatically using the LCDS plug-in for Flash Builder 4’s new capabilities. When creating the front-end module, perform the following steps:

    1. On the Create a Flex project wizard page, select J2EE as the application server type, select Use remote object access service, and then select the LiveCycle Data Services ES option, as shown in Figure 3.

     

     

    Figure 3. Creating the front-end module.

     

    2. On the Configure J2EE Server wizard page, configure the LCDS destinations, as shown in Figure 4.

     

     

    Figure 4. Set the default location for Tomcat.

     

    3. To trigger the code-generation feature, click Data > Connect to LCDS, then choose your previously defined destination, projectDestination, as shown in Figure 5.

     

     

    Figure 5. Select your project destination.

     

    4. Click Finish.

Many classes are automatically created-for example, Project.as, Comment.as, ProjectService.as, and _Super_Project.as. All the generated code is nicely enclosed in _Super_Project.as, allowing you to edit and eventually override methods in ProjectService.as. In fact, you need to override the fill function to take into account the earlier JPA @NamedQuery declaration:

override public function fill(arg0 : ArrayCollection):AsyncToken{	var ac:ArrayCollection = new ArrayCollection(); 	var token:AsyncToken= _serviceControl.fill(ac, 'project.all', arg0);	token.resultAC = ac;	return token;          }

Again, take a simplistic approach: a minimal user interface (UI) consisting of a top menu bar with the option of opening existing projects, exiting the application, and the classic Help > About. A status message at the bottom indicates whether network connectivity is either online or offline. When a project is opened, a grid appears containing Comment objects, editable inline when you click a row, as shown in Figure 6.

 

 

Figure 6.

The simple UI

 

These are basically two data grids. Editing comments in the main window triggers a save operation when you edit a row, and the Open Project pop-up window displays a list of projects to choose from:

 
dataProvider="{comments}" editable="true" x="9" y="24" itemEditEnd="saveComment(event)">




Be sure to set a cache identifier when the application initializes by attributing a unique value to the ds.cacheID property on the data source.

 

The conflictHandler should implement a conflict resolution that is presented to the user. For example, if two different users have edited the text of the same comment, a differential approach should be presented rather than simply displaying the whole text, so that a decision looks obvious to the user.

 

Taking the application offline and bringing it back online is done automatically. You implement the functionality by opening a SocketMonitor and adding a handler intended to listen for the network status change event:

private var monitor:SocketMonitor;monitor = new SocketMonitor('localhost',2037); monitor.pollInterval = 30;monitor.addEventListener(StatusEvent.STATUS, statusChangeHandler);  monitor.start();

Many times in an enterprise, listening for network connectivity on a port where the application server runs is sometimes not enough: A single server might run multiple applications. A safer bet is to monitor to an endpoint that has the capability to detect whether the application in question is running or in maintenance mode or whether any other event might derail reliable access. The statusChangeHandler shows the network status, propagates any changes to the server, or populates data from the cache if the application is in offline mode:

private function statusChangeHandler(event:StatusEvent):void{	if (monitor.available)	{		statusLabel.text = statusImage.toolTip = 'Online';		statusImage.source = onlineIcon;		if (ds.connected)		{			propagateChanges();		}		else		{			connect();		}	}	else	{		statusLabel.text = statusImage.toolTip = 'Offline';		statusImage.source = offlineIcon;		if (noData())		{			retrieveProjects();		}	}}

To test the offline scenario, perform these steps:

    1. Shut down the server after opening a project. The status will change to offline along with the red icon.
    2. Edit one of the comments.
    3. Wait for the server to bounce back.

A message will pop up stating that the changes were propagated from the local cache back to the live server. If the application is started in debug mode, trace messages describe how the application acts depending on the network status.

 

Conclusion

 

In this article, you configured an application to detect its network connectivity transparently and automatically synchronize with the main server using LCDS. Furthermore, online clients can have the updated data saved by other clients pushed directly to their application, resulting in less frequent conflicts. You can mitigate conflicts automatically at the field level (or through a custom Java solution-using reflection comes to mind) or manually either when the user has to assume the final decision or when an automatic solution cannot be strategized.

 

The main challenges you’ll face are mainly related to synchronization. Nevertheless, these challenges are better approached early on, when architecting such an application, rather than later, when technical decisions can result in bad user experience or limitations. Having a seamless transition between online and offline modes is a key differentiator to other solutions, where users would have to bring the data offline to edit it.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist