I’ve been sharing some thoughts about what’s become a significant trend in many IT organizations, and in particular with my clients…converting Java applications from JEE Application Servers to Tomcat, and more typically Tomcat+add-ons.
Many IT organizations are re-thinking their commitment to commercial JEE Application Servers, due to both challenging business environments that drive the need for more cost effective application architectures and the need to transition to more responsive/agile applications development. When IT organizations talk about “migrating” their applications, I’ve noted that they generally are focusing on one or more of three distinct situations. These are:
I’ve been focusing on the migration of existing JEE applications to the most popular of the light weight containers, Tomcat. There are many excellent reasons to consider moving applications off of the commercial JEE servers sold by Oracle/BEA, IBM, etc. While we are focusing on the migration process, many of the business and technical decision factors apply equally well to the second and third situations.
This time, I will be discussing the technologies involved in migrating JEE application code from commercial JEE servers to Tomcat. I’d like to thank the kind (and very expert) folks at SpringSource, as well as a number of other friends around the industry, for their valuable insight regarding the technologies involved. Any errors (and opinions) are mine alone. Additionally, some of the material draws on information published by SpringSource and other open source materials found on the internet.
The Java EE specification is quite complex and it also includes a significant number of Java Optional Packages. For each package, the specification defines the version for environments that support a full-stack Java EE 5.0 environment…IE JEE Application Servers.
Note: I assert that Tomcat IS an application server, although it IS NOT a “JEE Application Server”. This is both a strength (by design, it leaves out tons of stuff we rarely use anyway and the needless cost/weight/complexity) and a weakness (it lacks some frequently used services and most administrative capabilities). That said, I’d much rather start with a rock solid, performant, base and then add what I need, rather than dealing with the cost/weight/cpomplexity of the JEE Application Servers..
A Tomcat environment provides access to most of these APIs as a standard capability, by enabling you to “drop in” standard implementation JARs to the Tomcat lib directory, or by bundling the standard implementation JARs with the deployed applications. In some cases, excellent commercial alternatives also exist. In my clients experience, a very large portion (80%+) of their applications require only Tomcat and a fairly small number of add-on’s/drop-in’s.
It is a definite advantage of the Tomcat environment that many of these JEE packages are not embedded out-of-the-box. This is because, as previously noted, most applications only utilize a small portion of the JEE standard and even less of the WLS/WAS proprietary functionality. You can add needed services to the environment fairly easily, in “drop-in” fashion, which means you run the latest version of the specifications and you significantly reduce server bloat (from nearly a GB to tens of MB) by bringing in only needed services and capabilities. Additionally, you can choose among multiple providers, and server operators and application deployers can decide whether to add a package as a standard server feature (by dropping it into the server lib directory), or to embed it in each application.
The following table summarizes the availability of the Java Optional Packages defined by Java EE 5.0 in the Tomcat environment. These are in the same order as Table EE.6-1 in the Java EE 5.0 specification document.
Java EE API Support |
Tomcat |
|
EJB 2.1 and 3.0 |
Client access only. No support for running EJBs themselves. |
|
Servlet 2.5 |
Standard. |
|
Java Server Pages (JSP) 2.1 |
Standard. |
|
JavaMail 1.4 |
Drop in standard JAR to Tomcat lib directory or embed in applications. |
|
JMS 1.1 |
Client: Drop in standard JAR. Server: No embedded implementation. Multiple third-party (open-source or commercial) products available to run alongside or embedded. |
|
JTA 1.1 |
No embedded implementation. Plug-in external JTA provider if needed. |
|
JAF 1.1 |
Drop in standard JAR to Tomcat lib directory or embed in applications. |
|
Connector 1.5 |
Not supported. |
|
Web Services 1.2 |
Drop in standard or third-party JAR. Multiple open-source and commercial implementations available. Add JARs to Tomcat lib directory or embed in applications. |
|
JAX-RPC 1.1 |
Drop in standard or third-party JAR. Multiple open-source and commercial implementations available. Add JARs to Tomcat lib directory or embed in applications. |
|
JAX-WS 2.0 |
Drop in standard or third-party JAR. Multiple open-source and commercial implementations available. Add JARs to Tomcat lib directory or embed in applications. |
|
JAXB 2.0 |
Drop in standard JAR. Add standard JAR to Tomcat lib directory or embed in applications. |
|
SAAJ 1.3 |
Drop in standard JAR to Tomcat lib directory or embed in applications. |
|
JAXR 1.0 |
Drop in standard JAR to Tomcat lib directory or embed in applications. |
|
Java EE Management 1.1 |
Not supported. |
|
Java EE Deployment 1.2 |
Not supported. |
|
JACC 1.1 |
Not supported. |
|
JSP Debugging 1.0 |
Standard |
|
JSTL 1.2 |
Standard, |
|
Web Services Metadata 2.0 |
Drop in standard JAR to Tomcat lib directory or embed in applications. |
|
JSF 1.2 |
Drop in standard or third-party JAR. Multiple open-source and commercial implementations available. Add JARs to Tomcat lib directory or embed in applications. |
|
Common Annotations 1.0 |
Standard. Supported in Servlets per specification. |
|
StAX 1.0 |
Drop in standard or third-party JAR. Multiple open-source and commercial implementations available. Add JARs to Tomcat lib directory or embed in applications. |
|
Java Persistence 1.0 |
Standard or third-party drop-in, including updated 2.0 spec. Multiple open-source and commercial implementations available. Add JARs to Tomcat lib directory or embed in applications |
In the sections that follow, I’ll take a look at various aspects of the code migration process, including layers, transactions, EJBs, remoting, data sources, security, messaging, and management.
A properly layered, interface-driven, and decoupled architecture that uses inversion of control and dependency injection patterns offers tangible benefits:
These benefits dramatically facilitate code refactoring, although care should also be taken to avoid un-necessary “creeping elegance”. It’s all too easy to get tangled up in what can amount to a complete re-write, while the objective (and budget) may be much more modest.
An existing architecture that is not adequately layered and interface-based requires additional refactoring to make it so. So, consider whether this additional refactoring, above and beyond what is immediately needed for the migration effort, will pay off in future ongoing maintenance and enhancement of the application.
If you need to migrate code-level changes, consider refactoring and migrating piece-by- piece instead of in a “big bang” process, as this is generally less disruptive and more manageable. You can work in multiple iterations, possibly with working code between iterations, until all refactoring is completed.
Where migration involves refactoring away from technology that is not available in Tomcat, with the goal of working code at the end of each iteration, the migration effort should remain in the JEE Application Server until those technologies are completely refactored. For example, consider an existing application that implements business services as session EJBs, currently running in a full-stack Java EE environment, that are being refactored to execute in a Tomcat environment. Migration in pieces allows only a subset of the EJBs to be refactored in each iteration (to POJO implementations of those services, for example…or to an appropriate framework). Of course, if the goal is to test working code between each iteration (a VERY good practice, and particularly suited to agile development environments), this implies staying in the JEE Application Server until all the EJB’s are refactored.
Migration in pieces usually requires one of two strategies: migration by vertical slice, or migration by layer. In the migration by vertical slice approach, a complete “vertical slice” encompassing multiple layers (services, data access, domain, and so on) often representing a logical application component, is migrated in one iteration. Over multiple iterations, code gradually moves from legacy vertical slices to migrated vertical slices. In the migration by layer approach, code is migrated a layer (for example, certain application serevices) at a time. The decision to use a migration by vertical slice or migration by layer approach is application-specific and depends on existing architecture and existing code coupling. It also depends strongly on team preference and standard prqactices. In both cases, the idea is to perform refactoring in manageable chunks.
Many larger JEE applications have a distinct set of functionality that implements a service or application layer. This code implements use cases, or functionality, specific to the particular application. In best-practice fashion, this layer typically is hidden behind use case-specific interfaces, and it often interacts with a data access layer (by interface), and with domain objects or data transfer objects. In those applications that do use EJBs, it is fairly typical for the service layer to be implemented as EJBs. Additionally, both transactions and method level security are usually implemented in the service layer.
Where a more than minimal refactoring effort is required in the service layer anyway, consider whether to expense potentially additional effort towards effecting a properly layered and interface based codebase. This effort may pay off in the migration effort itself and/or in subsequent maintenance costs.
Many larger Java applications have a distinct set of functionality that implements a data-access layer, to the extent that this code is responsible for persisting data transfer objects or domain objects to one or more datastores. Typically, code in this layer is hidden behind a set of interfaces that abstract the exact persistence technology in use.
However, in practice, it is not practical to try to abstract away the use of lower level persistence technology such as JDBC, as opposed to higher level technology such as object-relational mapping (ORM). Code in this layer is also often referred to as data-access objects (DAOs). Where data access functionality is implemented through entity EJBs, it is necessary to migrate this code.
Migration from JEE Application Servers to Tomcat does not normally entail significant refactoring of web tier or presentation layer code, because Tomcat was the reference implementation for this portion of the JEE standard and/or is the actual implementation within some JEE Applications ervers. In many cases, web tier code can remain almost untouched.
Areas that may require refactoring are related to EJB access and to remoting. Where web tier code is a client of EJB services, and is directly performing EJB stub lookup, a straightforward conversion process to the use of plain Java services may be required. Where code is using EJB remoting, and continued use of remoting is actually necessary, refactoring to an alternate remoting technology is necessary.
Before migrating existing transactional code to a Tomcat environment, it is important to understand the options available in a Tomcat environment for demarcating and driving transactions in application code.
Although application-specific code may drive transactions toward using these APIs, it is also common for application code to utilize a third-party abstraction layer, which itself talks to these APIs. As an example, the Spring Framework offers transaction functionality, which allows both a declarative and programmatic approach to transaction demarcation, and which abstracts the distinction between JTA and local resource transactions.
Tomcat, out-of-the-box, does not include a transaction manager implementation that supports the JTA APIs. You can choose among several approaches to migrating application code that uses JTA, whether through POJO services or EJBs using bean-managed transactions:
EJB 2.x session beans are not available in a Tomcat environment. A migration to Tomcat entails refactoring any application logic currently implemented as session beans. EJB 2.x session beans are generally considered dead-end, legacy technology (if indeed they were ever a good idea), which is another factor that drives refactoring of this code.
The general approach to refactoring session beans is to re-implement the logic as POJO services. General concerns stem from handling transactions for the session beans, and to a lesser degree, the handling of security.
CMT implies that transactions are applied to EJB code through a declarative process, where the EJB deployment descriptors are annotated to indicate that the container should make certain methods transactional when invoked. The use of CMT must be refactored by one of several approaches:
See JTA: Non-EJB and EJB Bean-Managed Transactions, as the same general approach applies to migrating BMT code to Tomcat as POJO code, whether using JTA or local resource APIs.
I’ve rarely found a client who used container managed security, due to it’s inherent limitations. You can annotate EJBs in a declarative fashion in their XML descriptors to indicate that the container should apply simple (simplistic?) role-based security to certain EJB methods. You need to refactor container-managed security by one of these approaches:
EJB message-driven beans (MDBs) are also not available as a technology choice in a Tomcat environment. A migration to Tomcat entails some refactoring away from MDBs. However, because code inside MDBs does not have a significant dependency on the EJB container (as opposed to other types of EJBs), the preferred refactoring approach requires minimal (if any) code-level changes.
EJB 2.x entity beans are not available in a Tomcat environment. Migration to Tomcat requires refactoring of persistence logic currently implemented as entity beans. Entity beans are considered dead-end, legacy technology, and their use fell out of favor a number of years ago. Where legacy applications still use entity beans, their disadvantages are an excellent argument for refactoring, with or without a migration to Tomcat. Many IT organizations have standardized on third party Data Persistence solutions, including Hibernate and Kodo.
The goal in refactoring away from entity beans is to end up with an interface-based, decoupled data-access layer, so that to the extent possible, higher layers are mostly agnostic to actual persistence choices. Three of the technologies that exist for implementing persistence logic in the data access layer are:
Client code that accesses EJB functionality sometimes accesses EJBs through an abstraction or lookup layer, so that the client code is only aware of plain Java business interfaces. In general, this type of client code does not require significant refactoring when you are moving away from EJBs.
Where EJB client code has direct awareness of service EJBs, EJB stubs during lookup through JNDI, EJB exceptions, or EJB remote interfaces, you must refactor the client code as part of refactoring the service EJB code to plain Java service implementations. For migrations in which the Spring Framework is available and many EJBs exist, consider migrating EJB client code to use Spring's EJB proxies as an initial step. Client code working through Spring's EJB proxies is generally unaware of EJBs, and is concerned simply with business interfaces. The decoupling achieved in this fashion will then facilitate the Migrate in Pieces migration strategy, as it allows client code to be unaware of when exactly EJB service code is migrated.
Existing application code that accesses or implements non-EJB remoting services through RMI can migrate essentially unchanged to a Tomcat environment. Convert remote EJB’s to POJO services as described in the preceding section on migrating EJB’s, and where remoting is still needed, implement remoting using RMI or another appropriate protocol.
Where remoting is desired, and some refactoring is needed regardless, consider using Spring's transparent remoting functionality. Spring Remoting allows POJO services to be exported in a declarative fashion, over a number of wire protocols, without the services having to be aware of or coupled to the remoting protocol. Similarly, on the client, side, Spring can create client-side remoting proxies such that client code calling through the proxies is unaware that it is talking to anything other than a local business interface.
In a full stack application server environment, DataSources are typically application server-managed connection pools bound to the JNDI tree. When migrating to Tomcat, you can bind an instance of the high concurrency connection pool provided with Tomcat to Tomcat's JNDI tree, allowing client code to work as-is. Another common connection pool in usage in Tomcat environments is Commons database connection pool (DBCP), although the high concurrency pool is recommended for its higher scalability.
It is also quite common to embed the connection pool in the deployed application itself, where it is a product, such as DBCP, that allows this scenario. DBCP makes this process very easy, because you can create and configure it as a simple JavaBean.
Existing code utilizing Servlet spec security in the web layer will normally work unchanged in a Tomcat environment. That said, it is fairly rare for any real world application to depend on the Servlet spec security model and all the major JEE server vendors have augmented JEE security by adding sophisticated user privilege management services to their servers, as well as offering stand alone security products compatible with their JEE servers.
It is slightly more common for applications based on WAS to utilize container-managed security for EJB’s, although even there it is more common to utilize IBM’s excellent Tivoli Access Manager security, CA’s SiteMinder, or any of a number of third party application security products. In the case of WLS, BEA incorporated a comprehensive user/application security service directly into the server, and also offers a commercial upgrade that manages security for other environments. CA, IBM, and Oracle/BEA work with Tomcat, and user security generally transfers fairly readily.
The Tomcat environment does not provide an embedded message queue. As such, when moving applications from a full-stack application server environment where an embedded message queue in that environment is being used, an external message queuing product must be used alongside Tomcat. A number of options are available to you, both open source (for example, ActiveMQ) and commercial.
Convert EJB message-driven beans as described in Message-Driven EJB Migration Strategy. Where messaging-related code is being refactored, consider using Spring's extensive messaging functionality, to reduce messaging boilerplate in general, and to take advantage of Spring's out-of-the-box container managed Message Driven POJO capability.
Other messaging code should normally work unchanged in the Tomcat environment, once an external queue is plugged in, with the caveat that any vendor-specific code must be converted to the new message queuing product.
Completely separate from the JEE specification (and other services) provided by the commercial JEE servers, these products also provide sophisticated management capabilities that are used by developers and IT administrators alike. Tomcat lacks most such capabilities, which makes the migration process somewhat more difficult for developers trying to debug the migrated code.
Many IT organizations have “rolled their own” Tomcat administration solutions. Some of these are excellent, but also have represented a major investment. They also tend to be script/console based and have fairly steep learning curves. None of the solutions I’ve seen include monitoring or management of other environments or of the complete stack, much less things like virtualized instances.
A number of commercial offerings are available that include a managed version of Tomcat environments, including VMware/SpringSource tcServer and MuleSoft’s Tcat Server. VMware/SpringSource’s product includes a Hyperic subset and is upgradable to full Hyperic for those environments where sophisticated monitoring and management is needed up/down the stack and across multiple environments. MuleSoft’s product is based on LambdaProbe, and while it is much less functional than Hyperic, it has also been optimized for Tomcat monitoring and management.
We’ve been discussing the conversion of applications from JEE Application Server’s to Tomcat based deployment environments. We’ve reached a number of conclusions, based on both IT industry experience and on expert advice. Some of the most important points are:
Take a hard look at where you are spending your IT dollars. You can save a lot by telling your JEE Application Server vendor “thanks, but we’re moving to Tomcat!"
Popular Links
Comments
Post new comment