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

Steps for Creating a Java Server Application

This chapter describes the basic steps involved in creating a WLE Java server application. The steps shown in this chapter are not definitive; there may be other steps you may need to take for your particular server application, and you may want to change the order in which you follow some of these steps. However, the development process for every WLE server application has each of these steps in common.

This chapter presents the following topics:

This chapter begins with a summary of the steps, and also lists the development tools and commands used throughout this book. Your particular deployment environment might use additional software development tools, so the tools and commands listed and described in this chapter are also not definitive.

The chapter uses examples from the Bankapp sample application, which is provided with the WLE software. For complete details about the sample application, see the Guide to the Java Sample Applications. For complete information about the tools and commands used throughout this book, see the Java Programming Reference.

Summary of the Java Server Application Development Process

The basic steps involved in the creation of a server application are summarized in the following table:

Step 1: Compile the OMG IDL file for the server application.
Step 2: Write the methods that implement each interface's operations.
Step 3: Create the Server object.
Step 4: Compile the Java source files.
Step 5: Define the object activation and transaction policies.
Step 6: Verify the environment variables.
Step 7: Finish the Server Description File.
Step 8: Deploy the server application.

The WLE software also provides the following development tools and commands:

Tool

Description

m3idltojava

Compiles your application's OMG IDL file.

buildjavaserver

Creates a JAR file containing your Java server class files; also creates a server descriptor file (SER).

buildXAJS

For applications that use an XA-compliant resource manager, creates an XA-specific version of the JavaServer.

tmloadcf

Creates the TUXCONFIG file, a binary file for the WLE domain that specifies the configuration of your server application.

tmadmin

Among other things, creates a log of transactional activities, which is used in some of the sample applications.

Step 1: Compile the OMG IDL file for the server application.

The basic structure of the client and server portions of the application that runs in the WLE domain are determined by statements in the application's OMG IDL file. When you compile your application's OMG IDL file, the m3idltojava compiler generates many files, some of which are shown in the following diagram:

The preceding diagram shows some of the files generated when the sample BankApp.IDL file is compiled by the m3idltojava command.

These files are described in Table 2-1.

Note: Do not modify these files.

Table 2-1 Sample Files Produced by the m3idltojava Compiler

File

Default Name

Description

Base interface class file

interface .java

Contains an implementation of the interface, written in Java.

Copy this file to create a new file and add your business logic to the new file. By convention in our samples and in this document, we name this file interfaceImpl.java , substituting the actual name of the interface in the file name. We call this new file an object implementation file.

Client stub file

_interface Stub.java

Contains generated code for sending a request.

Server skeleton file

_interface ImplBase.java

Contains Java skeletons for each interface specified in the OMG IDL file. The skeleton maps client requests to the appropriate operation in the Java server application during run time.

Holder class file

interface Holder.java

Contains the implementation of the Holder class. The Holder class provides operations for out and inout arguments, which CORBA has, but which do not map exactly to Java.

Helper class file

interface Helper.java

Contains the implementation of the Helper class. The Helper class provides auxiliary functionality, notably the narrow method.

Using the m3idltojava Compiler

To generate the files listed in Table 2-1, enter the following command:

m3idltojava [options] idl-filename

In the m3idltojava command syntax:

For more information about the m3idltojava compiler, including details on the m3idltojava command, see the Java Programming Reference.

Note: The m3idltojava compiler supports all the functionality provided by the idltojava compiler from Sun Microsystems, Inc. For more information about the idltojava compiler, refer to the following Web site:

http://java.sun.com/products/jdk/idl/

Step 2: Write the methods that implement each interface's operations.

As the server application programmer, your task is to write the methods that implement the operations for each interface you have defined in your application's OMG IDL file.

The Java object implementation file contains:

Creating an Object Implementation File

Although you can create your server application's object implementation file manually, you can save time and effort by using the m3idltojava compiler to generate a file for each interface. The interface .java file contains Java signatures for the methods that implement each of the operations defined for your application's interfaces.

To take advantage of this shortcut, use the following steps:

  1. Create a copy of the interface .java file, which was created when you compiled your OMG IDL file with the m3idltojava command, and name it interface Impl.java . For example, using the Bankapp sample file names, you would copy Teller.java to a new file named TellerImpl.java .

  2. Open the new interface Impl.java file. For example, in the previously unedited TellerImpl.java file, we changed:

    public interface Teller extends org.omg.CORBA.Object {

    to:

    public class TellerImpl extends Bankapp._TellerImplBase {

    Bankapp._TellerImplBase is the class defined in the server skeleton file that was generated by the m3idltojava compiler for the Teller object.

  3. For each method in TellerImpl.java , we added the public keyword. For example, we changed:

    float deposit(int accountID, float amount)

    to:

    public float deposit(int accountID, float amount)

Repeat this procedure to create interface Impl.java object implementation files for your interfaces, and add the business logic for your Java server application.

Implementing a Factory Object

As mentioned in the section How Client Applications Access and Manipulate Your Application's CORBA Objects, you need to create factories so that client applications can easily locate the objects managed by your server application. A factory is like any other CORBA object that you implement, with the exception that you register it with the FactoryFinder object. Registering a factory is described in the section Writing the Code That Creates and Registers a Factory.

The primary function of a factory is to create object references, which it does by invoking the com.beasys.Tobj.TP.create_object_reference method. The create_object_reference method requires the following input parameters:

For example, in the Bankapp sample application, the TellerFactory interface specifies the following operations in the TellerFactoryImpl.java file.

Note: In this code fragment, the Import statement appeared earlier in the source file and is not shown here.

org.omg.CORBA.Object teller_oref =
TP.create_object_reference(
BankApp.TellerHelper.id(), // Repository ID
tellerName, // Object ID
null // Routing Criteria
);

In the previous code example, notice the following:

Using Threads with WLE

WLE supports the ability to configure multithreaded JavaServers. For each JavaServer, you can establish the maximum number of threads in the application's UBBCONFIG file.

For information about the tradeoffs of using single-threaded JavaServers or multithreaded JavaServers, see the section Enabling Multithreaded JavaServers. For information about defining the UBBCONFIG parameters, see Chapter 3 of the Administration Guide.

Step 3: Create the Server object.

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

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.

When your Java server application starts, the TP Framework creates the Server object specified in the XML file. Then, the TP Framework invokes the initialize method. If the method returns true, the server application starts. If the method throws the com.beasys.TobjS.InitializeFailed exception, or returns false , the server application does not start.

When the server application shuts down, the TP Framework invokes the release method on the Server object.

Any command-line options specified in the CLOPT parameter for your specific server application in the SERVERS section of the WLE domain's UBBCONFIG file are passed to the public boolean initialize(string[] args) operation as args . For more information about passing arguments to the server application, see the Administration Guide. For examples of passing arguments to the server application, see the Guide to the Java Sample Applications.

Within the initialize method, you can include code that does the following, if applicable:

Writing the Code That Creates and Registers a Factory

If your server application manages a factory that you want client applications to be able to locate easily, you need to write the code that registers that factory with the FactoryFinder object, which is invoked typically as the final step of the server application initialization process.

To write the code that registers a factory managed by your server application, do the following:

  1. Create an object reference to the factory.

    This step involves creating an object reference as described in the section Implementing a Factory Object. In this step, you include an invocation to the com.beasys.Tobj.TP.create_object_reference method, specifying the Interface Repository ID of the factory's OMG IDL interface.The following Bankapp example, from the BankAppServerImpl.java file, creates an object reference, represented by the variable fact_oref , to the TellerFactory factory:

    // Save the Teller factory name.
    tellerFName = new String(args[0]);

    // Create the Teller factory object reference.

    fact_oref = TP.create_object_reference(
    BankApp.TellerFactoryHelper.id(), // factory Repository id
    tellerFName, // object id
    null // no routing criteria
    );

  2. Register the factory with the WLE domain.

    This step involves invoking the following operation for each of the factories managed by the server application:

    // Register the factory reference with the factory finder.

    TP.register_factory(
    fact_oref, // factory object reference
    tellerFName // factory name
    );

    The com.beasys.Tobj.TP.register_factory method registers the server application's factories with the FactoryFinder object. This operation requires the following input parameters:

Releasing the Server Application

When the WLE system administrator enters the tmshutdown command, the TP Framework invokes the following operation in the Server object of each running server application in the WLE domain:

public void release()

Within the release() operation, you may perform any application-specific cleanup tasks that are specific to the server application, such as:

Once a server application receives a request to shut down, the server application can no longer receive requests from other remote objects. This has implications on the order in which server applications should be shut down, which is an administrative task. For example, do not shut down one server process if a second server process contains an invocation in its release() operation to the first server process.

During server shutdown, you may want to include an invocation to unregister each of the server application's factories. For example, the following example is from the BankAppServerImpl.java file:

// Unregister the factory.
// Use a try block since cleanup code shouldn't throw exceptions.

try {
TP.unregister_factory(
fact_oref, // factory object reference
TellerFName // factory interface id
);

} catch (Exception e){
TP.userlog("Couldn't unregister the TellerFactory: " +
e.getMessage());
e.printStackTrace();
}

The invocation of the com.beasys.Tobj.TP.unregister_factory method should be one of the first actions in the release() implementation. The unregister_factory method unregisters the server application's factories. This operation requires the following input arguments:

Step 4: Compile the Java source files.

After you have implemented your application's objects and the Server object, use the javac compiler to create the bytecodes for all the class files that comprise your application. This set of files includes the *.java source files generated by the m3idltojava compiler, plus the object implementation files and server class file that you created.

Step 5: Define the object activation and transaction policies.

As stated in the section Managing Object State, you determine what events cause an object to be deactivated by assigning object activation policies, transaction policies, and, optionally, using the application-controlled deactivation feature.

You specify default object activation and transaction policies in the Server Description File, which is expressed in XML, and you implement application-controlled deactivation via the com.beasys.Tobj.TP.deactivateEnable method in your Java code. This section explains how you implement one of the mechanisms, using the Bankapp WLE sample application as an example.

Specifying Policies in XML

The WLE software supports the following activation policies, described in Object Activation Policies:

Activation Policy

Description

method

Causes the object to be active only for the duration of the invocation on one of the object's operations.

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.

process

Causes the object to be activated when an operation is invoked on it, and to be deactivated only when one of the following occurs:

The WLE software also supports the following transaction policies, described in Integrating Transactions into a Java Server Application:

Transaction Policy

Description

always

When an operation on this object is invoked, this policy causes the TP Framework to begin a transaction for this object, if there is not already an active transaction. If the TP Framework starts the transaction, the TP Framework commits the transaction if the operation completes successfully, or rolls back the transaction if the operation raises an exception.

If always is specified, the AUTOTRAN parameter in the application's UBBCONFIG file is ignored.

optional

The implementation may be transactional. Objects can be invoked either inside or outside the scope of a transaction. If the AUTOTRAN parameter is enabled in the application's UBBCONFIG file, the implementation is transactional. Servers containing transactional objects must be configured within a group associated with an XA-compliant resource manager.

Optional is the default transaction policy.

never

Causes the TP Framework to generate an error condition if this object is invoked during a transaction.

If never is specified, the AUTOTRAN parameter in the application's UBBCONFIG file is ignored.

ignore

If a transaction is currently active when an operation on this object is invoked, the transaction is suspended until the operation invocation is complete. This transaction policy prevents any transaction from being propagated to the object to which this transaction policy has been assigned.

If ignore is specified, the AUTOTRAN parameter in the application's UBBCONFIG file is ignored.

To assign these policies to the objects in your application, create the Server Description File, which is written in the Extensible Markup Language (XML). Specify the activation policies for each of your application's interfaces.

Note: For information about the XML tags used with the WLE Server Description File, see the CORBA Java Programming Reference.

The following example shows a portion of the BankApp.xml file that was created for the WLE Bankapp sample application. Notice that there are no default policy settings in the XML file; the policies are explicitly assigned.

<?xml version="1.0"?>
<!DOCTYPE M3-SERVER SYSTEM "m3.dtd">

<M3-SERVER server-implementation="com.beasys.samples.BankAppServerImpl"
server-descriptor-name="BankApp.ser">

<MODULE name="com.beasys.samples">
<IMPLEMENTATION
name="TellerFactoryImpl"
activation="process"
transaction="never"
/>

<IMPLEMENTATION
name="TellerImpl"
activation="method"
transaction="never"
/>

<IMPLEMENTATION
name="DBAccessImpl"
activation="method"
transaction="never"
/>
</MODULE>
.
.
.
</M3-SERVER>


Step 6: Verify the environment variables.

Several environment variables are defined by the WLE software when the product is installed, but it is always a good idea to verify the following key environment variables prior to the buildjavaserver compilation step. The environment variables are:

To verify whether an environment variable has been set, you can use the echo command, as shown in the following examples:

On Windows NT systems:

echo %JAVA_HOME%

On Solaris systems:

echo $JAVA_HOME

If you discover that required WLE system variables are not set on your system, you can set them as shown in the following examples.

On Windows NT systems:

set JAVA_HOME=c:\jdk1.2

set CLASSPATH=.;%TUXDIR%\udataobj\java\jdk\m3.jar;%TUXDIR%\locale\java\M3

set PATH=%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;%JAVA_HOME%\jre\bin\classic;
%TUXDIR%\lib;%TUXDIR%\bin;%PATH%

On Solaris systems:

JAVA_HOME=/usr/kits/jdk1.2

CLASSPATH=.:$TUXDIR/udataobj/java/jdk/M3.jar:$TUXDIR/locale/java/M3

PATH=$JAVA_HOME/bin:$TUXDIR/bin:$PATH

LD_LIBRARY_PATH=$JAVA_HOME/jre/lib/sparc/native_threads:
$JAVA_HOME/jre/lib/sparc/classic:$JAVA_HOME/jre/lib/sparc:$TUXDIR/lib

THREADS_FLAG=native

export JAVA_HOME CLASSPATH PATH LD_LIBRARY_PATH THREADS_FLAG

Note that during the deployment step, you must also define the environment variables APPDIR and TUXCONFIG . These variables are described in subsequent sections of this chapter.

Step 7: Finish the Server Description File.

After you have compiled the Java source code and defined the environment variables, enter additional information in the XML-based Server Description File, and then supply the Server Description File as input to the buildjavaserver command.

Edit your Server Description File to identify the Server object and the name of the file that will contain your Java application's server descriptor. This portion of the XML file is called the server declaration; its location in the file is immediately after the prolog. The required prolog contains the following two lines:

<?xml version="1.0"?>
<!DOCTYPE M3-SERVER SYSTEM "m3.dtd">

Note: The DTD file type stands for Document Type Definition. In XML, a DTD file is used to specify software descriptions or to format documents. The m3.dtd file is supplied by the WLE system and specifies the set of elements (or tags, such as <IMPLEMENTATION> ) that are parsed by the buildjavaserver compiler. The compiler understands the attributes attached to each element, and which elements can be used with another element.

The server declaration used in the sample BankApp.xml file is as follows:

<M3-SERVER
server-implementation="com.beasys.samples.BankAppServerImpl"
server-descriptor-name="BankApp.ser">

In the XML file for your Java server application, you can also include elements that will cause buildjavaserver to create a Java Archive (JAR) file. This section of the XML file is optional, because you could use the JAR command to assemble your application's classes into a JAR file. However, the <ARCHIVE> element provides help by simplifying the process of collecting the files.

For example, the BankApp.XML file contains the following elements:

<ARCHIVE name="BankApp.jar">
<PACKAGE-RECURSIVE name="com.beasys.samples"/>
</ARCHIVE>

The archive element must be the last element inside the <M3-SERVER> element. It must be located after all the modules and implementations.

If the XML file contains instructions to create an archive, both the class specified by server_name and the file specified by server_descriptor are stored in the archive. The server_descriptor file is inserted in the archive manifest with the M3-Server tag; this insertion makes the server descriptor the entry point during server execution.

If you do not include the archive element, the buildjavaserver command generates only the server descriptor and writes it in the file specified in the server-descriptor-name attribute of the M3-SERVER element.

For more information about the elements and options in the XML-based Server Description File, see the Java Programming Reference.

When you have completed your edit to the Server Description File, you are ready to use the buildjavaserver command. (This step assumes that you have already defined the environment variables that are identified in the section Step 6: Verify the environment variables..)

The buildjavaserver command has the following format:

buildjavaserver [-s searchpath] input_file.xml

In the buildjavaserver command syntax:

Step 8: Deploy the server application.

You or the system administrator deploy the WLE server application by using the procedure summarized in this section. For complete details on building and deploying the WLE Bankapp sample application, see the Guide to the Java Sample Applications.

To deploy the server application:

  1. Place the server application JAR file in the directory listed in APPDIR . On NT systems, this directory must be on a local drive (not a networked drive). On Solaris, the directory can be local or remote.

  2. If your Java server application uses an XA-compliant resource manager such as Oracle, you must build an XA-specific version of the JavaServer by using the buildXAJS command at a system prompt. Provide as input to the command the resource manager that is associated with the server. In your application's UBBCONFIG file, you also must use the JavaServerXA element in place of JavaServer to associate the XA resource manager with a specified server group. See the Java Programming Reference for details about the buildXAJS command.

  3. Create the application's configuration file, also known as the UBBCONFIG file, in a text editor. Include the parameters to start JavaServer or JavaServerXA . For example:

    *SERVERS
    .
    .
    .
    JavaServer
    SRVGRP = BANK_GROUP2
    SRVID = 8
    CLOPT = "-A -- -M 10 BankApp.jar TellerFactory_1"
    SYSTEM_ACCESS=FASTPATH
    RESTART = N

    Note: There is a strict order to starting servers in WLE Java. Also, you can specify a fully qualified path to the location of the JAR file; or, JavaServer looks for the application's JAR file in the value for the APPDIR environment variable. See Chapter 3 of the Administration Guide for UBBCONFIG file details.

  4. Set the following additional environment variables on the machine from which you are booting the WLE server application:

  5. If you have not already done so, set the TUXDIR environment variable on all machines that are running in the WLE domain or that are connected to the WLE domain. This environment variable points to the location where the WLE software is installed.

  6. Enter the following command to create the TUXCONFIG file:

    prompt> tmloadcf -y application- ubbconfig -file

    The command-line argument application- ubbconfig -file represents the name of your application's UBBCONFIG file. Note that you may need to remove any old TUXCONFIG files to execute this command.

  7. Enter the following command to start the WLE server application:

    prompt> tmboot -y

    You can reboot a server application without reloading the UBBCONFIG file.

For complete details about configuring the JDBC Bankapp and XA Bankapp sample applications, see the Guide to the Java Sample Applications. For complete details on creating the UBBCONFIG file for WLE applications, see the Administration Guide.

Development and Debugging Tips

The following topics are discussed in this section:

Use of CORBA and WLE Exceptions and the User Log

This section discusses the following topics:

Client Application View of Exceptions

When a client application invokes an operation on a CORBA object, an exception may be returned as a result of the invocation. The only valid exceptions that can be returned to a client application are the following:

The WLE system works to ensure that these CORBA-defined restrictions are not violated, which is described in the section Server Application View of Exceptions.

Because the set of exceptions exposed to the client application is limited, client applications may occasionally catch exceptions for which the cause is ambiguous. Whenever possible, the WLE system supplements such exceptions with descriptive messages in the user log, which serves as an aid in detecting and debugging error conditions. These cases are described in the following section.

Server Application View of Exceptions

This section presents the following topics:

Exceptions Raised by the WLE System that Can Be Caught by Application Code

The WLE system may return the following types of exceptions to an application when operations on the TP object are invoked:

The WLE System's Handling of Exceptions Raised by Application Code during the Invocation of Operations on CORBA Objects

A server application can raise exceptions in the following places in the course of servicing a client invocation:

It is possible for the server application to raise any of the following types of exceptions:

All exceptions raised by server application code that are not caught by the server application are caught by the WLE system. When these exceptions are caught, one of the following occurs:

The following sections show how the WLE system handles exceptions raised by the server application during the course of a client invocation on a CORBA object.

Exceptions raised in the com.beasys.Tobj_Servant.activate_object method

If any exception is raised in the activate_object method:

Exceptions raised in operation implementations

The WLE system requires operation implementations to throw either CORBA system exceptions, or user-defined exceptions defined in OMG IDL that are known to the client application. If these types of exceptions are thrown by operation implementations, then the WLE system returns them to the client application, unless one of the following conditions exists:

Exceptions raised in the com.beasys.Tobj_Servant.deactivate_object method

If any exception is raised in the deactivate_object method, the following occurs:

Detecting Error Conditions in the Callback Methods

The WLE system provides a set of predefined exceptions that allow you to specify message strings that the TP Framework writes to the user log if application code gets an error in any of the following callback methods:

You can use these exceptions as a useful debugging aid that allows you to send unambiguous information about why an exception is being raised. Note that the TP Framework writes these messages to the user log only. They are not returned to the client application.

You specify these messages with the following exceptions, which have an optional reason string:

Exception

Callback Methods That Can Raise This Exception

ActivateObjectFailed

com.beasys.Tobj_Servant.
activate_object

DeactivateObjectFailed

com.beasys.Tobj_Servant.
deactivate_object

InitializeFailed

com.beasys.Tobj.Server.initialize

ReleaseFailed

com.beasys.Tobj.Server.release

To send a message string to the user log, specify the string in the exception, as in the following example:

throw new InitializeFailed("Unable to Initialize Bankapp server");

Note the following:

Common Pitfalls of OMG IDL Interface Versioning and Modification

An object is instantiated based on its Interface Repository ID. It is crucial that this interface ID is the same as the one supplied in the factory when the factory invokes the com.beasys.Tobj.TP.create_object_reference method.

It is possible for this condition to arise if, during the course of development, different versions of the interface are being developed or many modifications are being made to the IDL file. Even if you typically use the interfaceHelper.id method to specify the interface repository ID, it is possible for a mismatch to occur.

If the interface IDs do not match, the following message is placed in the user log (ULOG ) and the create_object_reference method returns a null object reference:

IJTPFW_CAT:38: ERROR: TP.create.object.reference() could not create object reference for: Interface = Interface-ID OID= oid-number

Caveat for State Handling in com.beasys.Tobj_Servant.deactivate_object

The deactivate_object method is invoked when the activation boundary for an object is reached. You may, optionally, write durable state to disk in the implementation of this operation. It is important to understand that exceptions raised in this operation are not returned to the client application. The client application will be unaware of any error conditions raised in this operation unless the object is participating in a transaction. Therefore, in cases where it is important that the client application know whether the writing of state via this operation is successful, we recommend that transactions be used.

If you decide to use the deactivate_object method for writing state, and the client application needs to know the outcome of the write operations, we recommend that you do the following:

If transactions are not used, we recommend that you write object state within the scope of individual operations on the object, rather than via the deactivate_object method. This way, if an error occurs, the operation can raise an exception that is returned to the client application.