Migrating WebLogic Startup Code to WebSphere Application Server V5

One of the many issues to consider when moving applications from competitive platforms to WebSphere Application Server is how to migrate the use of proprietary APIs to J2EE standards. This paper examines several alternatives for migrating the use of specific WebLogic Server APIs to WebSphere Application Server.

Wayne Beaton, Senior Software Consultant, IBM Software Services for WebSphere

Wayne Beaton is a Senior Software Consultant with Software Services for WebSphere, part of the IBM Software Group. His main focus is migration from previous versions of WebSphere and competitive products to WebSphere Application Server 5.0. He has coauthored two IBM Redbooks on the topic of migration. Wayne's diverse role involves him in lots of other interesting stuff including the WebSphere Skills Transfer programs and general consulting. Wayne likes to spend his free time convincing people that Extreme Programming, refactoring and unit testing actually work.



Sree Anand Ratnasinghe (sreer@us.ibm.com), Certified IT Specialist, IBM

Sree Anand Ratnasinghe is an IBM and Open Group Certified IT Specialist with the Technology Practice team in IBM Software Services for WebSphere (ISSW). She has implemented proofs-of-concept and mentored customers interested in the WebSphere Application Server and the WebSphere family of products. She currently leads technical enablement programs for IBM Consultants as well as curriculum architecture for conferences on WebSphere products. Sree holds an MS in Information Networking from Carnegie Mellon University.



28 January 2004

Introduction

There are many issues to consider when moving applications from competitive platforms to WebSphere Application Server. One of these is how to migrate the use of proprietary APIs to J2EE standards. At times when there is no direct analog to the proprietary APIs available in J2EE, one ends up considering several alternatives, each with various tradeoffs.

Recently, while migrating an application written for BEA WebLogic Server to WebSphere Application Server, we encountered the use of a set of proprietary APIs -- T3StartupDef and T3ShutdownDef -- which allow applications to register code that gets executed by the server during startup and shutdown. This paper examines several alternatives for migrating the use of WebLogic Server's T3StartupDef and T3ShutdownDef APIs to WebSphere Application Server, and discusses the pros and cons of the various alternatives.


Background

The weblogic.common.T3StartupDef interface is included with WebLogic Server. Implementations of this interface are used to execute custom code when the server starts up. T3StartupDefs are connected to the application server instance itself and not to any particular J2EE application or module.

The T3StartupDef interface provides two methods:

  • setServices(T3ServicesDef services)
    Called by the server before any other methods to allow the instance to initialize the T3Services variable that provides access to various services in the WebLogic server.
  • startup(String, Hashtable)
    Invoked by WebLogic Server after all J2EE modules are loaded, initialized, and started, but before any incoming requests are accepted.

Listing 1 shows a very simple implementation of T3StartupDef that, for illustration purposes, creates and initializes a fictitious rules engine. This class is used to initialize a rules server and do some pre-fetching of rules on server startup.

Listing 1. An example of T3StartupDef

package com.issw.startup;
import weblogic.common.*;
import javax.naming.*;
import com.issw.rules.*;

public class MyT3StartupImpl implements T3StartupDef {
  private T3ServicesDef services;
  public void setServices(T3ServicesDef services) {
    this.services = services;
  }

  public String startup(String name, Hashtable args)
  throws Exception {
    RulesEngine rs = new RulesEngine();
    rs.start();
    rs.preFetchRules();

    //now we will stash it in JNDI for future use
    InitialContext ic = new InitialContext();
    InitialContext ic = new InitialContext();
    Context startupContext = ic.createSubcontext("startup");
    startupContext.bind("ruleserver", rs);
  }
}

T3ShutdownDef is very similar, except that its shutdown(String, Hashtable) method is called on server shutdown, after the last incoming request is handled, but before the J2EE modules are shutdown.

T3StartupDefs and T3ShutdownDefs must be registered explicitly with the server to run. The startup class implements the interface, and is registered in WebLogic Server's config.xml file.

For documentation on the T3StartupDef and T3ShutdownDef interfaces, see Resources.


Startup code for a Web container

The servlet specification provides two well-defined mechanisms for running start up code in a Web container:

  • Servlet initialization via the init() method
  • Initialization of the servlet context.

These options are discussed below.

Servlet initialization

A servlet is initialized when it is loaded; during initialization, the servlet's init() method is invoked (if it exists). By default, servlets are loaded and initialized the first time they are accessed. Alternatively, the servlet specification provides for individual servlets to be loaded when the Web container starts. In summary, the combination of setting a servlet to load when the Web container is started ,and providing that servlet with an init() method, can be used to provide startup code.

A servlet's init() method, if one exists, is called immediately after the servlet is loaded, but before it responds to any requests. The destroy() method, if it exists, is called when the containing Web module shuts down, after the last request is complete. Given this lifecycle, the combination of these methods can be appropriate for application startup and shutdown code in some circumstances.

You can create a servlet as part of your Web application called, for example, StartupServlet, and put all your startup logic in its init() method. By default, servlets are loaded and initialized when they are first accessed. The servlet specification provides for preloading servlets by marking them to "load at startup" with a load order. By default, servlets are loaded and initialized starting with the lowest number (a servlet with a load order of "0" is loaded before all others; a negative value guarantees that the servlet will be loaded, but does not guarantee order).

A startup servlet need not actually respond to any user requests. If desired, you can prevent the servlet from responding to user requests by not providing any servlet mapping entries for the servlet and by disabling the servlet's service(HttpServletRequest, HttpServletResponse) method (have the method throw a ServletException).

Figure 1 shows where to set the necessary deployment descriptor parameters in WebSphere Studio Application Developer 5.1.

Figure 1. Configuring a servlet to load on start up
Configuring a servlet to load on start up

The servlet's init() method is always called after the application server and the Web container are started. The init() method can therefore access application server resources like data sources and queues.

Listing 2 shows an example of a startup servlet that initializes our fictitious rules engine.

Listing 2. An example servlet init() method

package com.issw.servlets.startup;

import java.io.*;
import javax.naming.*;
import javax.servlet.*;
import javax.servlet.http.*;
import com.issw.rules.*;

public class StartupServlet extends HttpServlet {
  public void init() throws ServletException {
    RulesEngine rs = new RulesEngine();
    rs.start();
    rs.preFetchRules();

    //now we will stash it in JNDI for future use
    InitialContext ic = new InitialContext();
    Context startupContext = ic.createSubcontext("startup");
    startupContext.bind("ruleserver", rs);

  }

  protected void service(
    HttpServletRequest request,
    HttpServletResponse response)
  throws ServletException, IOException {
    throw new ServletException("Do not use.");
  }
}

There are several advantages to this method, including:

  • This method exploits a standard mechanism for configuration using servlet parameters that is fully supported by the servlet and J2EE specifications.
  • The servlet init() method runs after the application server itself is started and configured, which means that application server resources such as DataSources and queues can be accessed.
  • The servlet destroy() method runs after the Web container has completed its last request, but before the container and application server instances are shut down.
  • The servlet can be configured (using load order) to run before any other servlets are loaded and initialized.
  • It will work in WebSphere Application Server V4.0 and V5.0 (and on other J2EE 1.2+ compatible application servers).

One potential disadvantage to this approach is found in the servlet specification. According to the specification, a servlet is destroyed when "the servlet container determines that a servlet should be removed from service" (Servlet 2.3, section 2.3.4). Essentially, the Web container can potentially destroy and then reload and reinitialize the servlet at another time (as needed). In effect, startup code placed in an init() method can potentially be run multiple times during the life of an application (though we have never observed this behavior in WebSphere Application Server).

Initialization of the servlet context

The javax.servlet.ServletContextListener interface was introduced in the Servlet 2.3 API. Instances of this class, which are explicitly registered in a Web module's deployment descriptor, respond to a collection of lifecycle events that are triggered in response to changes in state within the Web container.

The contextInitialized(ServletContextEvent) method is invoked by the Web container when the Web module is ready to process requests. That is, this method is invoked after the application server has configured all of its resources, the Web container has started, and all servlets are preloaded (as required). This is an ideal place to pre-fetch and cache required values or set up objects that will be needed later. The contextDestroyed(ServletContextEvent) method serves as a notification that the Web module is about to be shut down.

To create a ServletContextListener in WebSphere Studio Application Developer (you must be in a J2EE 1.3/Servlet 2.3 project to do this), right click on Java Source in your Web application project, and select New => Life-cycle Listener. Use the New Life-cycle Listener wizard, shown in Figure 2, to create an implementation of the javax.servlet.ServletContextListener class. Ensure that the option to add the listener to the deployment descriptor (web.xml) is selected.

Figure 2. Creating a new ServletContextListener in WebSphere Studio Application Developer 5.0
Creating a new ServletContextListener in WebSphere Studio Application Developer 5.0

To register this ServletContextListener, a listenerType element is added to the to the Web project's deployment descriptor. This additional element will appear on the Listeners tab for your Web project's deployment descriptor, as shown in Figure 3.

Figure 3. Registering the listener with the Web application
Registering the listener with the Web application

Listing 3 shows a sample implementation of ServletContextListener.

Listing 3. A sample implementation of ServletContextListener

package com.issw.servlets.startup;

import javax.servlet.*;
import com.issw.rules.*;

public class StartupServletContextListener implements
ServletContextListener {
  public void contextDestroyed(ServletContextEvent event) {
  }

  public void contextInitialized(ServletContextEvent event) {
    RuleServer rs = new RuleServer();
    rs.start();
    rs.preFetchRules();

    //now we will stash it in JNDI for future use
    InitialContext ic = new InitialContext();
    Context startupContext = ic.createSubContext("startup");
    startupContext.bind("ruleserver", rs);
  }
}

This option is essentially the same as servlet init() method with two fundamental differences:

  1. The behavior is invoked after the Web container has started, so all configured servlets have been loaded and initialized.
  2. It will only work on application servers that support J2EE 1.3 or later (it will not work on WebSphere Application Server V4).

Startup code for an EJB container

The EJB 2.0 specification provides no accommodation for code to be run at startup. In fact, the EJB specification actively restricts the options that are available by forbidding the use of static initializers and other static behavior that might be used for these purposes.

Still, there are at least two options:

  • Web container startup code can be leveraged to invoke startup behavior on EJBs.
  • The startup beans in WebSphere Application Server Enterprise provide startup behavior.

These options are explored below.

Startup from the Web container

Using techniques discussed in the previous section, the Web container can be used to perform startup operations using EJBs. An application might, for example, use a stateless session bean to gather configuration information from a database, or use a stateless session bean to preload entity beans for performance reasons.

Listing 4 shows an example of a servlet init() method that invokes a method on an EJB to perform some startup code.

Listing 4. An example of using a servlet to invoke EJB startup code

package com.wtb.servlets;

import javax.ejb.*;
import javax.naming.*;
import javax.servlet.*;
import com.issw.beans.*;

public class StartupServletContextListener
implements ServletContextListener {
  private static final String prefetchHome =
    "java:comp/env/PrefetchHome";

  public void contextDestroyed(ServletContextEvent event) {
  }

  public void contextInitialized(ServletContextEvent event) {
    InitialContext context = null;
    try {
      context = new InitialContext();
      PrefetchLocalHome home =
        (PrefetchLocalHome)context.lookup(prefetchHome);
      PrefetchLocal bean = home.create();
      bean.doPrefetch();
    } catch (NamingException e) {
      // TODO* Add logging.
      // For now, we'll let life go on.
    } catch (CreateException e) {
      // TODO Add logging.
      // For now, we'll let life go on.
    } finally {
      try {
        context.close();
      } catch (NamingException e) {
      }
    }
  }
}

* When you include "TODO" at the beginning of a Java comment in WebSphere Studio V5.1, it appears as a task in the task list. Use it to mark code that needs to be revisited.

The example above makes it look pretty easy to provide startup code for an EJB. However, there is one wrinkle: the J2EE specification provides no standard mechanism for enforcing a particular load order for modules in an enterprise archive. The order in which the modules are specified in an enterprise archive's deployment descriptor does not necessarily affect the load order in WebSphere Application Server V5.

WebSphere Application Server V5 does, however, provide a mechanism for imposing a particular load order of the modules. The configuration for the WebSphere Application Server can be changed to load J2EE modules in a specific order based on weights. The downside of this mechanism is that it is completely detached from the code: the relevant configuration is not part of the EAR file, and needs to be updated separately each time the application is deployed to the server.

To change the ordering of modules within an enterprise application in WebSphere Studio, open the Server Configuration Editor for your test server and select the Applications tab, as shown in Figure 4. Here, you can select your application, and edit the start weight for it, which affects the order in which it will be loaded relative to other enterprise applications. What is more useful in our case is to expand the enterprise application, and set the start weights for the individual Web and EJB modules within the application so that dependencies can be resolved as necessary. Modules with a numerically smaller starting weight take precedence.

Figure 4. Setting Web and EJB module relative start weights in the WebSphere Studio Server Configuration Editor
Setting Web and EJB module relative start weights in the WebSphere Studio Server Configuration Editor

Startup weights are tied to the application server instance (or cell) configuration, not the application. This means that when you deploy your application from WebSphere Studio into WebSphere Application Server, these starting weights must be provided again. In fact, these values must be specified every time the application is deployed or redeployed.

To configure weight values in the WebSphere Application Server administrative console, navigate to the configuration for each module and set the values as shown in Figure 5.

Figure 5. Setting an EJB module's relative start weight in the WebSphere Application Server Administrative Console
Setting an EJB module's relative start weight in the WebSphere Application Server Administrative Console

Once you have set the starting weights as necessary, save your changes and restart the application. You will need to revisit the weight settings every time you redeploy your application. A script may make the process more safely repeatable (the construction of such a script is beyond the scope of this paper).

J2EE compliant EJB startup code

The above technique works and is relatively simple to implement. However, the solution is non standard and puts increased pressure on the administration staff to ensure that the application is properly deployed.

It is possible to provide similar functionality that works within the framework of J2EE. However, the solution is a lot heavier and relies on additional configuration (though completely standard configuration) of the application server.

Rather than directly invoking the EJB, the servlet can instead invoke a message driven bean using JMS. If the EJB container is loaded and initialized before the Web container, the message driven bean will immediately pick up and process the initialization method. If the Web container starts first, the startup code will put a message on the queue where it will sit and wait for the EJB container to load and for the message driven bean to consume the message.

When the message driven bean receives the startup message, it can process the startup code itself. This should work well for invoking startup code that is contained within the EJB container, but it will not work if the EJB container needs to pass values back to the Web container as part of the startup process.

If JMS is used to provide startup code, the load order of the J2EE modules is irrelevant. This solution, while rather heavy and complex, is J2EE compliant and should work on all J2EE 1.3+ application servers.

WebSphere startup beans

WebSphere Application Server Enterprise Version 5.0 includes a very powerful programming model extension known as the startup bean (see the WebSphere Application Server Enterprise Information Center for a full description). Startup beans are special session beans which are loaded and executed by the EJB container just before the application starts. Unlike some of the Web application-oriented solutions we examined earlier, when a startup bean's start method is invoked, all J2EE modules (including Web and EJB modules) will have already been loaded, and the bean can make full use of EJBs and all other J2EE features without having to worry about timing dependencies. Also, an application will not complete its startup until the startup bean has successfully completed its execution.

Startup beans are tied directly to a particular enterprise application: they are defined alongside regular EJBs in an EJB module. After an enterprise application and all of its modules are loaded and initialized, but before the application accepts any incoming requests, the start() method for each startup bean's remote interface is invoked to do whatever needs to be done at application start time; there is also a stop() method invoked just before the enterprise application stops.

In order for an application to mark a bean as a startup bean, the application developer must adhere to the following restrictions:

  • The bean must be an EJB 2.0 session bean with a remote interface. The bean's home interface must be com.ibm.websphere.startupservice.AppStartUpHome (this interface is defined in startupbean.jar which is included with WebSphere Application Server Enterprise).
  • The bean remote interface must be or extend com.ibm.websphere.startupservice.AppStartUp.
  • The start() and stop() methods on the AppStartUp interface can be any transaction attribute except TX_MANDATORY. This is to guarantee the bean creates its own transaction if one is needed.

The bean can be stateful or stateless. If it is stateful then the same instance is used for start() and stop(). The EJB environment variable java:comp/env/wasStartupPriority (an integer value) can be used to configure the order in which startup beans are invoked. This allows an ordering to be specified if multiple startup beans are present in a module. The beans are started from low to high order. Beans are stopped in the reverse order that they are started in. This allows for guaranteed ordering relationships among startup beans.

Startup beans have access to all resources deployed on the application server (DataSources, queues, etc.) as well as to all J2EE resources deployed within the enterprise application. Startup beans can be used, for example, in conjunction with asynchronous beans to do things like warming up entity bean caches, or setting up worker threads or schedule tasks. (Asynchronous beans, effectively threads managed by the application server, are intended to provide services denied by J2EE's lack of support for application threads. For more information, consult the WebSphere Application Server Enterprise Information Center.

You can create a startup bean using WebSphere Studio Application Developer Integration Edition (hereafter called Application Developer). Before you can add a startup bean to your EJB Project, you will first need to add the required library, startupbean.jar, to the project's build path (this library contains the code required to support startup beans in run time). To do this, open the properties dialog for your EJB project and select the Java Build Path page. On the Libraries tab, select Add Variable. On the resulting "New Variable Classpath Entry" dialog, select the WAS_EE_V5 variable, and select Extend. On the "Variable Extension" dialog, expand the lib directory and select startupbean.jar. Click OK. The results are shown in Figure 6.

This addition is only required to make the build succeed in Application Developer; the library is automatically included on the run time classpath for WebSphere Application Server Enterprise.

Figure 6. Add startupbean.jar to build path
Add startupbean.jar to build path

Next, create an EJB Session Bean using the WebSphere Studio wizards in the normal way. The bean can be stateful or stateless. Set the following values, as shown in Figure 7:

  • Remote home interface: com.ibm.websphere.startupservice.AppStartUpHome
  • Remote interface: com.ibm.websphere.startupservice.AppStartUp.
Figure 7. Creating a startup bean
Creating a startup bean

At this point, you will see two warnings in the task pane, indicating that you have not implemented the start() and stop() methods, as defined in the EJB remote interface. You need to fill these in with your business logic. A rudimentary sample is shown in Listing 5.

package com.ibm.test;
public class StartupSampleBean implements javax.ejb.SessionBean {
  private javax.ejb.SessionContext mySessionCtx;

  public boolean start() {
    System.out.println ("In Application Start");
    Cache ch = new Cache();
    ch.preFetch();
    return true;
  }

  public void stop() {
    System.out.println ("In Application Stop");
    //do cleanup here as needed
  }

  // The standard EJB methods, including getSessionContext(),
  // setSessionContext(), ejbCreate(), ejbActivate(), etc.
  // are not shown.
}

The advantages of startup beans are many:

  • No timing dependency issues: you don€™t have to "poll and see" if an EJB module is up; the startup beans are the last things to be executed before the server starts.
  • Veto power: if the startup bean returns false due to some catastrophic occurrence, the application start will not proceed.
  • You can have multiple startup beans and easily prioritize their starting order.
  • Startup beans have the full IBM WebSphere Application Server J2EE and enterprise extensions programming model available for use (they are very powerful when, for example, combined with asynchronous beans).
  • Startup beans run with the full J2EE security context.

WebSphere Application Server Enterprise is needed to make use of startup beans.


Conclusion

Finding suitable replacements for proprietary technology is one of the most challenging aspects of code migration. Whenever possible, we choose solutions that conform to standards or are supported configurations.

The J2EE specification does not currently include any specific support for startup or shutdown code. As discussed in this article, servlet init() and destroy() methods can be used for this purpose, as can implementations of ServletContextListener. Unfortunately, with these it is only possible to guarantee that the startup code will run after the containing Web module itself is loaded. There is no standard way to ensure that the startup code will run after an EJB module, for example, is loaded, but you can use module starting weights within WebSphere Application Server to achieve this result.

While startup beans are not currently part of any standard, they are fully supported. Further, they are an elegant and relatively simple solution; the startup beans themselves are configured and managed alongside the application code with which they work. Startup beans are probably the most general solution available, not only providing support to access all application server resources, but the contents of every module in the enterprise application as well.


Acknowledgements

Thanks to Roland Barcia for being very patient with us.

Resources

Documentation for the T3StartupDef interface
Documentation for the T3ShutdownDef interface

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=14112
ArticleTitle=Migrating WebLogic Startup Code to WebSphere Application Server V5
publish-date=01282004