BEA Logo BEA WebLogic Enterprise Release 5.0

  Corporate Info  |  News  |  Solutions  |  Products  |  Partners  |  Services  |  Events  |  Download  |  How To Buy

 

   WLE Doc Home   |   CORBA Programming & Related Topics   |   Previous   |   Next   |   Contents   |   Index

Java Server Application Concepts

This chapter presents the following WLE topics:

For background information about WLE server applications and how they work, see Getting Started.

Overview

This section provides an overview of the Java server application creation process. The file names shown are based on the Bankapp sample application that is included with the WLE software. Many steps have been omitted from this simple overview. The purpose here is to give you an idea of the overall process, before you read about CORBA object state management and other key concepts in the remainder of this chapter, and before you read about detailed build steps in subsequent chapters.

To create a Java server application:

  1. Create a text file that describes the interfaces for your CORBA objects.

    The descriptions are written in the Object Management Group Interface Definition Language (OMG IDL). For example, the BankApp.idl file describes the Teller and TellerFactory interfaces.

  2. Compile the IDL files by using the m3idltojava compiler to generate, for each interface, Java object implementation files, client stubs, server skeletons, Helper classes, and Holder classes. Do not edit these files.

  3. Copy each object implementation file to a new file.

    For example, compiling the BankApp.idl file with the m3idltojava compiler generates a Teller.java file and a TellerFactory.java file. To create Java files that you can use as a starting point for adding your business logic and object implementations, you can:

    1. Copy Teller.java to TellerImpl.java.

    2. Copy TellerFactory.java to TellerFactoryImpl.java.

  4. Edit your object implementation files, adding the business logic to each object's methods.

  5. Create the Server object, which is code that performs the initialize and release functions for the server application.

  6. Use the javac compiler to compile all the *.java files into Java bytecodes (*.class files).

  7. Create a text file called a Server Description File, which is expressed in the XML language.

    To see a sample file, open the BankApp.xml file that is included with the WLE software in the following directory:

    Windows NT

    drive:\M3dir\samples\corba\bankapp_java\jdbc\

    UNIX

    /usr/local/M3dir/samples/corba/bankapp_java/jdbc/

    In your Server Description File, you assign the activation and transaction policies for the interfaces implemented in your server application. This XML file also contains a server declaration, which includes the name of the Server object and the name of the server descriptor file (SER). You can also identify the Java class files that comprise the server application's Java Archive (JAR) file.

  8. Compile the XML-based Server Description File with the buildjavaserver command and generate the SER file and JAR file.

  9. Deploy your Java server application.

The Entities You Create to Build a WLE Java Server Application

To build a WLE Java server application, you create the following entities:

There are also a number of files that you work with that are generated by the m3idltojava compiler and that you build into an WLE server application. These files are listed and described in Steps for Creating a Java Server Application.

The Implementation of the CORBA Objects for Your Java Server Application

Having a clear understanding of what CORBA objects are, and how they are defined, implemented, instantiated, and managed is critical for the person who is designing or creating an WLE Java server application.

The CORBA objects for which you have defined interfaces in the Object Management Group Interface Definition Language (OMG IDL) contain the business logic and data for your WLE Java server applications. All client application requests involve invoking operations on a CORBA object. The code you write that implements the operations defined for an interface is called an object implementation. For example, in Java, the object implementation is a Java class.

This section discusses the following topics:

How Interface Definitions Establish the Operations on a CORBA Object

A CORBA object's interface identifies the operations that can be performed on it. A distinguishing characteristic of CORBA objects is that an object's interface definition is separate from its implementation. The definition for the interface establishes how the operations on the interface must be implemented, including what the valid parameters are that can be passed to and returned from an operation.

An interface definition, which is expressed in OMG IDL, establishes the client/server contract for an application. That is, for a given interface, the server application is bound to do the following:

How the server application implements the operations may change over time. This is acceptable behavior as long as the server application continues to meet the requirement of implementing the defined interface and using the defined parameters. In this way, the client stub is always a reliable proxy for the object implementation on the server machine. This underscores one of the key architectural strengths of CORBA -- that you can change how a server application implements an object over time without requiring the client application to be modified or even to be aware that the object implementation has changed.

The interface definition also determines the content of both the client stub and the skeleton in the server application; these two entities, in combination with the ORB and the Portable Object Adapter (POA), ensure that a client request for an operation on an object can be routed to the code in the server application that can satisfy the request.

Once the system designer has specified the interfaces of the business objects in the application, the programmer's job is to implement those interfaces. This book explains how.

For more information about OMG IDL, see Creating CORBA Client Applications.

How You Implement the Operations on a CORBA Object

As stated earlier, the code that implements the operations defined for a CORBA object's interface is called an object implementation. For Java, this code consists of a set of methods, one for each of the operations defined for the interfaces in your application's OMG IDL file.

In the WLE Java environment, you define an object implementation file by copying the interface .java file generated by the m3idltojava compiler and editing the copy. For example, using the file names in the Bankapp sample application, copy the Teller.java file to TellerImpl.java . Then, you edit TellerImpl.java , adding your business logic to create the Teller object's implementation file. The suggested modification steps are described in the section Creating an Object Implementation File.

You also define the object's default in-memory behavior in a separate file, the XML-based Server Description File. In this XML file, you define the default activation and transaction policies for each interface that is implemented in the server application. You then provide this file as input to the buildjavaserver command.

How Client Applications Access and Manipulate Your Application's CORBA Objects

Client applications access and manipulate the CORBA objects managed by the server application via object references to those objects. Client applications invoke operations (that is, requests) on an object reference. These requests are sent as messages to the server application, which invokes the appropriate operations on CORBA objects. The fact that these requests are sent to the server application and invoked in the server application is completely transparent to the client; client applications appear simply to be making invocations on the client stub.

Client applications may manipulate a CORBA object only by means of an object reference. One primary design consideration is how to create object references and return them to the client applications that need them in a way that is appropriate for your application.

Typically, object references to CORBA objects are created in the WLE system by factories. A factory is any CORBA object that returns, as one of its operations, a reference to another CORBA object. You implement your application's factories the same way that you implement other CORBA objects defined for your application.

You can make your factories widely known to the WLE domain, and to clients connected to the WLE domain, by registering them with the FactoryFinder. Registering a factory is an operation typically performed by the Server object, which is described in the section The Server Object. For more information about designing factories, see the section Generating Object References.

The Content of an Object Reference

From the client application's perspective, an object reference is opaque; it is like a black box that client applications use without having to know what is inside. However, object references contain all the information needed for the WLE system to locate a specific object instance and to locate any state data that is associated with that object.

An object reference contains the following information:

The Lifetime of an Object Reference

Object references created by server applications running in a WLE domain have a usable lifespan that extends beyond the life of the server process that creates them. WLE object references can be used by client applications regardless of whether the server processes that originally created them are still running. In this way, object references are not tied to a specific server process.

The Server Object

The Java Server object is the other programming code entity that you create for an WLE server application. The Java Server object implements operations that execute the following tasks:

You implement this Server object by creating a new class that derives from com.beasys.Tobj.Server and overrides the initialize and release methods. In the server application code, you can also write a public default constructor. You create the Server object class from scratch using a text editor.

For example:

import com.beasys.Tobj.*;

/**
* Provides code to initialize and stop the server invocation.
* BankAppServerImpl is specified in the BankApp.XML input file
* as the name of the Server object.
*/

public class BankAppServerImpl
extends com.beasys.Tobj.Server {

public boolean initialize(string[] args)
throws com.beasys.TobjS.InitializeFailed;

public boolean release()
throws com.beasys.TobjS.ReleaseFailed;

}

In the XML-coded Server Description File, which you process with the buildjavaserver command, you identify the name of the Server object.

The create_servant method, used in the C++ environment of WLE, is not used in the Java environment. In Java, objects are created dynamically, without prior knowledge of the classes being used.

In the Java environment of WLE, a servant factory is used to retrieve an implementation class, given the interface repository ID. This information is stored in a server descriptor file created by the buildjavaserver command for each implementation class.

When a method request is received, and no servant is available for the interface, the servant factory looks up the interface and creates an object of the appropriate implementation class.

This collection of the object's implementation and data compose the run-time, active instance of the CORBA object.

For more information about creating the Server object, see Steps for Creating a Java Server Application.

Understanding Object References and Object State

This section presents important background information about the following topics, which have a major influence on how you design and implement WLE server applications:

It is not essential that you read these topics before proceeding to the next chapter; however, this information is located here because it applies broadly to fundamental design and implementation issues for all WLE server applications.

Generating Object References

One of the most basic functions of a WLE server application is providing client applications with object references to the objects they need to execute their business logic. WLE client applications typically get object references to the initial CORBA objects they use from the following two sources:

Client applications use the Bootstrap object to resolve initial references to a specific set of objects in the WLE domain, such as the FactoryFinder and the SecurityCurrent objects. The Bootstrap object is described in Getting Started and in Creating CORBA Client Applications.

Factories, however, are designed, implemented, and registered by you, and they provide the means by which client applications get references to objects in the WLE server application, particularly the initial server application object. At its simplest, a factory is a CORBA object that returns an object reference to another CORBA object. The client application typically invokes an operation on a factory to obtain an object reference to a CORBA object of a specific type. Planning and implementing your factories carefully is an important task when developing WLE server applications.

Client applications are able to locate via the FactoryFinder the factories managed by your server application. When you develop the Server object, you typically include code that registers with the FactoryFinder any factories managed by the server application.

It is via this registration operation that the FactoryFinder keeps track of your server application's factories and can provide object references to them to the client applications that request them. We recommend that you use factories and register them with the FactoryFinder; this model makes it simple for client applications to find the objects in your WLE server application.

Note: In WLE 4.2, references to objects implemented in Java can be created only by factories that are also implemented in Java. You cannot mix and match factories and objects with regards to implementation language.

Managing Object State

Object state management is a fundamentally important concern of large-scale client/server systems, because it is critical that such systems optimize throughput and response time. The majority of high-throughput applications, such as applications you run in a WLE domain, tend to be stateless, meaning that the system flushes state information from memory after a service or an operation has been fulfilled.

Managing state is an integral part of writing CORBA-based server applications. Typically, it is difficult to manage state in these server applications in a way that scales and performs well. The WLE software provides an easy way to manage state and simultaneously ensure scalability and high performance.

The scalability qualities that you can build into a WLE server application help the server application function well in an environment that includes hundreds or thousands of client applications, multiple machines, replicated server processes, and a proportionately greater number of objects and client invocations on those objects.

About Object State

In a WLE domain, object state refers specifically to the process, or in-memory, state of an object across client invocations on it. The WLE software uses the following definitions of stateless and stateful objects:

Object

Behavior Characteristics

Stateless

The object is mapped into memory only for the duration of an invocation on one of the object's operations, and is deactivated and has its process state flushed from memory after the invocation is complete; that is, the object's state is not maintained in memory after the invocation is complete.

Stateful

The object remains activated between invocations on it, and its state is maintained in memory across those invocations. The state remains in memory until a specific event occurs, such as:

Both stateless and stateful objects have data; however, stateful objects may have nonpersistent data in memory that is required to maintain context (state) between operation invocations on those objects. Thus, subsequent invocations on such a stateful object always go to the same servant. Conversely, invocations on a stateless object can be directed by the WLE system to any available server process that can activate the object.

State management also involves how long an object remains active, which has important implications on server performance and the use of machine resources. The section How to Manage Object State explains the various mechanisms the WLE system provides to control object state.

Object state is transparent to the client application. Client applications implement a conversational model of interaction with distributed objects. As long as a client application has an object reference, it assumes that the object is always available for additional requests, and the object appears to be maintained continuously in memory for the duration of the client application interaction with it.

To achieve optimal application performance, you need to carefully plan how your application's objects manage state. Objects are required to save their state to durable storage, if applicable, before they are deactivated. Objects must also restore their state from durable storage, if applicable, when they are activated. For more information about reading and writing object state information, see the section Reading and Writing an Object's Data.

How to Manage Object State

WLE provides two basic means to control object state:

Object Activation Policies

The WLE system provides three object activation policies that you can assign to an object's interface to determine how long an object remains in memory after it has been invoked by a client request. These policies determine whether the object to which they apply is generally stateless or stateful.

The three policies are listed and described in the following table.

Policy

Description

Method

Causes the object to be active only for the duration of the invocation on one of the object's operations; that is, the object is activated at the beginning of the invocation, and is deactivated at the end of the invocation. An object with this activation policy is called a method-bound object.

The method activation policy is associated with stateless objects. This activation policy is the default.

Transaction

Causes the object to be activated when an operation is invoked on it. If the object is activated within the scope of a transaction, the object remains active until the transaction is either committed or rolled back. If the object is activated outside the scope of a transaction, its behavior is the same as that of a method-bound object. An object with this activation policy is called a transaction-bound object.

For more information about object behavior within the scope of a transaction, and general guidelines about using this policy, see Integrating Transactions into a Java Server Application.

The transaction activation policy is associated with stateful objects for a limited time and under specific circumstances.

Process

Causes the object to be activated when an operation is invoked on it, and to be deactivated only under the following circumstances:

You determine what events cause an object to be deactivated by assigning object activation policies. For more information about how you assign object activation policies to an object's interface, see the section Step 5: Define the object activation and transaction policies..

Application-Controlled Deactivation

Application-controlled deactivation provides a means for an application to deactivate an object during run time. The TP Framework provides the com.beasys.Tobj.TP.deactivateEnable method, which a process-bound object can invoke on itself. When invoked, the deactivateEnable method causes the object in which it exists to be deactivated upon completion of the current client invocation on that object. An object can invoke this method only on itself; you cannot invoke this method on any object but the object in which the invocation is made.

The application-controlled deactivation feature is particularly useful when you want an object to remain in memory for the duration of a limited number of client invocations on it, and you want the client application to be able to tell the object that the client is finished with the object. At this point, the object takes itself out of memory.

Application-controlled deactivation, therefore, allows an object to remain in memory in much the same way that a process-bound object can: the object is activated as a result of a client invocation on it, and it remains in memory after the initial client invocation on it is completed. You can then deactivate the object without having to shut down the server process in which the object exists.

An alternative to application-controlled deactivation is to scope a transaction to maintain a conversation between a client application and an object; however, transactions are inherently more costly, and transactions are generally inappropriate in situations where the duration of the transaction may be indefinite.

A good rule of thumb to use when choosing between application-controlled deactivation and transactions for a conversation is whether there are any disk writing operations involved. If the conversation involves read-only operations, or involves maintaining state only in memory, then application-controlled deactivation is appropriate. If the conversation involves writing data to disk during or at the end of the conversation, transactions may be more appropriate.

Note: If you use application-controlled deactivation to implement a conversational model between a client application and an object managed by the server application, make sure that the object eventually invokes the com.beasys.Tobj.TP.deactivateEnable method. Otherwise, the object remains idle in memory indefinitely. (Note that this can be a risk if the client application crashes before the deactivateEnable method is invoked. Transactions, on the other hand, implement a time-out mechanism to prevent the situation in which the object remains idle for an indefinite period. This may be another consideration when choosing between the two conversational models.)

You implement application-controlled deactivation in an object using the following procedure:

  1. In the implementation file, insert an invocation to the deactivateEnable method at the appropriate location within the operation of the interface that uses application-controlled deactivation.

  2. In the Server Description File (XML), assign the process activation policy to the interface that contains the operation that invokes the deactivateEnable method.

  3. Build and deploy your application as described in the sections Step 7: Finish the Server Description File. and Step 8: Deploy the server application..

Choosing Between Stateless and Stateful Objects

In general, you need to balance the costs of implementing stateless objects against the costs of implementing stateful objects.

In the case where the cost to initialize an object with its durable state is expensive (because, for example, the object's data takes up a great deal of space, or the durable state is located on a disk very remote to the servant that activates it), it may make sense to keep the object stateful, even if the object is idle during a conversation. In the case where the cost to keep an object active is expensive in terms of machine resource usage, it may make sense to make such an object stateless.

By managing object state in a way that is efficient and appropriate for your application, you can maximize your application's ability to support large numbers of simultaneous client applications that use large numbers of objects. You generally do this by assigning the method activation policy to these objects, which has the effect of deactivating idle object instances so that machine resources can be allocated to other object instances. However, your specific application characteristics and needs may vary.

When You Want Stateless Objects

Stateless objects generally provide good performance and optimal usage of server resources, because server resources are never used when objects are idle. Stateless objects are generally a good approach to implementing server applications. Stateless objects are particularly appropriate in the following situations:

By making an object stateless, you can generally assure that server application resources are not being tied up for an arbitrarily long time waiting for input from the client application.

Note the following characteristics about an application that employs a stateless object model:

When You Want Stateful Objects

A stateful object, once activated, remains in memory until a specific event occurs, such as the process in which the object exists is shut down, or the transaction in which the object is activated is completed.

Stateful objects are typically appropriate in the following situations:

Note the following behavior with stateful objects:

Reading and Writing an Object's Data

Many of the CORBA objects managed by the server application may have data that is in external storage. This externally stored data may be regarded as the persistent or durable state of the object. You must address durable state handling at appropriate points in the object implementation for object state management to work correctly.

Because of the wide variety of requirements you may have for your client/server application with regards to reading and writing an object's durable state, the TP Framework cannot automatically handle durable object state on disk. In general, if an object's durable state is modified as a result of one or more client invocations, you must make sure that durable state is saved before the object is deactivated, and you should plan carefully how the object's data is stored or initialized while the object is active.

The sections that follow describe the mechanisms available to you to handle an object's durable state, and give some general advice about how to read and write object state under specific circumstances. The specific topics presented include:

How you choose to read and write durable state invariably depends on the specific requirements of your client/server application, especially with regard to how the data is structured. In general, your priority should be to minimize the number of disk operations, especially where a database controlled by an XA resource manager is involved.

Available Mechanisms for Reading and Writing an Object's Durable State

Table 1-1 and Table 1-2 describe the available mechanisms for reading and writing an object's durable state.

Table 1-1 Available Mechanisms for Reading an Object's Durable State

Mechanism

Description

com.beasys.
Tobj_Servant.
activate_object
method

After the TP Framework creates the servant for an object, the TP Framework invokes the activate_object method on that servant. This method is defined on the Tobj_Servant class, from which all the CORBA objects you define for your client/server application inherit.

You may choose not to define and implement the activate_object method on your object, in which case nothing happens regarding specific object state handling when the TP Framework activates your object. However, if you define and implement this method, you can choose to include code in this method that reads some or all of an object's durable state into memory. Therefore, the activate_object method provides your server application with its first opportunity to read an object's durable state into memory.

Note that if an object's OID contains a database key, the activate_object method provides the only means the object has to extract that key from the OID.

For more information about implementing the activate_object method, see Step 2: Write the methods that implement each interface's operations..

Operations on the object

You can include inside the individual operations that you define on the object the code that reads an object's durable state.

Table 1-2 Available Mechanisms for Writing an Object's Durable State

Mechanism

Description

com.beasys.
Tobj_Servant.
deactivate_object
method

When an object is being deactivated by the TP Framework, the TP Framework invokes this operation on the object as the final step of object deactivation. As with the activate_object method, the deactivate_object method is defined on the Tobj_Servant class. You implement the deactivate_object method on your object optionally if you have specific object state that you want flushed from memory or written to a database.

The deactivate_object method provides the final opportunity your server application has to write durable state to disk before the object is deactivated.

If your object keeps any data in memory, or allocates memory for any purpose, you implement the deactivate_object method so your object has a final opportunity to flush that data from memory. Flushing any state from memory before an object is deactivated is critical in avoiding memory leaks.

Operations on the object

As you may have individual operations on the objects that read durable state from disk, you may also have individual operations on the object that write durable state back to disk.

For method-bound and process-bound objects in general, you typically perform database write operations within these operations and not in the deactivate_object method.

For transaction-bound objects, however, writing durable state in the deactivate_object method provides a number of object management efficiencies that may make sense for your transactional server applications.

.

Note: If you use the deactivate_object method to write any durable state to disk, any errors that occur while writing to disk are not reported to the client application. Therefore, the only circumstances under which you should write data to disk in this operation is when the object is transaction-bound (that is, it has the transaction activation policy assigned to it), or you scope the disk write operations within a transaction by invoking the TransactionCurrent object.

Any errors encountered while writing to disk during a transaction can be reported back to the client application. For more information about using the deactivate_object method to write object state to disk, see the section Caveat for State Handling in com.beasys.Tobj_Servant.deactivate_object.

Reading State at Object Activation

Using the com.beasys.Tobj_Servant.activate_object method on an object to read durable state may be appropriate when either of the following conditions exist:

The advantages of using the activate_object method to read durable state include:

Reading State Within Individual Operations on an Object

With all objects, regardless of activation policy, you can read durable state in each operation that needs that data. That is, you handle the reading of durable state outside the com.beasys.Tobj_Servant.activate_object method. Cases where this approach may be appropriate include the following:

For example, consider an object that represents a customer's investment portfolio. The object contains several discrete records for each investment. If a given operation affects only one investment in the portfolio, it may be more efficient to allow that operation to read the one record than to have a general-purpose activate_object method that automatically reads in the entire investment portfolio each time the object is invoked.

Stateless Objects and Durable State

In the case of stateless objects -- that is, objects defined with the method activation policy -- you must ensure the following:

The TP Framework invokes the com.beasys.Tobj_Servant.activate_object method on an object at activation. If an object has an OID that contains a key to the object's durable state on disk, the activate_object method provides the only opportunity the object has to retrieve that key from the OID.

If you have a stateless object that you want to be able to participate in a transaction, we generally recommend that if the object writes any durable state to disk that it be done within individual methods on the object. However, if you have a stateless object that is always transactional -- that is, a transaction is always scoped when this object is invoked -- you have the option to handle the database write operations in the deactivate_object method, because you have a reliable mechanism in the XA resource manager to commit or roll back database write operations accurately.

Note: Even if your object is method-bound, you may have to take into account the possibility that two server processes are accessing the same disk data at the same time. In this case, you may want to consider a concurrency management technique, the easiest of which is transactions. For more information about transactions and transactional objects, see Integrating Transactions into a Java Server Application.

Stateful Objects and Durable State

For stateful objects, you should read and write durable state only at the point where it is needed. This may introduce the following optimizations:

In general, transaction-bound objects must depend on the XA resource manager to handle all database write or rollback operations automatically.

Note: Data written to external storage that is not managed by an XA resource manager will not be coordinated within the scope of a transaction; if the transaction is rolled back, the data is not rolled back.

For more information about objects and transactions, see Integrating Transactions into a Java Server Application.

Your Responsibilities for Object Deactivation

As mentioned in the preceding sections, you implement the com.beasys.Tobj_Servant.deactivate_object method to write an object's durable state to disk. You should also implement this operation on an object to flush any remaining object data from memory so that the object's servant can be used to activate another instance of that object. You should not assume that an invocation to an object's deactivate_object method also results in an invocation of that object's destructor.

Avoiding Unnecessary I/O

Be careful not to introduce inefficiencies into the application by doing unnecessary I/O in objects. Situations to be aware of include the following:

Sample Activation Walkthrough

For examples of the sequence of activity that takes place when an object is activated, see Getting Started.

Using Design Patterns

It is important to structure the business logic of your application around a well-formed design. The WLE software provides a set of design patterns to address this need. A design pattern is simply a structured solution to a specific design problem. The value of a design pattern lies in its ability to be expressed in a form you can reuse and apply to other design problems.

The WLE design patterns are structured solutions to enterprise-class application design problems. You can use them to design successful large-scale client/server applications.

The design patterns summarized here are a guide to using good design practices in WLE client and server applications. They are an important and integral part of designing WLE client and server applications, and the chapters in this book show examples of using these design patterns to implement the Bankapp sample applications.

The Process-Entity design pattern applies to a large segment of enterprise-class client/server applications. This design pattern is referred to as the flyweight pattern in Object-Oriented Design Patterns, Gamma et al., and as the Model-View-Controller in other publications.

In this pattern, the client application creates a long-lived process object that the client application interacts with to make requests. For example, in the WLE University sample applications, this object might be the registrar that handles course browsing operations on behalf of the client application. The courses themselves are database entities and are not made visible to the client application.

The advantages of the Process-Entity design pattern include:

For complete details on the Process-Entity design pattern, see Technical Articles on the Online Documentation CD.