Adding Some Agility to Java EE Application Deployment with GlassFish

by Julien Ponge

Four noteworthy features in GlassFish add agility to Java EE application deployment.

Published February 2012

Downloads:

Download: GlassFish

Download: NetBeans IDE

Download: Sample Code (Zip)

Note: Two editions of GlassFish exist: GlassFish Server Open Source Edition and Oracle GlassFish Server. This article is relevant to both.

Introduction

Deploying and managing Java Platform, Enterprise Edition (Java EE) applications seems like a fairly established activity. Applications can be deployed, undeployed, and upgraded through a combination of deployment and undeployment. Applications use various types of resources, such as JDBC connection pools or Java Message Service (JMS) destinations. Such resources need to be created, configured, and administered using an application server means, such as configuration files, command-line tools, and graphical interfaces. While the tasks do not vary much from one Java EE application server to another, each one is free to provide a broader set of features that make the developer’s and the infrastructure team’s jobs more enjoyable.

This article presents four noteworthy features in GlassFish that increase agility in Java EE application deployment:

  • Session data preservation across redeployments

  • Servlet fragments

  • Application-scoped resources

  • Application versioning

Running Example

This article will use a running example called TaskEE, which is a very simple task list application (see Figure 1) that perfectly serves as an application to deploy. Tasks are stored in a volatile Web session. We will morph TaskEE into TaskEEPA later in the article to store tasks in a relational database rather than in a Web session. We will show only the excerpts relevant to the comprehension of this article; however, the interested reader can study the complete yet simple source code here (zip file).

Figure 1: TaskEE Application

Preserving Sessions Across Deployments

A Java EE application maintains session state in several places. The most widely known places are HTTP Web sessions and stateful Enterprise JavaBeans (EJB) sessions. Starting with Java EE 6 and the introduction of timer services, persistent EJB timers are also associated with session data. The problem with such data is that it’s lost whenever an application is redeployed. Because iterating over the development of an application requires frequent redeployments, a significant productivity loss is suffered. This results from the need to manually replay common use-case steps, such as logging in to an application, filling in some data, and performing certain steps to get back to the state before the redeployment.

By default, application servers erase session data not only because doing so makes deployments easier to manage but also because there are valid reasons for erasing session data. Indeed, updates to an application can include data model and business logic adjustments. The data model can be incompatible between versions, while the business logic can cause some valid session data in a given version to become inconsistent in a subsequent version. This is why erasing session data is generally a safe and reasonable choice. Yet, erasing data frequently bites developers because, in reality, they made forward-compatible changes.

GlassFish offers an option to preserve session data across redeployments. It is turned off by default, but you can explicitly enable it by passing the --keepstate=true flag to the redeploy command.

Suppose that we had already deployed TaskEE. We could redeploy it and preserve all session data as follows:

$ asadmin redeploy --keepstate=true --name=taskee-1.0-SNAPSHOT target/taskee-1.0-SNAPSHOT.war 
Application deployed with name taskee-1.0-SNAPSHOT.
Command redeploy executed successfully.

If you had entered and removed a few tasks in TaskEE, you could easily check that they were preserved after redeployment. You could also perform a session-preserving redeployment from the GlassFish Administration Console. You would just need to make sure that the corresponding option is selected in the redeployment screen, as illustrated in Figure 2. Finally, the NetBeans and Eclipse plug-ins provided by the GlassFish project keep session data by default.

Figure 2: Redeploying from the GlassFish Administration Console

Other means exist for instructing GlassFish to preserve sessions. In the case of Web applications deployed as WAR archives, you can create or edit the glassfish-web.xml configuration file, as follows:

<glassfish-web-app>
(...)
   <keep-state>true</keep-state> 
(...)
</glassfish-web-app>

Similarly, you can edit the glassfish-ejb-jar.xml file in a similar fashion to keep session data inside the EJB container:

<glassfish-ejb-jar>
(...)
   <keep-state>true</keep-state>
(...)
</glassfish-ejb-jar>

 

Finally, keeping session data can be enabled globally in the same way for EAR deployments inside the glassfish-application.xml file. The –keepstate=true flag that you can pass to asadmin redeploy takes precedence over the XML descriptors. The same holds true for the corresponding checkbox of the Administration Console, because both it and asadmin are on par in terms of administration capabilities.

How Session Preservation Works

Under the hood, session storage is a very simple process based on Java object serialization between memory and persistent storage. When an application is being redeployed, its associated classloader is discarded and a new one is established for the new application code and resources. Sessions are restored from the session storage using deserialization. Because of this, changes to class definitions can lead to the inability to restore session. In such cases, and whenever a problem is encountered, no session data is restored at all. A warning is also emitted in the server logs when a restoration failed.

The choice of the session persistence type is restricted to file both for the Web and EJB containers; otherwise, session preservation will not be performed. This does not require further action, because file is the default value. However, this implies that session preservation is not possible when high availability and session failover is enabled, which is reasonable because session preservation is a development-oriented feature. Theoretically, you could attempt session preservation in production as well, but be advised that GlassFish does not run any validation test to support session preservation in production environments.

Finally, it should also be mentioned that more elaborate solutions exist for reducing the hassles of redeploying applications in case you need more than what the session preservation feature of GlassFish offers. A popular solution is JRebel from ZeroTurnaround, which hot-patches application bytecode and resources as they are modified. JRebel also features excellent integration with GlassFish servers and integrated development environments such as NetBeans. By contrast, GlassFish does not manipulate bytecode.

Servlet 3.0 Fragments

The Servlet 3.0 specification introduces fragments as a mean for increasing the modularity of Java EE Web applications. Fragments allow parts of a web.xml configuration file to be embedded with dependencies, such as frameworks and libraries. However, there is more to it. Web applications more often depend on a set of static assets: cascading style sheets (CSS), pictures, JavaScript libraries, and configuration files. Most developers are accustomed to placing those files within the structure of their WAR archives, but doing so is not optimal. Specifically, you might be working on several projects that share assets such as visual branding and client-side JavaScript code.

Whenever those assets need to be updated, you have to manually update them in each project.

Thanks to the Servlets 3.0 fragments, you can now package assets such as libraries within JAR files. This is very useful, since you can create a JAR file for the CSS theming, a JAR file with a JavaScript library such as jQuery, a JAR file with your own JavaScript libraries, and so on. Assembling them as part of your project is then made easier, especially when you are using a dependency management system, such as Apache Maven or Apache Ant plus Apache Ivy.

The structure of a fragment JAR file is very simple: All resources should be placed under META-INF/resources, for example:

jquery-1.6.4.jar/
└── META-INF
    └── resources
        └── jquery-1.6.4.min.js

Each fragment JAR file can then be embedded as part of a Web application by being placed under WEB-INF/lib, just like any other regular dependency. Every resource found under META-INF/resources for a JAR file of WEB-INF/lib will be made directly available from the root of the deployed application Web context.

Back to the previous example, jquery-1.6.4.min.js would be exposed at a URL like this:

http://server:port/webcontext/jquery-1.6.4.min.js

While this feature is very useful for better managing Web applications assemblies, it might also be used for other types of assets, to absorb variance between development and production profiles, or to target different customers with specific branding requirements.

In this last example, you can make visual branding JAR files on a per-customer basis, with customized CSS pictures and images. By using a build tool with dependencies management capabilities, such as Maven, the project can be split into a “core” module that has the application, several “branding” modules producing fragment JAR files, and per-customer “assembly” modules that each has a dependency on the “core” module and the required branding module. This method makes it easy to industrialize the production of artifacts while retaining a high modularity level.

Application-Scoped Resources

A key point in Java EE architectures is that application modules are easy to decouple from their resource configuration, management, and binding. Using Java Naming and Directory Interface (JDNI) registries, applications need to know only the declared name of each resource that is required. A typical example is accessing a JDBC connection pool with a JNDI name such as java:jdbc/MyDatasource.

This method completely decouples the application from knowing technical details, such as the physical host of the database, the JDBC driver being used, the database vendor, the database login credentials, or the connection pool size. This method has other benefits too, because developers can work on a lightweight stack that is easy to set up, while infrastructure experts do not have to repackage deployment artifacts. They only need to declare, configure, monitor, and fine-tune the required resources for Java EE applications to work. Having to extract the content of a WAR file, edit a configuration file, repackage the WAR file, and deploy it is never a pleasant experience.

There are, however, contexts in which having the resources declared and configured separately in an application server actually makes deploying applications more complicated than necessary. A good example faced by software vendors is the need to ship a version of their application as a standalone, self-contained bundle. Shipping a standalone version is useful when the target users have limited technical knowledge about how to run application servers and deploy applications to them, and it is also useful for distributing evaluation versions of a product. In either case, the application ships with the application server and provides a straightforward start script.

Another case that justifies the need to embed resource declarations with an application would be complex continuous deployment pipelines, where it is easier to simply deploy an artifact to an application server instance than deal with its configuration separately. This is the case of most cloud/PaaS (Platform as-a Service)/hosted environments, where server configurations must be generic and cannot be customized.

GlassFish provides support for declaring resources with a deployable artifact. They are called application-scoped resources, meaning they are visible only within the scope of an application. An interesting by-product of this is that other applications deployed to an application server cannot access them. If you declare, say, an application-scoped JDBC connection pool of 20 instances, you do not risk other applications consuming them and causing potential delays and resource starvations.

Using Application-Scoped Resources

We modified TaskEE to become TaskEEPA (see Figure 3). The application looks the same. Instead of storing task items in a Web session, we now store them in a relational database. This means that all users now share the same task list, but this is fine given that it was developed solely for the needs of this article.

Figure 3: TaskEEPA (or TaskEE) Gets Relational Database Persistence

A task is defined as a Java Persistence API (JPA)-mapped entity bean. See the code excerpt shown in Listing 1.

@Entity
public class Task implements Serializable {

    @Id
    @GeneratedValue
    private Long id;

    private String description;

    public Task(String description) {
        this.description = description;
    }

    public Task() {
    }
    
    // (...) boilerplate getters and setters

Listing 1. Defining a Task

Such entities are managed as part of a persistence unit. JPA requires us to define the configuration as part of the META-INF/persistence.xml. In our case, its content is the code shown in Listing 2:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns=" "
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="  
                 http://bit.ly/9uLTCp" version="2.0">
    <persistence-unit name="tasks-pu" transaction-type="JTA">
        <jta-data-source>java:app/jdbc/tasks-datasource</jta-data-source>
        <class>taskee.entities.Task</class>
        <properties>
            <property name="eclipselink.ddl-generation" value="create-tables"/>
            <property name="eclipselink.logging.level" value="FINEST"/>
        </properties>
    </persistence-unit>
</persistence>

Listing 2. Defining the Configuration

The content of this configuration file will not be surprising to the large majority of Java EE developers, who are already familiar with JPA. We have a single entity to be managed (taskee.entities.Task) and the JPA implementation that we use, called EclipseLink, is asked to create the necessary database tables unless they already exist.

The configuration references a datasource under the java:app/jdbc/tasks-datasource JNDI name. Note the app namespace in the JNDI path, which is used to denote an application-scoped resource. If the datasource had been declared in the traditional way outside of the application artifact, we would have likely named it java:jdbc/tasks-datasource.

Application-scoped resources are declared as part of a file called glassfish-resources.xml. In the case of a WAR deployment, the file is placed in WEB-INF/glassfish-resources. In our case, we declared the datasource for that file with the code shown in Listing 3:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN"
        "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
<resources>
    <jdbc-connection-pool
            max-pool-size="10"
            datasource-classname="org.apache.derby.jdbc.ClientDataSource"
            res-type="javax.sql.DataSource"
            name="java:app/jdbc/tasks-pool"
            pool-resize-quantity="10">
        <property name="user" value="APP"/>
        <property name="PortNumber" value="1527"/>
        <property name="password" value="APP"/>
        <property name="ServerName" value="localhost"/>
        <property name="databaseName" value="tasks-db"/>
        <property name="connectionAttributes" value=";create=true"/>
    </jdbc-connection-pool>
    <jdbc-resource
            pool-name="java:app/jdbc/tasks-pool"
            jndi-name="java:app/jdbc/tasks-datasource"/>
</resources>

Listing 3. Declaring the Datasource

The resources tag can contain different types of resource declaration. Here, we declared a JDBC connection pool to an Apache Derby server, and then bound it to the java:app/jdbc/tasks-datasource application-scoped JNDI resource name. Other types of candidate resources include external JNDI references, mail resources, connector resources, and resource adapters.

Application-scoped resources are created when the application is deployed. They are deleted when the application is undeployed or redeployed. Note that this does not necessarily imply that subsequent data is deleted too! In the case of TaskEEPA, the data put in the Apache Derby database that we referred to will remain intact. You can ask for resources to be preserved while performing a redeployment. To do that from asadmin, pass the --preserveAppScopedResources=true flag. A related option exists as part of the Administration Console.

@DataSource Annotation

A lesser-known option exists when it comes to declaring datasources. The application-scoped resources mechanism that we introduced is specific to GlassFish. The Java EE 6 specification features the @DataSource annotation, which can be applied to a type. You can pass JDBC parameters as annotation parameters as shown in Listing 4:

@DataSourceDefinition (
    name="java:app/env/UserDB",
    className="javax.sql.DataSource",
    portNumber=1527,
    serverName="localhost",
    databaseName="sample",
    user="APP",
    password="APP")
@Singleton
public class DataSources { }

Listing 4. Passing JDBC Parameters

When the DataSources singleton is loaded into the EJB container, an application-scoped datasource is being declared under the java:app/env/UserDB JNDI name. When multiple datasources need to be declared, you can declare several classes with each one annotated with @DataSource. Alternatively, you can use a single class annotated with @DataSources, an annotation that can embed several @DataSource annotations.

Application Versioning

Deployed applications are associated with a name that is used for performing various administrative tasks, including redeployment, undeployment, monitoring, and so on. By default, the name corresponds to the name of the artifact.

For example, when we deployed taskee-1.0-SNAPSHOT.war, the taskee-1.0-SNAPSHOT name was automatically defined for the Web application that we deployed. This name can be changed at deployment time by using the --name flag, for example:

$ asadmin deploy --name=taskee target/taskee-1.0-SNAPSHOT.war 
Application deployed with name taskee.
Command deploy executed successfully.

Starting with GlassFish 3.1, you can also tag an application name with a “version.” This functionality allows concurrent versions of an application to be deployed to an application server at the same time.

Only one version may be active at a time while the other ones remain deployed and configured. This requirement is useful for quickly switching between versions and rolling back to an earlier version if need be. It is also useful for reducing downtime while performing an upgrade, because it requires several tasks between the earlier version to be undeployed and the new version to be deployed and running. For example, a new version can be deployed to a cluster and switched to be the active version once all instances are ready to go. Meanwhile, the previous version remains active and continues to serve client requests.

Application versioning is done by suffixing the --name argument with a colon and a version tag. Suppose that we would like to deploy TaskEE as TaskEE version 1.0:

$ asadmin deploy --name=taskee:1.0 target/taskee-1.0-SNAPSHOT.war 
Application deployed with name taskee:1.0.
Command deploy executed successfully.

We could then decide to deploy TaskEEPA as Taskee:2.0 with the deployment command shown in Listing 5. (The warnings are perfectly fine because we already deployed it earlier and EclipseLink created the database tables for us.)

$ asadmin deploy --name=taskee:2.0 target/taskeepa-1.0-SNAPSHOT.war 

PER01003: Deployment encountered SQL Exceptions:
	PER01000: Got SQLException executing statement "CREATE TABLE TASK (ID BIGINT NOT NULL, 
DESCRIPTION VARCHAR(255), PRIMARY KEY (ID))": java.sql.SQLException: Table/View 'TASK' 
already exists in Schema 'APP'.
	PER01000: Got SQLException executing statement "CREATE TABLE SEQUENCE 
(SEQ_NAME VARCHAR(50) NOT NULL, SEQ_COUNT DECIMAL(15), PRIMARY KEY (SEQ_NAME))": 
java.sql.SQLException: Table/View 'SEQUENCE' already exists in Schema 'APP'.
	PER01000: Got SQLException executing statement "INSERT INTO 
SEQUENCE(SEQ_NAME, SEQ_COUNT) values ('SEQ_GEN', 0)": 
java.sql.SQLIntegrityConstraintViolationException: The statement was aborted because 
it would have caused a duplicate key value in a unique or primary key constraint or 
unique index identified by 'SQL110817213731180' defined on 'SEQUENCE'.
Command deploy completed with warnings.

Listing 5: Deploying TaskEEPA as Taskee 2.0

We can check all applications and versions and see that the active version is the last one deployed:

$ asadmin list-applications -l
NAME        TYPE   STATUS    
taskee:1.0    disabled   taskee:2.0    enabled    

The version tag must validate against the following regular expression:

[A-Z|a-z|0-9]+([_|.|-][A-Z|a-z|0-9]+)* 

This validation scheme gives a wide range of options for supporting common naming schemes. Valid version tags would be 1.0-SNAPSHOT, 1.5_GA, RC-10.2011_08_26, internal-3.0.1-beta3, and so on.

We can switch the active version back and forth using the enable and disable commands, as shown in Listing 6. Note that while there can be only one active version for an application, there can also be none.

$ asadmin enable taskee:1.0
Command enable executed successfully.
$ asadmin list-applications -l
NAME        TYPE   STATUS    
taskee:1.0  <web>  enabled   
taskee:2.0  <web>  disabled  
Command list-applications executed successfully.
$ asadmin disable taskee:1.0
Command disable executed successfully.
taskee:1.0  <web>  disabled  
taskee:2.0  <web>  disabled  
Command list-applications executed successfully.
$ asadmin enable taskee:2.0
Command enable executed successfully.
$ asadmin list-applications -l
NAME        TYPE   STATUS    
taskee:1.0  <web>  disabled  
taskee:2.0  <web>  enabled   

Listing 6: Enabling and Disabling the Active Version

An application version can be deployed as disabled. This is useful, say, for rolling an upgrade while a previous version is still running and later switching to the new version once it has been deployed. To do that, you need to pass the --enabled=false argument to the deploy command:

$ asadmin deploy --enabled=false  --name=taskee:2.0 taskeepa-1.0-SNAPSHOT.war

Names qualified with a version tag can be used in places where the --name parameter is expected, such as with the deploy, redeploy, undeploy, or show-component-status commands. We could undeploy a specific version as follows:

$ asadmin undeploy taskee:2.0
Command undeploy executed successfully.

Given that you are likely to have many versions deployed for a single application, you can introduce a wildcard (*) as part of the version. For example, --name=’1.0-*’ matches any version starting with 1.0-, such as 1.0-beta1and 1.0-beta2. You should take care and encode such versions into quotes to prevent shell expansion when using asadmin from a terminal.

We can effortlessly undeploy all versions of TaskEE using a wildcard, as shown in Listing 7:

$ asadmin list-applications -l
NAME        TYPE   STATUS    
taskee:1.0  <web>  disabled  
taskee:2.0  <web>  enabled   
Command list-applications executed successfully.
$ asadmin undeploy 'taskee:*'
Command undeploy executed successfully.
$ asadmin list-applications -l
Nothing to list.
Command list-applications executed successfully.

Listing 7: Undeploying All Versions of TaskEE

Application versioning was contributed by French engineering services company Serli, proving that GlassFish is a genuine open source project.

Conclusion

This article presented four features of GlassFish that add flexibility in relation to Java EE application deployment for developers and infrastructure engineers.

  • Session preservation can save substantial time while developing an application.

  • Servlets fragments make it easier to package static assets as libraries, hence facilitating the management of assemblies for a wide range of deployment profiles and targets.

  • Application-scoped resources make it possible to embed resource declarations within application artifacts, such as WAR archives. While this goes against the beauty of the Java EE model, where applications are decoupled from their resource declarations and configuration, embedding resources makes GlassFish a compelling choice when pragmatic concerns encourage such a tradeoff.

  • Finally, application versioning makes it possible to quickly switch between versions, roll back to a previous one, and minimize downtime while performing an upgrade.

These features complement each other as both public and private cloud computing platforms are being increasingly used to deploy applications. Cloud computing platforms require more deployment flexibility when connected to DevOps pipelines; version upgrades and rollbacks should be easy and transparent to end users, while server instances should be easy to provision and de-provision.

See Also

About the Author

Julien Ponge is a long-time open source craftsman. He created the IzPack installer framework and has participated in several other projects, including the GlassFish application server in cooperation with Sun Microsystems. Holding a Ph.D. in computer science from UNSW Sydney and UBP Clermont-Ferrand, he is currently an Associate Professor in Computer Science and Engineering at INSA de Lyon, focusing his research on a combined approach of programming languages, virtual machines, and middleware. Speaking both industrial and academic languages, he is highly motivated in further developing synergies between those worlds.