Enterprise applications are composed of numerous parts. It requires much effort to assemble Web servers, application servers, databases, queue managers, along with other parts, and have them all work together. Application code running on an application server is just one piece of the puzzle. The application code, however, forms the backbone of the application; it contains the business logic that defines your application. It is likely that your application code was the component that took the largest amount of effort to assemble. It is a natural place to start when effecting a migration.
Other aspects of your application -- other running parts -- are equally important. As part of any migration effort, you should carefully consider what it takes to migrate these components. Part 1 in this Migrating to IBM WebSphere® Application Server series discussed techniques for preparing applications for a migration effort. Part 2 presented a general migration plan. This article, Part 3, takes a more pragmatic approach and presents a list of things to look for in application code when assessing a migration effort. This list is not complete, but forms a foundation for assessing the amount of effort you will spend to effect a migration.
An application written in full conformance of the JavaTM 2 Platform, Enterprise Edition (J2EE) 1.2 specification should migrate to WebSphere Application Server with relative ease. Much of the work will be concerned with generating WebSphere-specific deployment information. Such is the goal of J2EE: application developers build applications to the specification and deployers address the idiosyncrasies of the particular application server declaratively with deployment descriptors. However, applications not written in conformance with J2EE 1.2, or applications that are based on previous versions (and previous versions of composed specifications), are generally not as easy to migrate.
Today, most application development efforts start with an intent to conform to the latest version of the specification. But as development progresses and the specification continues to evolve, often, the application code winds up somewhere in the middle: not quite fully J2EE compliant.
"Not fully J2EE compliant" is a dangerous place to be in a migration sense. Each application server is different, implementing different interpretations of the various specifications that make up J2EE. Where the implementation of one application server might permit a subtle violation of the specification, another might be more strict. Some application servers provide multiple versions of some specification. WebSphere Application Server 3.5.x, for example, provides support for three different versions of the JSPTM specification (Version 4.0.x only supports JSP 1.1). In short, the closer to J2EE conformance your application is, the easier the migration process will be.
Every application is different. Before any migration effort is attempted, it is important to assess the scope of the effort. You need to know how much work will be required. For application code, this is a process of first identifying what kinds of things need to be changed, then quantifying those things. From that information, the estimation process begins.
As part of the assessment process, it helps to actually do part of the migration. By doing so, you gain greater insight into what really needs to be done, and what kind of effort will be required to make it happen.
An easy way to identify and quantify the issues that will affect migrations is to walk through a small number of "vertical slices" of the application. A vertical slice, in its simplest form, is a single path of execution through your application -- a top-to-bottom flow -- roughly analogous to a use case. If you've maintained documentation for your application, the use cases are a great place to start (even if your application is not well-documented, it should be possible to derive one or more use cases from it). Pick one or two vertical slices and follow them through the code. Those that make the most use out of the greatest number of technologies are the best.
Work through the vertical slices with one or more developers who are knowledgeable of the application architecture and code, and one or two developers who are familiar with WebSphere Application Server 4.0 and J2EE 1.2. A large comfortable room with a single workstation projected on the wall works well for this task. As you work through the code, identify potential issues as they arise.
Here are some specific things to look for when assessing the application code:
- Layered architecture
- Code bulk
- Corporate framework use
- Proprietary technology use
- JSP level
- Servlet level
- Enterprise JavaBeansTM conformance
- Remediation opportunities
This list is not complete; it can never be fully complete. Every application is different and with each different application, the issues are different. It is important to be extremely vigilant as you step through the code to identify other problems.
Each of these potential migration issues is discussed here in greater detail.
For applications that conform to the J2EE specification, concerns about layering should have relatively little impact on the migration process itself. In general, however, a layered architecture is easier to migrate. If the application is layered, there is potential to migrate and test individual layers independently. This allows the actual migration process to be affected in steps, thereby reducing the overall complexity. This is of enormous value for large applications.
When migrating from a non-J2EE compatible application server, application layering takes on much greater significance. Often, when migrating from one of the so-called first generation application servers like NetDynamics or ATG Dynamo, very little of the view and controller (the "V" and "C" in "MVC") code can be reused. If the business logic is tightly coupled to the view and control logic, there is a danger that not much of the original application code can be reused. In this case, the migration effort can quickly turn into an effort of reimplementing the application in the new technologies.
Fixing an application architecture is generally out of scope for a migration effort. Still, it is worth documenting what you find in this regard as part of the assessment for later consideration.
Note: Often, a significant amount of time lapses between when the migration assessment occurs and when the migration actually starts. You might consider using this time to refactor your application code. Refactoring is a behavior-preservering process of improving code structure and quality. The improved quality of the code will make the migration process and future modifications easier.
Before starting the migration effort, you should remove extra classes and methods that are not actually used by the application. By reducing the code to the smallest size possible, the overall complexity of the task is reduced: fewer code artifacts have to be understood, migrated, and tested. While the pruning of unnecessary code can be effected during migration, it is recommended that such pruning be executed before starting the migration effort.
Corporate frameworks take many forms. They can be as simple as a single super class for all servlets, or they can be very complex, providing a large number of services. A framework can be something developed for a single application, or can be part of a corporate strategy and is maintained independently. In any case, the framework will have to be migrated along with the application.
Frameworks break the "vertical slice" model. The breadth of services that the framework provides should be fully reviewed. If your framework was developed by a particular group within your company (or by some third-party), then you are dependent on that group to migrate, validate, and certify the framework for use on WebSphere Application Server 4.0. As part of your migration process, you must communicate your requirements with that group, and adjust your schedule according to theirs.
Use of technologies not included as part of J2EE is often the largest source of work in a migration effort. If careful planning is not done in the earliest stages of development, it is likely that very little can be salvaged as part of the migration (see the "Layered architectures" section above).
Even J2EE application servers provide some kind of proprietary service that can make the migration effort more time-consuming. Consider things such as extended deployment descriptors for EJBs. In most cases, WebSphere Application Server will have similar functionality. For example, BEA WebLogic Server T3StartUp classes must be replaced with WebSphere CustomServices (or another mechanism).
Proprietary technology comes in many forms. Third-party products like Cryptix and TopLink are certified to be 100% Java and should work unchanged on WebSphere Application Server 4.0. Nonetheless, the viability of these products should still be confirmed in the context of your application as part of the migration assessment (there is a specific version of TopLink for WebSphere Application Server, for example, that is compatible with the TopLink for BEA WebLogic Server). Other third-party products may depend on specific features of a particular application server. If this is the case, then your migration schedule will depend heavily on that third party delivering equivalent functionality for WebSphere Application Server.
WebSphere Application Server 3.5.x supports three levels of JSP: 0.92, 1.0, and 1.1. WebSphere Application Server 4.0 supports JSP 1.1 only. Scriptlets and expressions have remained basically unchanged throughout the "JSP life." However, almost every other part of the specification has changed, at least in some subtle way. Most often, page directives and bean-centric JSP code must be updated.
The amount of work required to migrate JSPs depends largely on how they are built. If all of the URLs specified in your JSPs must be updated, then the migration process may be very time-consuming. Of course, this effort will be further complicated if the JSPs contain a lot of Java code.
For applications built using previous versions of the JSP specification, there is a tendency to decide to update the application to make use of the newer services, such as taglibs, provided by Version 1.1. It is strongly recommended that, as part of the migration effort, you make very few changes, if any at all, to the JSPs. Newer features should be considered as part of a later phase of the development process after the application has been migrated and has been proven to run on WebSphere Application Server 4.0.
The servlet specification has remained relatively stable for some time. Recent changes have seen some new APIs included and others deprecated. Perhaps the most significant change to the servlet specification has been with the way that HttpSessions are managed. With earlier versions of the specification, multiple applications running on the same application server shared the same session data. This is not true with Version 2 of the specification. Now, every application has its own session data. The implication of this is that if you have more than one application that share data through the HttpSession, significant changes may be required.
Enterprise JavaBeans conformance
Most of the time spent migrating EJBs will be in rebuilding
and tuning deployment descriptors. The standard ejb-jar.xml deployment
descriptor required by EJB 1.1 is generally compatible across all application
servers. However, almost without exception, all applications use extended deployment
information which must be rebuilt for WebSphere Application Server. Extended
deployment descriptors contain such things as the JNDI binding name and field
mappings for entity beans built using container-managed persistence.
Assume that you will need at least a couple of hours for each enterprise bean in your application. Discussed below are some examples of where you might spend some of that time.
Violations of the EJB 1.1 specification
It is difficult to enumerate all of the possible ways that the EJB specification can be violated. Knowledge of the EJB 1.1 specification is key to identifying problems.
Read-only methods in entity beans
Earlier versions of BEA WebLogic Server provide read-only
methods for entity beans by including an isModified()
method and corresponding modified boolean field
in the bean class. Methods that modify the state of the bean set the value of
the modified field to true. The container uses
the isModified() method to determine whether
the bean's state must be persisted when the transaction completes. Beans that
implement the isModified() method will work
without modification in WebSphere Application Server (WebSphere will simply
ignore the method).
WebSphere Application Server provides the ability to specify the access intent of a method declaratively. Using the access intent setting, the container automatically keeps track of whether the state must be persisted. The access intent is specified in the extended deployment information. To realize the performance benefits associated with this feature, the value must be set using either WebSphere Studio Application Developer or the Application Assembly Tool.
Bean classes that implement their own remote interface
The EJB specification does not explicitly forbid a bean class implementing its corresponding remote interface. The specification does, however, strongly recommend against doing so. Often, the intention is to permit instances of the bean class to be used interchangeably with the remote enterprise bean. One big problem with this is the potential it has to let clients gain direct access to the bean instance inside the container; this can have some rather odd effects on the running application.
Note: If the bean instances and the remote enterprise bean are being used interchangeably, then this is a good indicator that perhaps EJBs may not be the best solution for the problem. Applications that do this typically do so for reasons of performance and often do not make effective use of EJBs.
From a migration point of view, applications containing beans that must implement their own remote interface can result in a lot of work for you. The reasons for doing this must be understood and any code that permits clients to gain direct access to instances managed by the container must be rearchitected.
The fact that these "bean" subclasses implement the bean's
remote interface can lead to another RMI-related issue. All bean classes must
implement either javax.ejb.SessionBean or javax.ejb.EntityBean.
By extension, these bean classes also implement java.io.Serializable
which is a marker interface that indicates that the instances are passed by
value (copied). If the bean class also implements its remote interface, it will,
by extension, implement java.rmi.Remote. This
interface is another marker interface that is used to indicate that instances
are passed by reference. These local bean classes, by virtue of implementing
the bean's remote interface, implement both java.io.Serializable
and java.rmi.Remote which means that the instances
are passed both by value and by reference.
If an attempt is made to pass one of the "local" beans across
a distributed environment (either as a return type or method parameter), then
RMI will try to pass the object by reference (even if you really want it to
be passed by value). If the instances are created directly and are not registered
for remote use (using java.rmi.UnicastRemoteObject.export()),
this will result in an ObjectNotFoundException.
Understanding EJB 1.1 and RMI-IIOP is key to working out these sorts of issues.
Use of entity beans with container managed persistence
Entity beans can be one of the more time-consuming elements to migrate. While EJB 1.1 does go a long way toward standardizing many aspects of EJBs, it does stop short of providing any specific guidance for entity bean persistence mapping (and other aspects). Persistence mappings will have to be rebuilt. This can be a time-consuming process. If field mappings are complex, then the process can be even more time-consuming.
In addition to field mappings, finder queries are not handled
in a standard way. With WebSphere Application Server 3.5.x, queries are specified
as select statement fragments on a FinderHelper
class (full select statements are required with Version 3.0.x). In older versions
of BEA WebLogic Server, they are specified in a deployment descriptor in a prefix
notation format (WebLogic Query Language). Other application servers specify
these queries in other ways.
In any case, these queries will need to be migrated. In WebSphere Application Server 4.0, there are a number of options available. All of these options require that the query be included in a deployment descriptor. The queries can be specified as full or partial select statements. Perhaps, more interestingly, they can be specified in EJB-QL, the query language that is required as part of EJB 2.0. It is recommended that if you have to rebuild the queries, you should adopt EJB-QL.
Use of EJB communication protocols other than RMI/IIOP
WebSphere Application Server 4.0 uses RMI/IIOP to communicate with enterprise beans as required by EJB 1.1. From the application developer's point of view, communication protocols like BEA WebLogic Server's "T3" are similar to RMI/IIOP (although they are very different beneath the covers). For many migration efforts, the migration issues are mostly mechanical; the way you find the beans has to change.
Older application servers (and older applications written
to run on them) may make use of classic RMI/JRMP. In classic RMI, beans are
found by clients using the java.rmi.Naming class:
...
Object object = java.rmi.Naming.lookup("rmi://server:1099/MyBeanHome");
MyBeanHome home = (MyBeanHome)object;
... |
Client code of this form will have to change. With RMI/IIOP, lookups are done using the Java Naming and Directory Interface (JNDI) as shown below:
...
Hashtable properties = new Hashtable();
properties.put(Context.INITIAL_CONTEXT_FACTORY,
"com.ibm.websphere.naming.WsnInitialContextFactory");
properties.put(Context.PROVIDER_URL, "iiop://server:900");
InitialContext context = new InitialContext(properties);
Object object = context.lookup("MyBeanHome");
context.close();
MyBeanHome home =
(MyBeanHome)PortableRemoteObject.narrow(object, MyBeanHome.class);
... |
The parameterized constructor for InitialContext
is shown here. With this version of the constructor, the protocol and location
of the server is explicitly provided: iiop://server:900. The other information
provided in the Hashtable is the name of class that provides an implementation
of the "guts" of the initial context (com.ibm.websphere.naming.WsnInitialContextFactory).
These guts are specific to the particular application server and protocol. If
WebLogic's T3 were in use, for example, this value would be weblogic.jndi.WLInitialContextFactory.
The zero-argument constructor for InitialContext
is preferred. When this constructor is used, the values are taken from system
properties that are set up by the application server:
...
InitialContext context = new InitialContext();
Object object = context.lookup("MyBeanHome");
context.close();
MyBeanHome home =
(MyBeanHome)PortableRemoteObject.narrow(object, MyBeanHome.class);
... |
From a migration point of view, the zero-argument constructor has great appeal. Not only is the code generally easier to read and understand, but it is also now far more resilient to future change.
As part of the migration effort, deployment code for the bean will have to be regenerated (WebSphere Application Server's tools will do this automatically). Aside from the generation of deployment code, little change is required to upgrade the code (the "MyBean" Enterprise bean) specifically for compatibility with RMI/IIOP. Any lookup code in your application must be changed to make use of RMI/IIOP and the standard zero-argument InitialContext constructor if possible.
Additional changes may be required. Older RMI/JRMP solutions and solutions that use technologies like WebLogic's T3 tend to be more forgiving than RMI/IIOP. If an application does appear to make use of something other than RMI/IIOP, then further scrutiny of remote methods may be necessary. In particular, method parameter and return value types should be reviewed to confirm that they are legal values for RMI/IIOP. The RMI compiler (RMIC) itself can be quite good at rooting out violations of the RMI/IIOP specification.
Type narrowing is mentioned in several locations in the EJB 1.1 specification. All remote objects, according to the specification, must be narrowed when they are received. This includes bean homes retrieved through JNDI as well as any remote object that is obtained through a create or find on a home, or as a return value from any business method. Many applications are very lax in this regard and should be fixed.
In particular, code of the following form may work on some application servers:
...
InitialContext context = new InitialContext();
MyBeanHome home = (MyBeanHome)context.lookup("MyBeanHome");
context.close();
MyBean bean = home.create();
... |
Strictly speaking, this code violates the EJB 1.1 specification (although the specification does use "soft" wording to indicate that you should do this to remain compatible with other application servers). Code of this form should be changed to a form similar to:
...
InitialContext context = new InitialContext();
Object object = context.lookup("MyBeanHome");
context.close();
MyBeanHome home =
(MyBeanHome)PortableRemoteObject.narrow(object, MyBeanHome.class);
MyBean bean =
(MyBean)PortableRemoteObject.narrow(home.create(), MyBean.class);
... |
Almost without exception, every migration effort requires
some amount of remediation to the application. In particular, JDBC resources
are typically not closed correctly in most applications. According to the JDBC
specification, Statements, PreparedStatements
and Connections must all be explicitly closed.
To be completely safe, statements should be closed in a finally block of an exception handler, as shown below:
Connection connection = null;
Statement statement = null;
try {
// JDBC code
} catch (SQLException e) {
// handle the SQLException
} finally {
try {
if (statement != null) statement.close();
} catch (SQLException ne) {
}
try {
if (connection != null) connection.close();
} catch (SQLException ne) {
}
} |
Within the finally block,
the statement must be closed within another exception handler block to ensure
that the connection also gets a chance to close;
if an exception were to occur while attempting to close the statement
without exception handling individually around the statement
close and the connection close, then the connection
would not be closed.
While this is not a migration issue, it is an issue that can have significant effects on your application. As part of the migration effort, problems of this nature should at least be noted and considered for remediation either before or after the migration process.
There is no one tool that takes care of every migration issue imaginable. At least not yet. There are, however, a few tools that provide much help during the migration process.
VisualAge for Java 4.0 EJB 1.1 export tool
WebSphere Application Server 3.5.x supports Version 1.0 of
the EJB specifications and makes use of serialized deployment descriptors which
are completely incompatible with EJB 1.1. VisualAge® for Java 4.0 ships with
an EJB 1.1 export tool. This tool generates the deployment artifacts required
for WebSphere Application Server 4.0, including the ejb-jar.xml
extended deployment descriptors, and other CMP entity bean artifacts such as
the "schemaxmi" and "mapxmi" files required for field mappings.
The tool does not, however, modify the source code.
Applications built using VisualAge for Java 3.5 should import into Version 4.0 without modification. If your developers are currently using Version 3.5, they do not all need to be upgraded to 4.0. For most applications, it is sufficient to have one or two development machines configured to use Version 4.0, exclusively for use during the migration process.
Once the code is exported using the export tool, it can be imported into WebSphere Studio Application Developer.
This is a handy little tool that converts deployment descriptors from either BEA WebLogic Server or Sun's J2EE reference implementation to WebSphere Application Server 4.0 deployment descriptors. However, these tools only take you part of the way.
For EJBs, these tools will generate WebSphere Application
Server-compatible ejb-jar.xml and ibm-ejb-jar.xmi
files. The first file is the standard deployment descriptor that is required
as part of EJB 1.1. The second file is the extended deployment descriptor for
WebSphere that details the JNDI bindings for your bean. While this is only a
small part of the migration, it does provide a good starting point from which
to migrate your beans (you will have to take care of CMP entity bean field mappings
yourself).
For more information about WAS40Utils, go to the EJB Info Web site.
WebSphere Studio Application Developer
WebSphere Studio Application Developer is probably the most important tool for migrations to WebSphere Application Server 4.0. The validation features in this product do an excellent job of guiding you through the changes that need to be made to the Java code, JSPs, deployment descriptors, and other artifacts (including such things as XML validation). WebSphere Studio Application Developer includes WebSphere Application Server 4.0 Advanced Developer Edition as a test environment. The test environment is well-paired with the development environment, which provides great debugging support.
Most code migration efforts tend to employ a "dump and chase" strategy, especially when tight coupling makes identifying a vertical slice difficult. "Dump and chase" is a phrase taken from hockey; it is a hockey strategy that is quite popular in North America. In hockey, the ice is divided into zones. When on an attack, a team will often send the puck over the blue line into their opponent's zone. From there, the offensive players on the team cross into the zone and try to make a play. Some may argue that maintaining control by carrying the puck over the blue line into the zone is a more refined approach. In keeping with the metaphor, vertical slice migration would be like carrying the puck over the blue line.
WebSphere Studio Application Developer is the tool of choice for implementing this "dump and chase" strategy. In a code migration sense, dump and chase is effected by "dumping" the existing code into WebSphere Studio Application Developer and "chasing" and fixing the problems that are reported by the tool. With help from the debugger, this strategy is extended into the initial testing phases. There is an element of "dump and chase" in any migration strategy. At some level, some amount of code must be brought into the development environment and cleared of problems. The big difference is that a pure "dump and chase" effort tends to involve a huge amount of code; it is also generally difficult to test anything until everything is fixed.
Be sure to do much testing to make sure that nothing is missed.
An overriding theme throughout the potential issues presented here is that knowledge of the target platform is of paramount importance. As part of a migration effort to WebSphere Application Server 4.0, you must be familiar with the platform and the J2EE specifications upon which it is built. The most frequently asked question with respect to migration is, "How do we do X in WebSphere Application Server/J2EE?" (substitute your favorite topic for X). Knowing these answers, or at least knowing how to find the answers, will make your migration process much easier.
If you don't have the skills, get them. Educate your staff on J2EE and WebSphere. As part of the ongoing development effort, stick to J2EE as much as possible. First limit, and then abstract and encapsulate the use of proprietary technology. If you're starting a migration process, you're going to need strong J2EE and WebSphere Application Server 4.0 skills. If you don't have them yourself, get help.
- Migrating to IBM WebSphere
Application Server -- Part 1: Designing Software for Change
- Migrating to IBM WebSphere
Application Server -- Part 2: Stages of Migration
Wayne Beaton is a Senior Software Consultant with Software Services for WebSphere, part of the IBM Software Group. Wayne's diverse role involves him in many interesting areas, ranging from WebSphere Skills Transfer and Migration programs to general consulting. Wayne likes to spend his free time convincing people that extreme programming, refactoring, and unit testing actually work.




