Adding Some Agility to Java EE Application Deployment with GlassFish
by Julien Ponge
Published February 2012
Four noteworthy features in GlassFish add agility to Java EE application deployment.
Downloads:
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).
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
- GlassFish.org Website
- GlassFish videos on YouTube
- JSR 315: Java Servlet 3.0 Specification
- Application versioning design document
- Serli, the company that contributed application versioning
- EclipseLink, the JPA implementation provided by default with GlassFish
- JRebel from ZeroTurnaround
- Real Word Java EE Night Hacks, Dissecting the Business Tier by Adam Bien (April 2011)
- Beginning Java EE 6 Platform with GlassFish 3 by Antonio Goncalves (August 2010, second edition)
- The Java EE 6 Tutorial: Basic Conceptsby Eric Jendrock, Ian Evans, Devika Gollapudi, Kim Haase, and Chinmayee Srivathsa (September 2010, fourth edition)
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.