Skip to main content

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

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

All information submitted is secure.

  • Close [x]

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.

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

All information submitted is secure.

  • Close [x]

Build distributed object management frameworks for J2EE apps

A scope-aware system eases development and prevents memory leaks

Zhengrong Tang (ztang@zenovate.com), Chief Research Architect, CTB/McGraw-Hill
Zhengrong Tang has more than 15 years of experience in software development and specializes in J2EE, XML, AOP, and Perl. He currently serves as chief research architect at CTB/McGraw-Hill. Zhengrong Tang holds a Master of Science degree in computer science from the University of Pittsburgh. Contact him at ztang@zenovate.com.

Summary:  Many enterprise Java™ technology developers build their own object management infrastructures to improve application performance. However, traditional object pools encounter problems in applications that run across distributed JVMs on multiple physical machines. In this article, Zhengrong Tang presents an object management framework that uses the concept of scopes to handle distributed systems with ease.

Date:  25 May 2004
Level:  Introductory
Also available in:   Chinese

Activity:  5158 views
Comments:  

To get performance boosts out of Java™ applications, many developers choose to develop their own object management solutions to replace the default garbage collector. A common solution is object pooling. Creating a Java object is somewhat expensive; when you create objects once and reuse them many times, you reduce the overhead. This makes sense, particularly in server-side applications, as the same server code is normally executed repeatedly. For applications that run in a single Java Virtual Machine (JVM), such a solution is normally simple. In a multi-tiered architecture, however, managing object instances across multiple machines and multiple JVMs can be quite complicated.

In this article, I'll introduce a framework for managing Java objects in a multi-tiered J2EE architecture. This framework is based on standard interfaces defined by Servlet, JSP, EJB, JMS, and Struts technologies. Therefore, it is not tied to any specific vendor's solutions. Unlike many other object management solutions that require applications to explicitly release object instances when they are no longer needed, this framework uses the concept of scopes to release object instances automatically. This feature dramatically reduces the risk of memory leaks, which are a serious problem when the default garbage collector is disabled. Overall, this framework provides a practical and easy-to-use solution to improve performance in a distributed J2EE environment.

Object creation and pooling

Java object creation and garbage collection are expensive operations. Excessive object creation can be a main cause of performance problems in Java applications. This problem tends to be worse in server-side programs like servlets. Often, you have many local variables that are short-lived within a method, but the method itself is invoked repeatedly and frequently. As a result, objects are frequently created and then garbage collected. This object churning can hurt performance.

Object pooling is one solution to this problem. The idea is to have a collection of objects -- usually of the same type -- created and stored in a pool. If an object is needed, it is retrieved from the pool and not created afresh. After the object finishes its job, it is returned back to the pool and can be reused later. The object pool needs to keep track of the state of each object and to be thread safe. You can find many implementations of Java object pooling, such as Apache's Common Pool (see Resources). Apart from implementation details, object pools generally have the interface illustrated in Listing 1.


Listing 1. Object Pool API
public Object getObject(Class clazz);
public void returnObject(Object obj);

The getObject() method retrieves an instance of the given class from the pool. The returnObject() method releases the object back to the pool.

The object pooling approach improves performance in most cases. However, it is not without cost. First of all, developers have to pay the price of explicitly calling returnObject(object) when the object is no longer needed. This seems to defeat the whole purpose of automatic garbage collection, which is an important (perhaps the most important) feature of the Java language. Secondly, the developer risks memory leakage in the application. Objects can stay around if they are not returned to the pool explicitly. These two tradeoffs might scare developers away from adopting the pooling technique.

It would be nice if Java developers could enjoy the performance boost of object pooling without having to worry about explicitly releasing objects. Fortunately, this is feasible in most server-side J2EE applications. The framework I discuss in this article provides a practical solution.


A solution based on scopes

Take a look at a server-side application that has a servlet, JSP page, and EJB component. Using the MVC and Façade patterns, Figure 1 shows a typical design.


Figure 1. A server-side application
A server-side application

Upon receiving a request, a servlet calls a method on a session bean, which then invokes a method on an entity bean. After the session call returns, the servlet then forwards the request to a JSP page. Throughout the entire flow, objects are created as indicated in Figure 1. Different objects may have different lifespans. Some objects are intended for only one request cycle; after the request is completed, they are no longer useful. Other objects may have longer lifespans: for instance, they may live through an HTTP session or even the entire application. If you identify the intended scope of each object, you might specify to automatically release objects at the end of the scope. For example, object1 and object4 in the diagram are both bound to the request scope. After the request is completed, both objects can be released automatically. object2 is bound to a transaction scope. After the transaction is terminated, it can be released automatically. object3 is bound to the application scope and lives through the entire lifetime of the application.

To accomplish this effect, resolve these two issues: First, modify the object pool so that it understands the concept of scope. Second, make an automatic notification mechanism available to indicate when a scope has ended.


The ObjectManager API

I'll use the term object manager in this article to distinguish the framework from a conventional object pool. Much like an object pool, an object manager is responsible for getting objects from a pool and returning objects back to the pool. In addition, an object manager understands the concept of scope. Listing 2 shows the API for the object manager.


Listing 2. The ObjectManager API
public interface ObjectManager {
    /**
     * Retrieve an object instance of the give class from the object pool 
     * for the given scope, identified by a scope type and a scope key.
     *
     * @param clazz         the class
     * @param scopeType     the type of the scope
     * @param scopeKey      the key to identify the scope
     *
     * @return  an object instance retrieved from the object pool
     */
    public Object getObject(Class clazz, int scopeType, Object scopeKey);

    /**
     * Release an object back to the object pool.
     *
     * @param object   the object to be released
     */
    public void releaseObject(Object object);

    /**
     * Release all objects of the given scope, identified by a scope type 
     * and a scope key.
     * @param scopeType     the type of the scope that objects bound to
     * @param scopeKey      the key to identify the scope
     */
    public void releaseAll(int scopeType, Object scopeKey);

}

To retrieve an object from the object pool, provide a scope type and a scope key. The object manager associates the scope information to each object being retrieved. When a releaseAll() method is called, the object manager can then identify objects associated with the given scope and properly release them back to the pool.


Scope types

Six scope types are commonly used in most server-side J2EE applications:

  • Transaction
  • Request
  • HTTP session
  • Application
  • Global
  • None

Developers can add additional scope types if they need them for their applications. The scope types I discuss in this article are defined in Listing 3.


Listing 3. Scope type definition
 public class ScopeTypes {

    public final static int TRANSACTION =   	1; 
    public final static int REQUEST =       	2; 
    public final static int HTTP_SESSION =       	3; 
    public final static int APPLICATION =  	4; 
    public final static int GLOBAL =        		5; 
    public final static int NONE =          		6; 

}

Transaction scope

A transaction scope extends over the entire body of a demarcated transaction. The scope starts when a transaction begins. Create a unique scope key at that time. The scope ends when the transaction commits or rolls back. At that point, all objects bound to the transaction scope are automatically released back to their pools.

Request scope

A request scope corresponds to a servlet request scope; it starts immediately after the container calls a servlet to process a request. Create a unique scope key at the same time, and end it just before the servlet finishes the process. At that point, all objects bound to this scope are automatically released back to their pools.

HTTP session scope

An HTTP session scope corresponds to the lifespan of an HTTP session. It starts when a new HttpSession is created. Create a unique scope key at that time. End it when the session is destroyed or expired. At that point, all objects bound to this session scope should be automatically released back to their pools.

Application scope

An application scope encompasses the entire lifetime of an application. It starts when an application is deployed to the application server. A unique scope key should be created at that time. The scope ends when the application is stopped or removed from the application server. At that point, all objects bound to this application scope are automatically released back to their pools.

Global scope

A global scope is the largest scope. Objects tagged with it are never released.

None scope

The none scope is used for objects that are not pooled. Objects tagged this way are created through their own object constructors every time and are released by the Java garbage collector. The object manager does not manage them at all.


Scope keys and event notification

To use the object manager defined in the last section and to identify scopes, you'll need to use scope keys. In this section, I discuss when to create scope keys and how to store them. Having the object manager understand scopes addresses one of the issues raised at the beginning of this article. The other issue is how to automatically release objects based on their scopes. To free developers of responsibility for explicitly releasing objects, you need to notify the object manager when scopes end so that it can properly release objects. Servlet and EJB containers provide the necessary event trigger mechanism to handle the scope changes. Look at how to use this event trigger mechanism in all types of scopes.

Transaction scope

According to the EJB 2.0 specification, session bean classes that implement javax.ejb.SessionSynchronization have to implement afterBegin(), beforeCompletion(), and afterCompletion(). The EJB container calls afterBegin() directly after a transaction begins and beforeCompletion() right before a transaction completes. The EJB container calls afterCompletion()immediately after a transaction is completed or rolled back.

  • Use afterBegin() to create a unique scope key for a transaction scope. Store the scope key in the session bean itself for future retrieval.
  • Use afterCompletion() to notify the object managers that a transaction scope has ended and to release the corresponding objects.

Request scope

Depending on the technologies used, you can generate events for request scope in different ways. I'll look at four of the most popular technologies in use today in this section.

Servlet 2.4. Version 2.4 of the Java Servlet specification added a listener for request-related events. javax.servlet.ServletRequestListener has a requestInitialized() method and a requestDestroyed() method.

  • requestInitialized() - The container calls this method when a servlet request is about to go into scope.Use it to create a unique scope key for this request scope. Store the scope key in the request object itself for future retrieval.
  • requestDestroyed() - The container calls this method when a servlet request is about go out of scope. Use it to notify the object manager that a request scope is about to end and to release the corresponding objects.

Servlet 2.3 or older. Version 2.3 of the Java Servlet specification does not provide any listening capability for request-related events. The best way to deal with this lack is to implement the creation of a scope key and the notification logic in a superclass that extends from javax.servlet.http.HttpServlet. Then you can extend all application servlets from this superclass. Implement the creation of a scope key and the notification logic in the service() method of this superclass.

Struts 1.0. As in Servlet 2.3, in Struts 1.0 create a scope key and implement the notification logic in a superclass that extends from org.apache.struts.action.ActionServlet. You can use the process() method to create a scope key and implement the notification logic.

Struts 1.1 Since Struts 1.1, org.apache.struts.action.RequestProcessor has been added to process logic that the ActionServlet performs as it receives each servlet request from the container. Use the RequestProcessor.process() method to create a scope key and implement the notification logic.

HTTP session scope

According to the Servlet 2.3 specification, listeners that implement javax.servlet.http.HttpSessionListener need to implement sessionCreated() and sessionDestroyed().

  • sessionCreated() - The container calls this method after a new session is created. Use it to create a unique scope key for this session scope. Store the scope key in the session object itself for future retrieval.
  • sessionDestroyed() - The container calls this method after a session is invalidated or expired. Use it to notify object managers that a session scope has ended (or is about to end) and to release the corresponding objects. (Under the Servlet 2.4 spec, the second method is called when a session is about to be invalidated; the update does not affect this framework.)

Application scope

According to the Servlet 2.3 specification, listeners that implement javax.Servlet.ServletContextListener need to implement contextInitialized() and contextDestroyed().

  • contextInitialized() - The container calls this method when the application is initialized and ready to serve. Use it to create a unique scope key for this application scope. Store the scope key in the servlet context object itself for future retrieval.
  • contextDestroyed() - The container calls this method when the application is about to be shut down. Use it to notify object managers that an application scope has ended and to release the corresponding objects.

Global scope

No event is necessary for this scope, because objects associated with it are never released.

None scope

No event is necessary for this scope because objects associated with it are released by the garbage collector rather than object managers.


Putting it together: An example

In this section, I use an example to further explain the introduced concepts. The sample code demonstrates how to use the object manager to manage a request scope in a Struts 1.1 application.

MyRequestProcessor, shown in Listing 4, intercepts every Struts action request so request scope keys can be created before a request is processed and objects can be released after the request is processed.


Listing 4. A RequestProcessor class implementation
public class MyRequestProcessor extends RequestProcessor{
   public void 
          process(HttpServletRequest request, HttpServletResponse response) 
      throws ServletException {

      // before each request is processed, create a request key
      String requestScopeKey = UniqueIDGenerator.GetID();
      request.setAttribute("requestScopeKey ", requestScopeKey);

      try {

         // process the request
         super.process(request, response);
	
      } finally {

         // after the request is processed, release all objects 
         // bound to this request scope
         ObjectManager objMgr = ObjectManagerFactory.GetInstance();
         objMgr.releaseAll(ScopeTypes.REQUEST, requestScopeKey);
      }
   }

}

In Listing 5, AnyAction represents any Struts action; it shows how you can get an object from the object manager.


Listing 5. An action class implementation
public class AnyAction extends Action {
   public ActionForward execute(ActionMapping mapping,
               ActionForm form,
               HttpServletRequest request,
               HttpServletResponse response) {

         // ...

         String requestScopeKey = request.getAttribute("requestScopeKey");
         ObjectManager objMgr = ObjectManagerFactory.GetInstance();

         obj = objMgr.getObject(obj.getClass(), ScopeTypes.REQUEST,
requestScopeKey);

         // ...
   }

}

Everything discussed above works well as long as the scope stays within the same component or container. This is true in normal situations. For instance, a request scope starts and stops in a servlet container. A transaction scope starts and stops in an EJB container. If this kind of design works for you, you can stop here and ignore the rest of the article. However, in many cases, scopes may cross multiple components. Consider a case in which an EJB session bean method is always called in a Struts action. Some objects in this session bean method might belong to an HTTP request. That is, an HTTP request started in a Struts action can actually go into an EJB session bean. In the following sections, I address the issues in such a distributed environment.


Distributed object managers

A typical J2EE application may have multiple components that run on different JVMs or even on different physical machines. In a clustered environment, one logical component may consist of more than one physical node, with each node running on a JVM. In this distributed computing environment, illustrated in Figure 2, each JVM has at least one object manager.


Figure 2. A distributed computing environment
Figure 2

Different object managers may manage objects belonging to the same scope. For instance, object1, object2, object3, and object4 in Figure 2 are all bound to the same request scope. After the request scope has ended, their individual object managers must release them. Therefore, the notification mechanism must work not only within one JVM, but also across multiple JVMs. Use the Java Messaging Service (JMS) to fulfill this goal.


Using JMS to communicate

When you have to notify multiple object managers of an event at the same time, a publish-subscribe paradigm is best suited for your communication needs. The publish-subscribe model is supported in JMS, which uses topics for broadcasting messages. To use JMS, bundle each object manager with a JMS listener. As illustrated in Figure 3, when a scope ends, a message is published to a topic. The message contains a scope type and a scope key. All JMS listeners that subscribe to this topic will then review the message and ask their object managers to release objects bound to the scope.


Figure 3. Using JMS to coordinate object management
Figure 3

To see how this works in more detail, look at some sample code. Listing 6 shows how to broadcast messages that contain scope information.


Listing 6. A sample implementation of broadcasting messages
// ---------------------------------------------------
// a scope is ended at this point
// 	the scope type is stored in scopeType
// 	the scope key is stored in scoreKey
// 	ctx is a JNDI context


TopicConnectionFactory tcf = (TopicConnectionFactory) 
	ctx.lookup("ObjectManagerJMSConnection");
TopicConnection tc = tcf.createTopicConnection();
tc.start();
TopicSession ts = tc.createTopicSession(false, 1);
Topic t = (Topic) ctx.lookup("ObjectManagerJMSTopic");
TopicPublisher tp = ts.createPublisher(t);

TextMessage tm = ts.createTextMessage();
tm.setText(scopeType + ":" + scopeKey);
tp.publish(tm);

Listing 6 shows the standard way to publish a JMS message on a topic. The names of TopicConnectionFactory and Topic should reflect the names set in your JMS server. The message is a text message containing a scope.

Listing 7 shows how to implement a JMS listener that can be bundled with an object manager.


Listing 7. A MessageListener class implementation
public class ObjectManagerListener implements MessageListener {

   private Context ctx = null;
   private TopicConnection tcon = null;
   private TopicSession tsession = null;
   private TopicSubscriber tsubscriber = null;

   /**
    * Constructor.
    * 
    * @param ctx	a JNDI context
    * @throws NamingException
    */
   public ObjectManagerListener(Context ctx) throws NamingException{
      this.ctx = ctx;
   }


   /**
    * Starts the listener.
    *
    * @throws Exception
    */
   public void start() throws Exception {
      TopicConnectionFactory tconFactory = 
         (TopicConnectionFactory) ctx.lookup("ObjectManagerJMSConnection");
      tcon = tconFactory.createTopicConnection();
      tsession = tcon.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
      Topic topic = (Topic) ctx.lookup("ObjectManagerJMSTopic");
      tsubscriber = tsession.createSubscriber(topic);
      tsubscriber.setMessageListener(this);
      tcon.start();
   }

   /**
    * Stops the listener.
    *
    * @throws Exception
    */
   public void stop() throws Exception {
      this.close();
   }

   /**
    * Upon receiving a message, the listener asks its object manager
    * to release objects in the scope specified in the message.
    * 
    * @param msg	A message containing a scope type and a scope key.
    */
   public void onMessage(Message msg){
      try {
         String msgText;
         if (msg instanceof TextMessage) {
            msgText = ((TextMessage)msg).getText();
         } else {
            msgText = msg.toString();
      }

         StringTokenizer st = new StringTokenizer(msgText, ":");
         if (st.countTokens() == 2) {
            String scopeType = st.nextToken();
            String scopeKey = st.nextToken();
            ObjectManager objMgr = ObjectManagerFactory.GetInstance();
            objMgr.releaseAll(scopeType, scopeKey);
      }
      } catch (Exception e) {
         e.printStackTrace();
      }
   }


	public void close() throws Exception {
		tsubscriber.close();
		tsession.close();
		tcon.stop();
		tcon.close();
		ctx = null;
	}

}

ObjectManagerListener is a JMS listener. When starting, it connects to the JMS server and listens on the topic. Note that in the start() method, the names of the TopicConnectionFactory and Topic should reflect the names set in your JMS server. Upon receiving a text message, if the message is in the right format, the ObjectManagerListener extracts the scope type and the scope key from the message and asks the object manager to release all objects bound to this scope.


Context for passing scope information

In a distributed environment, scopes cross multiple components. Therefore, scope keys created in one component must be passed to other components so the components can use the keys to obtain objects bound to the same scopes. The only way to pass scope keys across multiple components is through method calls. To facilitate this approach, a context object is needed to store scope information. For every request, a context object is created. This object stores keys for the current request scope, HTTP session scope, and application scope. The object is then passed to other components through method calls. When a transaction starts, the scope key for this transaction is added to this context object. When a transaction ends, the corresponding scope key is removed. Listing 8 defines the API for this object.


Listing 8. RequestContext API
public interface RequestContext {
   /**
    * Get the scope key for the given scope type.
    * 
    * @param scopeType
    * @return
    */
   public Object getScopeKey(int scopeType);

   /**
    * Set the scope key for the given scope type.
    * 
    * @param scopeType
    * @param scopeKey
    */
   public void setScopeKey(int scopeType, Object scopeKey);
}

Since the context object needs to be passed across components, the object should be serializable.


The example, modified

With JMS and RequestContext, you can now modify the previous example. In addition to creating request scope keys, MyRequestProcessor needs to get the application scope key and session scope key and store them in a RequestContext object before a request is processed. After the request is processed, it sends a JMS message to indicate that this request scope has ended. Listing 9 shows how to do this.


Listing 9. A modified implementation of the RequestProcessor class
public class MyRequestProcessor extends RequestProcessor{
   public void 
          process(HttpServletRequest request, HttpServletResponse response) 
      throws IOException, ServletException {

      // assume when application started, the application id was generated 
      // and stored in ServletContext with attribute name " appScopeKey"
      String appScopeKey = 
                request.getServletContext().getAttribute("appScopeKey");

      // session scope key 
      String sessionScopeKey = request.getSession().getId();

      // request scope key
      String requestScopeKey = UniqueIDGenerator.GetID();

      RequestContext ctx = RequestContextFactory.getInstance();
      ctx.setScopeKey(ScopeType.APPLICATION, appScopeKey);
      ctx.setScopeKey(ScopeType.HTTP_SESSION, sessionScopeKey);
      ctx.setScopeKey(ScopeType.REQUEST, requestScopeKey);

      // store the request context in request
      request.setAttribute("requestContext", ctx);

      try {

         // process the request
         super.process(request, response);

      } finally {

         // after the request is processed, release all objects bound 
         // to this request scope
         TopicConnectionFactory tcf = (TopicConnectionFactory) 
         ctx.lookup("ObjectManagerJMSConnection");
         TopicConnection tc = tcf.createTopicConnection();
         tc.start();
         TopicSession ts = tc.createTopicSession(false, 1);
         Topic t = (Topic) ctx.lookup("ObjectManagerJMSTopic");
         TopicPublisher tp = ts.createPublisher(t);

         TextMessage tm = ts.createTextMessage();
         tm.setText(ScopeTypes.REQUEST + ":" + requestScopeKey);
         tp.publish(tm);

      }
   }

}

The modified AnyAction class in Listing 10 demonstrates how to retrieve the request scope key from the RequestContext object and pass the RequestContext object to EJB components.


Listing 10. A modified implementation of the Action class
public class AnyAction extends Action{
   public ActionForward execute(ActionMapping mapping,
   ActionForm form,
      HttpServletRequest request,
      HttpServletResponse response) {

         // ...

         RequestContext ctx = 
              (RequestContext) request.getAttribute("requestContext");

         // get the request scope key from the context object
         String scopeKey = ctx.getScopeKey(ScopeType.REQUEST);
         ObjectManager objMgr = ObjectManagerFactory.GetInstance();
         obj = 
              objMgr.getObject(obj.getClass(), ScopeTypes.REQUEST, scopeKey);

         // call methodY of sessionBeanX and pass the context object 
         // as an parameter
        sessionBeanX.methodY(params, ctx);

         // ...
   }
}

Instead of retrieving scope keys from HttpServletRequest as shown in Listing 5, the sample code in Listing 10 retrieves the RequestContext object from HttpServletRequest. It then retrieves the scope keys from the RequestContext object. When calling the remote or local interfaces of EJB objects, it passes the RequestContext object as a parameter.

I've added SessionBeanX, outlined in Listing 11, to demonstrate how to use the RequestContext object inside EJB components.


Listing 11. A SessionBean class implementation
public class SessionBeanX implements SessionBean{

   public void methodY (Object params, RequestContext ctx) {

      // ...

      // get the request scope key from the context object
      String scopeKey = ctx.getScopeKey(ScopeType.REQUEST);
      ObjectManager objMgr = ObjectManagerFactory.GetInstance();
      obj = objMgr.getObject(obj.getClass(), ScopeTypes.REQUEST, scopeKey);

      // ...

   }

}

To retrieve the scope information, each method of the SessionBeanX class must have the parameter RequestContext. As shown in Listing 10, when the EJB client calls the remote or local interface, it must pass the RequestContext object, which contains information about current scopes. The RequestContext object inside the bean method can then obtain the keys of current scopes -- through methodY(), for instance.


Conclusion

This article proposes a framework for managing object instances in J2EE applications. The main advantage of this framework is that it automatically releases object instances using the event-triggering mechanism defined in J2EE. This not only frees the developer from responsibility for explicitly releasing objects in code, but also reduces the risk of memory leakage. The first half of the article has targeted a system in which the server-side code runs in a single JVM. The second half of the article targeted an environment in which the server-side code runs in multiple JVMs. Throughout the article, I provided sample code to illustrate concepts. Performance benchmarks are beyond the scope of this article and will be published in the future.


Resources

About the author

Zhengrong Tang has more than 15 years of experience in software development and specializes in J2EE, XML, AOP, and Perl. He currently serves as chief research architect at CTB/McGraw-Hill. Zhengrong Tang holds a Master of Science degree in computer science from the University of Pittsburgh. Contact him at ztang@zenovate.com.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


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. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

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.

(Must be between 3 – 31 characters.)

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

 


Rate this article

Comments

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development, Java technology
ArticleID=10942
ArticleTitle=Build distributed object management frameworks for J2EE apps
publish-date=05252004
author1-email=ztang@zenovate.com
author1-email-cc=