IBM Skip to main content
Search for:   within 
      Search help  
     IBM home  |  Products & services  |  Support & downloads   |  My account

developerWorks > Java technology | Scenarios | Web services
developerWorks
Deploying multiple applications in J2EE 1.2
146KBe-mail it!
Contents:
Once upon a time ...
Weighing the options
Where to go from here
Summary
Resources
About the authors
Rate this article
Related content:
Implementing co-deployed components in IBM WebSphere Application Server, Versions 4.0 and 5.0
J2EE Packaging and Common Code
J2EE Application Deployment: One or Many Applications per Application Server?
EJB best practices series
Subscriptions:
dW newsletters
dW Subscription
(CDs and downloads)
Planning for reuse in your Enterprise JavaBeans components

Level: Intermediate

Kyle Brown (mailto:brownkyl@us.ibm.com?cc=&subject=Deploying multiple applications in J2EE 1.2), Senior Technical Staff Member, IBM
Keys Botzum (mailto:botzumk333@yahoo.com?cc=&subject=Deploying multiple applications in J2EE 1.2), Senior Consulting I/T Specialist, IBM

1 January 2003

If you are developing with EJB technology, you are creating potentially reusable components. Unfortunately, plans to deal with reuse are often not put into place until it's too late. In this article, IBM enterprise developers Kyle Brown and Keys Botzum examine a common reuse scenario and explore some considerations that arise from it. They'll show you how to make the best choices for packaging and deploying your applications. They also offer details on an implementation using IBM WebSphere Application Server as an example.

There is a serious deployment problem surrounding the building of J2EE applications, which has unwittingly been propagated by many of the books and articles written on the subject. The problem is caused by the difference between the requirements of a project that will fit within the context of an introductory book and the requirements that projects have in the real world. In short, the issue is this: You never have just one project, and projects are never completely isolated from each other.

In the real world, you don't deploy a single project and rest on your laurels. Reality is far more complicated. You often deploy one project and then two months later deploy another project that relies on the services provided by the first project. Sometimes you deploy a project and then six months later deploy version 2 of that project while keeping version 1 running concurrently for a period of time.

If you are an Application Service Provider (ASP) it's even more complicated. In this case, you may actually deploy the same application several times for different customers, with only the entry point URLs and the details of the static bitmaps and Web pages differing between customers.

Once upon a time . . . (an application development fairy tale)
As an example of the kind of complexity that really occurs, consider this scenario. Say that the first J2EE application built by our company, Widgets Inc., is an employee timesheet application for the Human Resources department. As part of this application, we create a set of EJB components to handle problems in employee management. We have an EmployeeManagement session bean to retrieve the set of employees that report to a particular manager, and we also have the ability to retrieve information about a particular employee (name, department, date of hire, salary band, and so on) from the HR database.

But then along comes a new requirement. We're asked to build a new application that allows an employee to select various parts of his benefits package, allowing him to sign up for medical insurance, a savings plan, and so on. For this application, we need exactly the kind of business logic that's already in the previous application. For instance, to allow the employee to buy and sell vacation days, we need to know his years of service -- something the EmployeeManagement session bean can already retrieve for us.

So what do we do? Do we rewrite the logic a second time, or do we find a way to reuse the existing logic? Rewriting certainly doesn't seem like the right thing to do, so reuse seems to be the most efficient option. But how do we reuse the session bean and its associated classes? Let's examine a few options.

Option 1: Merged-component deployment
In this case, we would package our new Benefits application as a Web archive (WAR) file (and perhaps an EJB-JAR or two) within the existing Timesheet enterprise archive (EAR). Figure 1 shows what this would look like:

Figure 1. Merged-component deployment
Figure 1. Merged-component deployment

What we've done here is to place the two applications inside the same EAR so that the Benefits application can access the classes and EJB components in the Timesheet application. This approach is very easy to accomplish, and it solves our immediate problem. However, it has some drawbacks that make it unattractive as a long-term solution.

Let's consider the way the two applications are used. The Timesheet application is used regularly, every day, by every hourly employee in the company. That means that we can easily predict the peak load and determine the appropriate hardware capacity that the application should be deployed on. But the Benefits application is different. Every employee in the company (hourly or not) will use this application, but only once for each employee, and the use could occur at any time within the two-week benefits signup period. So the hardware capacity needed to run this application could be very different from the Timesheet application -- it might be significantly higher if everyone waits until the last moment and signs up on the last day of the enrollment period (although we're sure that would never happen).

What's more, the use patterns of the applications differ -- the Timesheet application is in use during regular business hours every day, leaving evenings free for server and application maintenance. However, the Benefits application should be available at night for employees to use over the company's Virtual Private Network (VPN) so that they can involve their families when making their benefits choices. So how do we reconcile the different requirements?

Also, what about availability? In our example, it may be acceptable if the Benefits application goes down for an hour or so, but not the Timesheet application -- that's required to be up 100 percent of the time during business hours.

Drawing from these requirements, we can conclude that the applications cannot be deployed within the same EAR file; they are related, but not closely enough to warrant co-deployment. In fact, evaluation of the previous considerations will lead you to reject merged-component deployment as a deployment solution in most cases, which brings us to shared-service deployment.

Option 2: Shared-service deployment
With this approach to deployment, we include the code necessary to connect to the EmployeeManagement session bean in the WAR file of the Benefits application and to separate the applications into their own EAR files. This option is illustrated in Figure 2:

Figure 2. Shared-service deployment
Figure 2. Shared-service deployment

This approach treats the EmployeeManagement bean as a shared service. Deploying the applications into separate EAR files lets us deploy them into different application server JVM instances, which in turn allows us to make different decisions as to how much hardware to allocate to each application. What's more, this approach seems to help the maintenance problem -- we can now bring down the Benefits application without affecting the Timesheet application at all.

However, we haven't fully solved the problem. We now have a dependency between the two applications. While we can bring down the Benefits application, the Timesheet application must now have the same availability requirements as the Benefits application, creating a potential administration hassle. Furthermore, we now have to deal with a problem that's not addressed by the J2EE 1.2 specification: how to include the EJB stub code in the Benefits WAR file. Theoretically, we could separate out the client code from the Timesheet EJB-JAR file, but that seems error prone and likely to create problems. We'll come back to this possibility later.

Another possibility is to locate the client code outside of the EAR file on a shared classpath, but this approach not only violates the spirit of the J2EE specification, it can wreak havoc in our applications and the application server itself if we choose the wrong classpath. In short, this solution creates many more problems than it solves.

Shared-service deployment can degrade performance when compared to the merged-component solution. If we choose to separate the two applications by placing them into different JVMs, we require a cross-process call that could have been optimized out by the container in the solution where both applications were located in the same JVM. In short, calling an EJB component within the JVM it is deployed in is several times faster than calling it from outside that JVM.

However, there is a more insidious problem that this solution creates, which was also present in the previous solution: version drift. Imagine that in the initial version of the EmployeeManagement session bean we had a method with the following signature:


EmployeeVO getEmployeeDetails(int employeeID)

This method signature assumes that employeeIDs are integers, which works fine if they really are. However, let's assume that they are further limited by the size of the integer -- that some other back-end database requirement limits the size of the employee ID to six digits. Suppose this works fine for the initial version of both applications, but then at some point we run out of six-digit integers. As a workaround, the HR department redefines the employee ID rules to include alphabetic characters as well, leading to the following changed signature:


EmployeeVO getEmployeeDetails(String employeeID)

Because the Timesheet team is the one developing this EJB component, they plan to update their application to use the new employee ID signature. But what about the Benefits application? Suppose the Benefits application only rolls out a new version once a year right before the benefits signup period begins (because we can only change benefits during the signup period, but we can view our benefits at any time). If the benefits team isn't ready to come out with a new version of their application, then this change to the Timesheet application will break the Benefits application.

So how do we avoid the API version drift problem? To find the solution, we need to think "outside the box" a bit. While it may appear that the J2EE specification has locked us into a series of bad solutions, it, in fact, contains some hidden wisdom that allows us to solve this problem quite elegantly. The key to solving our problem is to conclude that the pieces of logical projects should not cross EAR files if at all possible. Let's take a look.

Option 3: Independent deployment
In this third option, the EARs are completely self-contained. Shared EJB components are handled by deploying the same EJB class multiple times (once per EAR) with different global Java Naming Directory Interface (JNDI) names -- keeping the local JNDI names the same in the web.xml and ejb-jar.xml files, but changing the runtime bindings between the local and global names when we deploy the EAR file, as shown in Figure 3:

Figure 3. Independent deployment
Figure 3. Independent deployment

To understand how this deployment option works, let's take a look at the way it's implemented in IBM WebSphere Application Server, Version 4.0. Application Server has a single "global" JNDI namespace that is managed at the domain level. The JNDI naming service in Application Server, Version 4.0 runs on the Admin server, so while we have one JNDI service per node, they all share the same global namespace, which is held in the common administrative database. Although the implementation is different, Application Server 5.0 also has a global and a local namespace so the principles are the same in the new version. However, there are two parts to this: local references (for instance, java:comp/env entries) are unique to a specific application because they are specified in the web.xml and ejb-jar.xml files. That means our code can refer to these names and be assured that it will not have to change if the configuration changes underneath them. The second part, then, is that these local names are bound (at deployment time) to names in the shared, global JNDI namespace. So we could have the case shown in Table 1:

Table 1. Local names bound to names in the global JNDI namespace
Web Application Local JNDI Reference Global JNDI Reference
Benefits java:comp/env/ejb/EmployeeManagementHome benefits/com/ibm/ejbs/ EmployeeManagementHome
Timesheet java:comp/env/ejb/EmployeeManagementHome timesheet/com/ibm/ejbs/ EmployeeManagementHome (a different global reference)

This approach solves a number of problems that occur when trying to split applications across EARs -- most notably the "API version drift" problem that occurs when we have two applications that each depend on a slightly different version of the shared EJB component. The argument that we often hear against this solution is that it "results in too many EJBs" and that it's wasteful of memory. That's simply not a valid argument. We can tune the cache sizes of our EJB servers and containers to be even more effective at memory management in this scheme than we can in the shared scheme. Also, many people not familiar with EJB technology (entity beans especially) think that this solution won't work because there is something "magical" about the way entity beans operate -- that somehow there's only ever one entity bean object for any piece of data, and that deploying beans in multiple ways like this will wreck our data management. That is also false, because the same problems of row locking and isolation level apply when we have multiple clones of an application in a clustered environment like we have in this solution. Concurrency is managed in entity beans at the database level, unless we're using EJB commit option A, as defined in section 10.5.9 of the EJB 2.0 specification (see Resources), which generally does not work in a clustered environment. In any case, choosing this approach will not affect the consistency or the access speed of our data in the least.

However, there is another potential problem that can still create issues with this solution. In solving the "API version drift" problem, we've left ourselves open to a more subtle "data/external drift" problem. Some applications now use older versions of the EJB code than other applications. It is quite possible to make changes to a database schema as part of implementing the newer version of the code that might break the old code. Unless we want to keep two copies of our production data (a data management nightmare), we must be very careful about implementing database schema changes.

Also, there is an even more subtle issue regarding changes to the business logic. Often a change to an external interface represents a change in the semantics of the way an API works. However, sometimes semantic changes occur without a change to the external API -- for example, a critical bug might be fixed, or a complex piece of business logic might be refactored for better performance. The major issue in this case is that if we choose independent deployment, we can deal with changes of this sort only by updating all the other EAR files. Thus, shared-service deployment is actually preferable in some cases where this may happen.

Weighing the options
In light of our little development fairy tale, independent deployment may seem to be the right option for every application development scenario. In fact, it's not. In our scenario, we simplified several considerations that may lead you toward shared-service deployment instead of independent deployment in a number of different situations. While you should carefully consider independent deployment in many deployment scenarios, it's not right for every case. In particular, you should choose shared-service deployment if any of the following conditions apply:

  1. If your logical application is quite complex (multiple JAR files), then it might be difficult to include all of it in every dependent application. However, the fact that your application is packaged in multiple JAR files should sound a warning in your mind in any case. If you find yourself having to include several JAR files to represent a single logical application, it might be better to make a logical app into one large JAR file. In J2EE there are some rules of thumb for the lower bound of granularity of EJB-JAR files in particular (like how many EJB components should be placed together in a single EJB-JAR file). For instance, only beans in the same EJB-JAR file can reference each other for any of the IBM EJB extensions (like entity bean inheritance), or for any of the EJB 2.0 extensions (relationships and so on). However, the upper bound is not quite as clear -- it is not obvious how many beans are too many in a single EJB-JAR. The level of granularity is most often set by development priorities rather than by deployment considerations (for instance, you may have a different EJB-JAR for each developer or for each development group).

    If your application has particular deployment constraints, then independent deployment might prove difficult or expensive to implement. For example:

    • Your application might use JMS with WebSphere MQ or JCA with the CICS Transaction Gateway (CTG), and you don't want to have to duplicate the installation of those products in every application server. For instance, you may not want to install several copies of the WebSphere MQ server to minimize the overall software cost of your production environment.

    • Your application might have complex management or state information. Perhaps it creates multiple threads that run to process work in a complex way. Integrating such an application with another can be difficult.

    • Your application might have strict security concerns and perhaps contains information that needs to be carefully protected (such as passwords). Such information should be in as few places as possible. Thus, copying the application's contents to another application may not be wise.

    • Your application data might also need to be carefully protected, making it inappropriate to give additional applications direct access to your data.
  2. If your application's basic external interface is well defined and stable, but the internals of that implementation are likely to change, then you should be careful about implementing independent deployment. Examples of changing internals might include changing database schemas, changing business logic, or even changing the implementation infrastructure (new databases, new third party products, and so on). In such an environment, it's best to separate the system layers so that the second application can be changed in radical ways without affecting the first application. As long as you take the time up front to design stable, well-designed session bean interfaces, shared-service deployment may be advantageous in this case.

  3. If your deployment environment involves a large number of geographically distributed data centers, independent deployment may not be efficient. For example, if the data for Application 1 is in Data Center 1, while the data for Application 2 is in Data Center 2, there is no good place to put a combined Application 1 and 2. Thus, it would be better to place Application 2 in Data Center 2 and configure it to make remote EJB component calls to Application 1.

In general, the more complex the application is the more difficult it is to implement independent deployment. Complicated applications often have complex management, installation, and initialization procedures (many configuration files, Application Server configuration constraints, and so on). In this case, combining them with other applications places a great burden on the client application. It's easier simply to provide the client application with the appropriate client EJB-JARs. Then the client only has to deal with the client interfaces and not the remainder of the client application. Of course, for simple applications it is far easier to combine them than to deal with the complexities of a distributed environment. Many organizations never even consider independent deployment, even when it could save time and effort.

If you must implement shared-service deployment, how do you make it work, considering that the existing J2EE tooling does not directly support that option? In fact, you have to work around the tooling. One option is to develop your own custom tooling capable of "stripping" a standard EJB-JAR file and packaging a new client JAR file. At a minimum, removing the ejb-jar.xml file from an EJB-JAR file will render it into a client JAR file, since the development tooling will not attempt to deploy the EJB components inside the JAR without the information held in the ejb-jar.xml file. However, in most cases you will want to strip out additional pieces of the JAR file to make it as light as possible (including, for instance, the EJB implementation classes and the generated skeleton and persistence classes).

Finally, another variation of shared-service deployment that we need to consider is the possibility of completely splitting out the shared components (in our case the EmployeeManagement bean) into its own EAR file, totally separate from any other application-specific code. This is probably the best long-term solution for applications where shared-service deployment is the best option. However, this presents its own set of concerns: Once the components are separated from the application code, who now owns them and is responsible for maintaining them? If a special reuse group has been set up to maintain the shared distributed components, then this can work well, but if not, then this option can result in a morass of finger-pointing and declarations of "not my job" when things go wrong with the shared component.

WebSphere Application Server example

If independent deployment is appropriate for your application and you are also developing in IBM WebSphere Application Server, the sidebar "Implementing co-deployed components in IBM WebSphere Application Server, Versions 4.0 and 5.0" explains how to achieve independent deployment through careful design of your application code, and demonstrates how this configuration operates in WebSphere Application Server.

Where to go from here
So how do you proceed? Here are some summaries of the issues involved:

  • An EAR is a deployment contract. Each EAR should represent a complete application and should not rely on code external to the EAR file, except what is provided by the application server itself.

  • Different logical applications should go in separate EAR files. When there is a dependency between one J2EE component and another, you must carefully design for and consider availability. Ideally, loosely coupled applications should be designed such that each application does not depend on another application being available at all times. This is best achieved using techniques such as asynchronous communication using JMS. However, when required by business needs, synchronous communication is acceptable. Keep in mind, though, that this requirement places heavy burdens on the related applications. In the extreme, if dozens of applications depended on the complete availability of each other, it is unlikely that the system would ever work.

  • Each version of an EAR contains the assembly of the J2EE components comprising that version of the logical application. In the example discussed in this article, the Benefits application should consist of the Benefits WAR file and a Timesheet EJB-JAR file (along with any other J2EE components that are necessary).

  • Each J2EE component (WAR or EJB-JAR) should be separately versioned and maintained in a Source Control Management (SCM) system. This allows you to build your EAR files by configuring together the proper versions of the proper WAR and EJB-JAR files. If the Benefits application version 2.0 needs the Timesheet JAR file version 1.0, while the Timesheet application version 2.0 needs the TimeSheet JAR file version 1.1, you can quickly identify and resolve this issue.

In fact, this last point deserves a little more clarification and some additional hints. One of the practices that we encourage is that J2EE components (EARs, WARs, and JARs) contain additional, human-readable metadata that indicates the versions of the various subcomponents that comprise it. So, for instance, an EJB-JAR file might contain a special XML file (not used by the application server, but meant to be read by humans and possibly generated by an SCM tool or script) that lists the version numbers of the different EJB components it contains. If an EJB-JAR or WAR file depends on other versions of other J2EE components, it should include that information in its XML metadata file as well. Likewise, an EAR file should contain the list of versions of the WAR and EJB-JAR files that it contains. That way, version mismatch problems can be caught automatically when the EAR is built. If a mismatch is detected, the build can abort before it is deployed.

Summary
Packaging and deploying related J2EE applications is a complex issue. No single answer will work for every environment or application. In this article we've provided some simple recommendations for deploying EAR files to avoid the problems of version drift, incompatible hardware requirements, and incompatible availability requirements among related applications. We've quickly eliminated an approach we see all too often, and then introduced two of the more useful approaches (shared-service deployment and independent deployment). We've also shown the different factors that you have to weigh in choosing the right deployment option for your particular problem.

While we would recommend that you adopt the simplest solution (independent deployment) where it applies, we have also given you the tools necessary to understand when it does not apply and instead requires a more involved approach. This should help you determine how to best configure your own application servers and how to avoid some of the more painful problems that improper deployment can cause.

Resources

About the authors
Kyle Brown is a Senior Technical Staff Member with IBM Software Services for WebSphere. Kyle provides consulting services, education, and mentoring on object-oriented topics and Java 2 Enterprise Edition (J2EE) technologies to Fortune 500 clients. He is co-author of Enterprise Java Programming with IBM WebSphere, WebSphere 4.0 AEs Workbook for Enterprise JavaBeans (3rd Edition), and The Design Patterns Smalltalk Companion. He is also a frequent conference speaker on Enterprise Java, OO design, and design patterns. You can reach him at mailto:brownkyl@us.ibm.com?subject=Deploying multiple applications in J2EE 1.2.


Keys Botzum is a senior consultant with IBM Software Services for WebSphere. Keys has over 10 years of experience in large scale distributed system design and specializes in security. He has worked with a variety of distributed technologies, including Sun RPC, DCE, CORBA, AFS, and DFS. Recently, he has been focusing on J2EE and related technologies. He holds a Master's degree in Computer Science from Stanford University and a B.S. in Applied Mathematics/Computer Science from Carnegie Mellon University. You can reach Keys at mailto:botzumk333@yahoo.com?subject=Deploying multiple applications in J2EE 1.2
The authors would like to thank Tom Alcott, Bill Hines, Randy Stafford, Martin Fowler, Mark Hapner, Bobby Woolf, and Richard Monson-Haefel for their penetrating analysis and helpful comments on the content of this article.



146KBe-mail it!

What do you think of this document?
Killer! (5) Good stuff (4) So-so; not bad (3) Needs work (2) Lame! (1)

Comments?



developerWorks > Java technology | Scenarios | Web services
developerWorks
  About IBM  |  Privacy  |  Terms of use  |  Contact