WebSphere Application Server: Best Practices for an Application Development Infrastructure

WebSphere Application Server: Best Practices for an Application Development Infrastructure
WebSphere Application Server
White Paper
WebSphere Application Server:
Best Practices for an Application
Development Infrastructure
Matthew A. Oberlin
I/T Architect/Specialist
IBM Pittsburgh Lab
September 2002
http://www.ibm.com/websphere/developer/services
Introduction
Purpose
Intended Audience
WebSphere Application Server Tooling
Ideal WebSphere Development Environment
Studio Integration
Development Test Integration
The Development Process
Developing WebSphere applications using WebSphere Studio Application Developer
Outputs of the Development Process
The EAR File
Additional Outputs from Development
Sample Installation Procedure
Application Packaging
WebSphere Studio Application Developer zipCreation Utility
Application Environment
Property Files
Automated Build Process
ANT - The Java Build Language
The preDeploy Tool
Summary
Appendix A
Appendix B
Appendix C
Appendix D
Topics for Future Consideration
Application Requirements and Design
Migrating the WebSphere Studio Application Developer Workspace
Organizing a Development Effort
WebSphere Studio Application Developer and Meta Data
J2EE Packaging and WebSphere
References
Redbooks
On the Web
White papers
Acknowledgments
Contact Information
Trademarks
Terms and Conditions
3
3
3
3
4
4
5
6
7
15
15
16
16
20
20
22
23
24
24
28
29
30
33
35
37
42
42
42
42
42
42
43
43
43
43
44
44
44
44
2
Introduction
Purpose
This white paper describes some best practices for implementing a development infrastructure in
which to create applications for WebSphere® Application Server. The goal is to have the
development of WebSphere applications be performed in a consistent, reliable and repeatable
manner. To accomplish this, there must be processes and procedures in place. Furthermore,
these processes and procedures must be well-documented, as well as reasonable, understood, and
communicated throughout the development organization.
Intended Audience
This paper assumes that the reader is familiar with the J2EE 1.2 specification; if not in complete
detail, then at least with the concepts and packaging. Terms like EAR (Enterprise Application
Archive), WAR (Web Application Archive), and EJB (Enterprise Java Bean) JAR (Java
Archive) should be familiar to the reader. A wealth of information, including the complete J2EE
specification can be found at Sun’s Java™ website: http://java.sun.com/j2ee/.
This paper also assumes that the reader knows how to use the tools that ship with WebSphere
Application Server 4.0, Advanced Edition.
WebSphere Application Server Tooling
The target production environment described (or assumed) here is WebSphere Application
Server 4.0, Advanced Edition. Some tools, such as wscp and the Application Assembly Tool
(AAT), ship with WebSphere Application Server Version 4.0x. More information about wscp
can be found in the WebSphere Application Server InfoCenter in Section 6.6.0.2.2. More
information about AAT can be found in the InfoCenter in Section “6.3: Assembling applications
and generating deployment code.”
Many people creating applications for WebSphere Application Server will use WebSphere
Studio Application Developer1. There are two open source products that are heavily used with
WebSphere Studio Application Developer: the Concurrent Versions System (CVS) and Jakarta
ANT. This paper will assume that the Software Configuration Management (SCM) repository
being used is CVS. More information about CVS can be found at http://www.cvshome.org/.
ANT is a scripting language to aid in building applications. More information about ANT can be
found at http://jakarta.apache.org/ant/.
1
When we say WebSphere Studio Application Developer, we are referring to the new WebSphere tooling based on
Eclipse. This is WebSphere Studio 4.0 or later. WebSphere Studio comes in a number of editions, but the
differences are not relevant to this paper.
3
Ideal WebSphere Development Environment
The ideal WebSphere development environment contains two distinct integration points within
the control of the development organization. The terms Studio Integration and Development
Integration Test will be used to distinguish these two processes, as shown in Figure 1. (For the
purpose of this paper, the author will assume that WebSphere Studio Application Developer is
the development tool of choice. Integration, therefore, will be done using WebSphere Studio
Application Developer. This same principal applies for other environments as well; when using
VisualAge™ for Java for development, VisualAge for Java should also be used for integration
testing.) Let’s review each of these integration points at a high level.
Figure 1. Ideal WebSphere Application Environment
Studio Integration
Within WebSphere Studio Application Developer, a team of developers will share a
development stream, a WebSphere Studio Application Developer term meaning the latest code
in the repository. Each developer updates his/her workspace with the shared code from the
stream. As they make changes to the code, the developers test their changes, then release the
changes to the shared stream. This is done almost continually, daily at a minimum. (Although,
technically speaking, each developer is performing integration as they create and test code, this
basic activity alone is not considered “integration” for the purposes of this paper.)
Then, on a periodic basis, someone is responsible for taking all of the latest released changes and
integrating them in WebSphere Studio Application Developer. A senior developer or technical
4
leader typically does this more formal level of integration. Important activities that occur at this
integration point include:
• All unit tests are executed across the entire application.
• When the application has passed all tests, it is then “versioned”.
It is recommended that this integration cycle occur at least once a week. A nice summary of the
release and version cycle is, Release frequently and version often.
Proponents of Extreme Programming talk about “continuous integration.” Although not always
explicitly mentioned in this paper, the concept of continuous integration is implied. Developers
must push their code to the team as quickly as possible, and should incorporate team changes
into their code as quickly as possible. While some of the principals of Extreme Programming
will be referenced in this discussion, a full explanation is beyond the scope of this paper. More
information about Extreme Programming can be found in the References section below.
Development Test Integration
Development Test Integration has two primary objectives:
• Flush out any differences between the development environment and the production
environment.
• Test the deployment of the application.
It is recommended that the development group has its own machine/node on which to deploy its
application.
This development environment should reasonably match the production
environment; at a minimum, it should have the same application server edition, version, etc., run
on the same operating system (including patches, etc.), and use the same database product. The
unit tests are run again in this environment.
In WebSphere Studio Application Developer 4.0, development will most likely be based on the
WebSphere Application Server 4.0 Advanced Single Server Edition, which is the test server that
is “inside” of WebSphere Studio Application Developer. A production server will most likely be
either WebSphere Application Server Advanced Edition or Enterprise Edition. Also, WebSphere
Studio Application Developer is typically run on Windows, while the production environment
will often be UNIX. The unit tests run on the platform used in Development Test Integration
will test any subtle differences between the WebSphere Application Server editions, as well as
any differences due to a different operating system.
Finally, the process to deploy the application into a WebSphere Application Server is also tested
at this stage. This test could be as simple as testing the installation instructions, or as complex as
creating and testing a script that will install the application. More on this later.
5
The Development Process
There are many similarities between any team development efforts. The basic concept is simple:
allow individuals to work and share that work. Ideally, this occurs with as little extraneous
activity as possible. Just as important is that the sharing occurs frequently. Any good Software
Configuration Management (SCM) system will allow this to happen. Before examining the
specifics of using CVS as an SCM from within WebSphere Studio Application Developer, let’s
look in general at application development using an SCM repository. (For the sake of
convenience, CVS terminology will be used here, even when discussing concepts common to
many SCM systems.)
The process of developing code (the word “code” can include any artifact required for the
application) at an individual desktop consists of starting with a working copy of the application,
or some piece of the application, making changes and then testing them locally. When the
changes are satisfactory and have passed the developer’s unit tests, the developer should then
“catch up” their local copy of the application with any changes that were made to the shared
copy in the repository. After the code passes the developer’s unit tests, it is released into the
shared repository. This release/catch up cycle will be discussed in detail later. Periodically, the
current code in the repository (the current release) is tested and versioned. This version becomes
a permanent, named copy of the application and is frozen. At any point in time we can look at,
build, or make modifications based on this frozen version of the application. The version itself is
read-only; any changes made to the version result in a new codeline.
A group of developers will share a main line of development, called a trunk. (WebSphere Studio
Application Developer uses the term “stream.” Both terms are used interchangeably throughout
this paper.) This trunk can be thought of as a current snapshot of the combined development
effort at a single point in time. This trunk, or base codeline, can have branches extending from
it. At some point, these branches may be merged back into the main trunk.
When should development branch from its main codeline? The short answer is: it depends.
However, the most important part of this answer is: only when it is allowed by the policy of the
organization. Each development group must have a policy for when it is acceptable to branch,
and also have a clear, standard naming convention for naming these code branches. Some
specific types of events will typically qualify for their own branch. For example, imagine a
developer receiving a feature request from a customer for a currently deployed version, while the
developer is working on a new version. Naturally, the individual situation will determine the
actual options available. However, one option might be to add the requested feature(s) to the
current application from the trunk. This approach is traditionally fraught with problems: the
trunk might not be a solid code set yet, it may give the customer features they shouldn’t have,
etc. The recommended option is to go back and add the feature(s) to the version that the
customer is running. This then becomes a branch off of the main codeline, which may or may
not be merged back in at some later time. The merge is a separate activity and is subject to a
separate decision, independent of the current development effort.
Two more good reasons to create code branches could include:
• wanting a developer (or developers) to work on a subproject apart from the main group.
6
•
experimental work to be merged back into the main codeline later.
The latter example would be one time when we would want to modify the code for
simplification or consistency, etc. This process is often called refactoring the code.. This gives
developers an opportunity to try some changes that may or may not be merged back into the
main codeline. If the new changes don’t perform well, fail unit tests, or break something else, all
is not lost. The trunk is still intact.
The decision to merge a code branch back into the main codeline should be made promptly.
This approach is called “The Flying Fish” approach: when a fish flies it doesn’t stay out of the
water for very long. Figure 2 illustrates this analogy.
Figure 2. Flying Fish Approach
Developing WebSphere applications using WebSphere Studio Application
Developer
WebSphere Studio Application Developer supports an optimistic locking model for SCM. Files
aren’t checked-out and locked while a developer works on them. Instead, each developer may
work on their own local copy of a file, and then at some point merge their changes into the
current copy of the file. (This paper assumes that this optimistic model is the one that the reader
will use.)
There are two approaches to using an SCM repository for a J2EE application. The first and most
obvious approach is to use the SCM repository as a place to store the source code of the
application, along with all of the other artifacts. The second approach is to store the output(s) of
the development process; that is, to store the EAR file(s) into the repository. These approaches
are not mutually exclusive, there is no reason you couldn’t do both. Storing the EAR will be
discussed later.
Figure 3 shows the structure of a WebSphere Studio Application Developer SCM repository.
Notice that each user has their own workspace which resides on their local disk. It is important
7
that this workspace not be shared. Do not create a Windows share for the file system where this
structure lives.
Figure 3. WebSphere Studio Application Developer Repository
It is important to understand the terms used within WebSphere Studio Application Developer for
team development. There are three terms that have a specific meaning in WebSphere Studio
Application Developer that could easily be confused with meanings in other SCM systems.
Release
Take your work in your workspace and send it to the repository.
Catch-Up
Take the repository contents and bring it into your workspace.
Merge Combine the changes between the stream and the workspace.
A fourth term, Synchronize encompasses the entire release, catch-up, merge process. Figure 4
shows the recommended process that a developer will use within WebSphere Studio Application
Developer. Inside the “Synchronize With Stream” box we are implicitly releasing changes we
made, and catching up with changes that were made by our teammates. The merge is shown
separately, because of the additional activity required. Merge requires looking at the code and
resolving any and all conflicts (changes made to workspace vs. changes made to repository).
8
Figure 4. Development using WebSphere Studio Application Developer
Let’s now look at some important points about this process when using CVS with WebSphere
Studio Application Developer.
Be certain that all machines within the development environment share the same current
time. This is crucial because all of the SCM activity is based on timestamps. It is nearly
impossible to perform these processes without all machines sharing the same time.
Releasing code should occur at the “lowest” possible code level. For example, a developer
who is responsible for several servlets might synchronize the entire Web application project with
the repository. However, it probably is not a good idea for the developer to release the entire
Web application to the repository, since it is very likely that another developer has made changes
to the stream while the servlet developer has been creating and modifying servlets. In Figure 5,
the servlet has been changed and may be released to the stream, but another developer has
modified a JSP. It also appears that another developer, in addition to our servlet developer, has
modified a servlet. Notice the three distinct icons for these three possible states:
•
•
•
Black arrow pointing right for release
Blue arrow pointing left for catch-up
Red diamond for conflict.
The resolution of the first two is simple. The developer should release his/her own work, and
catch-up with what was done by someone else. In the case of a conflict, the developer must stop
and actually look at the code. When the developer has ownership of the file, the developer may
make whatever changes are appropriate. This usually includes merging code from both sides.
When the developer has no authority over the code in conflict, then communication is needed.
(There is no explicit “ownership” or “authority” regarding files. These terms are used here in an
organizational sense.) Whether the developer owns the code in conflict or not, communication
must occur with all developers involved.
9
Figure 5. Synchronizing with the Stream
While the synchronize process may occur at a folder or package level, individual
developers will release only those items on which they have been working. Very simply, the
rule is “release what’s yours.” WebSphere Studio Application Developer will issue an alert
when the developer tries to release a resource that has a conflict or has changes to be caught-up.
Always take time to read these alerts. Ideally, the developer will not get these warnings because
the developer will only release code at the lowest appropriate level. Catch-up, on the other hand,
is generally performed at a higher level on the directory tree where, again, the developer must be
careful about common branches. Catching up a directory that contains many shared resources
and ignoring warnings could easily override important changes that could have been made along
the way. With the amount of coordination that’s necessary, the development process is always
smoother when a development team works together, sets guidelines and establishes a welldefined and clearly communicated process.
Use the SCM system to keep source code. The term “sourcecode” here means not only Java,
but also any artifact that is created for this application. It is important to discuss what shouldn’t
go into the SCM system:
•
•
Do not put compiled Java code into SCM. WebSphere Studio Application Developer
comes pre-configured to ignore anything that ends in .class. (See Window ->
Preferences -> Team -> Ignored Resources, *.class should be on the list.)
Do not put generated code into SCM. The most obvious J2EE example is the EJB code
that is generated by Generate -> Deploy and RMIC Code… As long as none of your
EJB source files begin with EJS or underline, setting Ignored Resources to EJS*.java and
_*.java will exclude generated EJB code.
Here the term “EJB source” is most subtle. EJB source is EJB code written by a developer, as
distinguished from Java source code that is generated by WebSphere Studio Application
10
Developer. Ignoring these EJB resources should be set on a per project basis. The only setting
for ignoring resources within WebSphere Studio Application Developer is global, since there is
no property sheet to allow you to configure resources to be ignored on a per project basis. This
setting must be given to CVS. WebSphere Studio Application Developer is file-based, and CVS
has a settings file that tells which resources do not get stored in the repository. This file is
.cvsignore, and when placed in a directory and released into CVS, it defines files to be ignored
for this directory and all directories below it. To enable this within a WebSphere Studio
Application Developer project, simply create a .cvsignore file and place it in the project folder.
The entries in the .cvsignore file are to be separated by white space, and it is recommend that
each entry be on a separate line.
WebSphere Studio Application Developer and the file system
Here is one guideline that will help every development team: the installation of WebSphere
Studio Application Developer and the location of workspaces should be consistent on every
developer’s machine. When installing WebSphere Studio Application Developer on Windows,
the default directory is
C:\Program Files\IBM\Application Developer\
This path should be exactly the same (including drive letter) on every machine where
WebSphere Studio Application Developer is installed.
It is also recommended that each Business Project have a separate workspace. (A Business
Project is a logical grouping of WebSphere Studio Application Developer projects. It could be
generalized that each application is a separate Business Project.) The workspace path would be
decided at the beginning of the development cycle and each individual working on that Business
Project would create a workspace following the path established. This will aid when metadata
is shared via the repository. WebSphere Studio Application Developer supports separate
workspaces. A separate workspace in indicated by a –data <workspace> on the command line
(or in the Windows shortcut definition) that invokes WebSphere Studio Application Developer,
where <workspace> is the path to the directory on the file system that WebSphere Studio
Application Developer will use for the workspace.
In most places, WebSphere Studio Application Developer uses relative rather than absolute file
paths. It is recommended that the development teams also use relative paths wherever possible.
Following the recommendations in this section will help minimize issues that arise in places
where WebSphere Studio Application Developer stores absolute file paths. In the WebSphere
Studio Application Developer Version 4.03, there are two common places where absolute file
systems paths may be seen: in the server configuration file, and in the application deployment
descriptor file, application.xml. As of version 4.02 and later of WebSphere Studio Application
Developer these absolute file paths are fixed when the files are edited. The developer is
informed that files paths are incorrect, and asked if they should be corrected.
Absolute versus Relative File Paths
Code Sample 1 shows the file that contains the build classpath for a WebSphere Studio
Application Developer project. This is one of the most pervasive types of meta data within
WebSphere Studio Application Developer. Note the entries of kind=”var”. These entries are
11
JAR files that are found on the file system but are external to the WebSphere Studio Application
Developer workspace. There are several classpath entries in Code Sample 1 that begin with
WAS_PLUGINDIR. This variable points to the WebSphere Application Server that is installed with
WebSphere Studio Application Developer, and is set during the WebSphere Studio Application
Developer installation. The significance of this to the developer is that this project can use
WebSphere Studio Application Developer run time classes for compilation without an absolute
reference to their physical location. The last var entry uses a variable that was defined after
WebSphere Studio Application Developer was installed. There is no need to define external
JAR files using an absolute path.
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="var" path="SERVERJDK_PLUGINDIR/jre/lib/rt.jar"
rootpath="src" sourcepath="SERVERJDK_PLUGINDIR/src.jar"/>
<classpathentry kind="src" path="ejbModule"/>
<classpathentry kind="var" path="WAS_PLUGINDIR/lib/j2ee.jar"/>
<classpathentry kind="var" path="WAS_PLUGINDIR/lib/csicpi.jar"/>
<classpathentry kind="var" path="WAS_PLUGINDIR/lib/ejbcontainer.jar"/>
<classpathentry kind="var" path="WAS_PLUGINDIR/lib/ivjejb35.jar"/>
<classpathentry kind="var" path="WAS_PLUGINDIR/lib/vaprt.jar"/>
<classpathentry kind="var" path="WAS_PLUGINDIR/lib/ras.jar"/>
<classpathentry kind="var" path="WAS_PLUGINDIR/lib/utils.jar"/>
<classpathentry kind="src" path="/OnlineMallWeb"/>
<classpathentry kind="lib" path="/OnlineMall/OnlineMallUtils.jar"/>
<classpathentry kind="var" path="EXT_UTILS/log4j.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
Code Sample 1. .classpath File from WebSphere Studio Application Developer
Generally, meta data for each project should be released to and caught-up from the SCM
repository. This process will be smooth as long as developers adhere to keeping installation and
workspace paths consistent across the team, and to using relative file references to items outside
of the WebSphere Studio Application Developer workspace. The WebSphere Studio
Application Developer project that contains information about the WebSphere test servers,
however, should be treated differently.
WebSphere Test Servers Project
There are two files that are used to configure a WebSphere Application Server 4.0 test server
(WebSphere v4.0 Test Environment or WebSphere v4.0 Remote Server): server-cfg.xml and
defaultInstance.wsi.
The WSI file may have a different file name and is unique to WebSphere Studio Application
Developer. For a remote server it describes where and how the application gets published to a
WebSphere Application Server other than the one that was installed with WebSphere Studio
Application Developer. The WSI file will have absolute file system paths in it and to the extent
they differ from one machine to another, this file should not be shared via the SCM repository.
The server-cfg.xml file is the configuration definition for WebSphere Application Server v4.0,
Advanced Single Server Edition. This file also contains the absolute paths, and generally should
12
not be shared via SCM. It is helpful for individual developers to version both of these files so
that they can track changes
There can be more than two of these files defined for any specific WebSphere Studio
Application Developer workspace. These files are put in a project that WebSphere Studio
Application Developer defines as a “server project”. A server project called “servers” is created
the first time you right-click a J2EE item and select, Run on Server…. It is recommended that
this project be renamed, or created by hand, and given a name that is specific to the application.
As part of the Studio Integration process described earlier, this project, containing the
WebSphere test server configuration files, will be created, maintained and versioned with the
application. The version process will be performed after the Studio Integration step has been
completed and all unit tests are passed, and will be done as part of versioning the application by
the same team lead that performs the integration. Team members may copy the versioned
servers project to their workspace, or use it to compare with their own test servers project.
New team members joining a project will need to copy the latest version of the WebSphere test
servers to their workspace. Other developers may have a need to modify the test environment on
their own workspace. The development team may or may not decide to put the server project
into the stream. Creating templates of the server configurations to share with other team
members is another approach for distributing server information.
Summary of Team Process
The team view of the development cycle discussed so far with regard to Studio integration is
shown in Figure 6. Next, we will discuss the process surrounding Development Test Integration.
13
Figure 6. WebSphere Studio Application Developer Macro Development Process
14
Outputs of the Development Process
This section describes the required deliverables to be produced by a development effort. What
matters from the development perspective is what the development group is required to produce.
Think of these requirements as a contract between development and operations. For our
purposes, “operations” is defined as anything that occurs with these deliverables after application
development. Although this paper might imply that the transition between development and
operations is a single point in time, this transition happens on a continuous basis
The EAR File
Within the J2EE 1.2 framework, the EAR file is the primary product created by developers,
consumed by operations, and is the focus of development. The emphasis on the EAR file as the
delivery mechanism for an application provides a delineation for the separation of concerns. The
EAR file contains a great deal of application configuration information that was specified by
administrators in earlier versions of WebSphere Application Server. For example, the URI
mappings for servlets are contained within the Web Application Archive (WAR) file that is part
of the EAR file. The URI is the part of the URL that is relative between parts of the application.
The hostname piece of the URL is set during application installation, as required within the J2EE
1.2 specification.
The situation for Enterprise Java Beans (EJBs) is comparable. Many of the attributes for EJBs,
such as transactional attributes as required by the EJB 1.1 spec or by IBM features such as
Optimistic Locking, are an integral part of the application. In previous versions of WebSphere
Application Server, these attributes were configurable from the console. However, since these
tightly coupled with the application and should be determined as part of a development effort,
they are now defined in the EJB JAR within the EAR file.
Although the J2EE 1.2 specification defines the use of an EAR file for an application server, the
spec may not address all of the issues surrounding application deployment. There may be parts
of an application that do not lend them into being put into a single monolithic EAR file. The
spec allows for a client JAR to live in an EAR file, but this can be very impractical. Imagine
installing an entire application, including Web Container and EJB container pieces on every
machine, which will have a standalone J2EE 1.2 Java client. First, the EAR file will be larger
than necessary. Second, it would be a security risk to expose the server-side code on all client
machines. Third, some of the server-side classes might cause classpath and upgrade problems.
It would be better just to put the client part of the application in its own EAR file.
Unfortunately, there is currently no tooling for doing this in WebSphere, but such a process
should be developed if there are to be standalone Java clients. This EAR file will need to contain
the EJB client components.
In addition, there may be parts of the application that will not be served by a J2EE-compliant
application server, and therefore cannot be put inside the EAR package. Currently it’s not clear
how static content will be served by a Web server directly from an EAR file. One possibility
would be to package this content with the EAR file, and then extract it as part of the install
process. It might also make sense to keep the content separate. In the latter case, this means that
development would deliver both an EAR file and something else. Some organizations might
15
find that their installation process should include creating an EAR file and a Tar file, which
contains content for the Web server. For other organizations this will not be an issue, with static
content being developed and managed separately from the J2EE application.
Finally, even within the J2EE 1.2 specification there are configurable items that fall within the
scope of operations. One obvious example is the webapplication virtual host. The goal is to
configure such items as part of the application installation process.
Additional Outputs from Development
The deployment process begins with development, since the developers know what resources are
required for the application to run. These requirements must be communicated as installation
instructions to operations either through documentation or scripts that will create the necessary
environment. If each application is unique enough to require a separate document and/or a
separate installation script, then it is a logical conclusion that responsibility for application
installation lies with development.
Development will provide to operations:
• an installation guide
• an EAR file
• installation scripts (optional).
The installation guide will cover what is new, important, etc. about the application and provide
crucial input for operations. The guide will tell operations what needs to be done, but it does not
need to be a WebSphere administration tutorial. It should be safely assumed that operations
understands the WebSphere administration process. For small applications, this should be
sufficient. For larger applications, it is better to provide a wscp script (or scripts) to install the
application automatically onto an existing application server, in addition to providing written
installation documentation.
Application deployment will be done at many stages during the life of the application: test,
production, updates, disaster recovery, etc. It is important to test the application installation as
part of the development integration test, as stated earlier. When the expected outputs are
understood and the process is followed, a contract between development and operations is
established. The development organization fulfills its obligation when they produce the required
output deliverables. Operations should not accept any application that doesn’t meet its
requirements.
Sample Installation Procedure
Let’s examine a sample procedure for a simple application. This application is designed to reflect
the typical applications we see, but is not meant to be all-inclusive. In your environment, you
will want to build on this example to support your specific applications and your specific
procedures.
This simple application has:
• three EJBs
• two Stateless Session EJBs
16
•
one Container Managed Persistence (CMP) Entity EJB.
HelloWorldEJB (one of the stateless session EJBs) calls PingEJB, which calls UserEJB (the
entity EJB). None of this is of much interest except that each EJB has a reference to the EJB it
calls, and the entity bean must have a datasource. The front end of our application is a servlet
called HelloServlet that calls the HelloWorldEJB. This means that the WAR file has a reference
to the HelloWorldEJB.
Take a look at Code Sample 2, part of the example application installation script. (The entire
script is shown in Appendix A).
# installApp learn4wscp c:/temp/learn4.ear default_host learn4App \
#
IBM-5DQQH9GVCF2 /ServerGroup:app2group/ jdbc/J2EELearnDB
#
proc installApp {appName earFile vhost jndiRoot node appServer dataSource} {
puts
puts
puts
puts
puts
puts
puts
puts
puts
puts
"================================================"
"Beginning install of $appName"
"input EAR file = $earFile"
"target vhost = $vhost"
"target node = $node"
"target app server or server group = $appServer"
"JNDI root for application = $jndiRoot"
"EJB CMP default datasource = $dataSource"
"================================================"
""
Code Sample 2. installApp.wscp
Taken by itself, this isn’t a particularly exciting piece of code, although it does illustrate what is
going to happen. The script requires seven pieces of information:
• The first two are basic: the names of the application and the EAR file.
• Two more parameters describe exactly where this application will be installed: the node
and application server. (Our installation assumes that the application server environment
for our application exists.)
• The virtual host is an installation-specific argument, even when the same virtual host
name is used by several applications, or across multiple instances of the same application.
This is clearly something that is determined by operations for the installation of each
application.
• The last two arguments are slightly different: JNDI Root and EJB DataSource.
Operations needs to be concerned with the organization of the global JNDI namespace.
This spans all applications within our WebSphere domain, possibly including multiple
versions of the same application. Management of the namespace is beyond the
reasonable scope of a development effort. However, operations does not really need to
care about the JNDI name of each EJB in our application. Development will have
created the bindings to map the local JNDI references into global JNDI references. The
script will take the JNDI argument given and create global JNDI names based upon the
naming convention being followed in this application.
############################################################
17
#Variables a developer would typically know
#
############################################################
set ejbjar "J2EELearnEJB.jar"
set war "J2EELearnWeb.war"
#EJBs referenced by the WAR
set warrefs "HelloWorldEJB"
#EJB binding and reference information.
#Each list within this list has the following form:
#
EJBNAME EJBREF .... EJBREF
#The EJBNAME is the name of an EJB that should be bound into the
#namespace. The remaining elements are the names of EJBs that are
#referenced by this EJB.
#It is assumed that EJBs are bound into the global namespace
#as $jndiRoot/ejb/EJBNAME and that references to other EJBs are always
#bound into the local namespace as java:comp/env/ejb/EJBREF.
set ejbs [list \
[list HelloWorldEJB PingEJB] \
[list PingEJB UserEJB] \
[list UserEJB] \
]
Code Sample 3. excerpt from installApp.wscp
The interesting part of Code Sample 3 is what the script does not require for arguments. Four
pieces of information are shown, all known by the developer and created as part of the definition
of the application:
• The name of the two files contained in the EAR: the WAR and the EJB JAR files.
• Two types of EJB bindings: The first is the WAR binding to an EJB, which will be used
by the HelloServlet to find the HelloWorldEJB. The second type of EJB references is
actually within the EJB JAR. Here, each EJB reference is per EJB, so the
HelloWorldEJB has a reference to the Ping EJB, and the PingEJB references UserEJB.
An EJB can have more than one EJB reference, or, in other words, an EJB can be a client
to more than one EJB.
Operations doesn’t need to be aware of these EJB references, but they are used as part of the
application installation based on the root JNDI which is passed to the script, as shown above.
This script alone is not enough to create a complete WebSphere environment that will be ready
to run the application. In fact, the application environment must exist before the script will
work. Our installApp.wscp script works with a set of supporting scripts. Code Sample 4 shows
a portion of ops.wscp , a script that will ultimately invoke the installApps script to perform the
application installation. (The entire source of ops.wscp appears in Appendix B.) Notice how
ops.wscp sets variables that will be used to create the application server environment where this
application will run. Session database information, database drivers, specific database classes,
nodes, file system paths, and other details (that we can safely assume already exist) will all be
used to create the environment.
The installApps script neatly completes the development
group’s responsibility to the operations organization.
#
# Created 2002-April-05 by Keys Botzum, IBM Software Services
#
#This script is typically edited by operations to define their
18
#specific environment. It sources the utils and installApp scripts.
##################################################
#Variables to be set by operations
##################################################
set WASROOT "c:/websphere/appserver"
set NODES {IBM-5DQQH9GVCF2}
set SESSIONDB mydb
##database JDBC JAR files
#Most DB JARS need to be visible to the WAS classloader, so they are in lib/ext
set DB2JAR "c:/PROGRAM FILES/SQLLIB/java/db2java.zip"
set ORACLEJAR "$WASROOT/lib/ext/classes12.zip"
#session mgr stuff
set SESSIONDS session
set SESSUSER was
set SESSPASS was
#The list below is optimized for safety, not peformance. Same as medium in
#admin console. Have also disabled allowing overflow.
set SESSATTRS [list "TuningWriteInterval 0" "TuningWriteFrequency 0" \
"TuningWriteContents 0" "TuningInvalidationTimeout 30"\
"TuningScheduleInvalidation true" \
"EnablePersistentSessions true" \
"TuningAllowOverflow false"\
"PersistentDatasourceName $SESSION" \
"PersistentUserId $SESSUSER"\
"PersistentPassword $SESSPASS"]
##################################################
#internal variables unlikely to be changed
set DB2CLASS "COM.ibm.db2.jdbc.DB2ConnectionPoolDataSource"
set ORACLECLASS "oracle.jdbc.pool.OracleConnectionPoolDataSource"
##################################################
Code Sample 4. Excerpt from ops.wscp Script
#virtual hosts
createVirtualHost vhost2 {www.other.com:80 www.other.com:443}
createAppServer IBM-5DQQH9GVCF2 foo $SESSATTRS 128 9099
}
proc installApps {} {
installApp learn4wscp c:/temp/learn4.ear default_host learn4App \
IBM-5DQQH9GVCF2 /ServerGroup:foogroup/ jdbc/J2EELearnDB
}
Code Sample 5. Excerpt from ops.wscp Script
19
Application Packaging
One of the most important aspects of application development is what pieces should be packaged
into an EAR file, and how. There are two important considerations here. First, the packaging of
your application should follow the application design itself. Second, the packaging of the EAR
file should begin at the very beginning of the development process.
Application packaging must be architecturally driven, but not topologically driven. If
architecturally driven, the EAR package should be a logical outgrowth of how the application is
built and how the pieces might be reassembled into other applications. To avoid being
topologically driven, development must defer decisions about the physical topology environment
for this application. For example, the application packaging must be flexible enough so that the
Web module and EJB module do not need to be installed on the same node. This would lead to
the creation of an EJB client JAR file, which would contain the stubs necessary for a client to
reference the EJBs in the EJB JAR file. The EJB client JAR could be bundled with the EAR file
separately.
This idea of modularity is nothing new. A modular structure within the application helps
promote packaging that is architecturally driven. We assume that part of the development effort
is centered on defining the application and any components that it shares with other applications.
It is wise to avoid “open” classes in the WAR file. These elements should be put in their own
JAR file, promoting reuse, and aiding in repackaging the application. The EJB JAR file should
contain only EJB code; no supporting classes should be contained within the EJB JAR file. The
potential downside to this strong recommendation is that some code, belonging solely to the EJB
JAR file, may be exposed.
WebSphere Studio Application Developer zipCreation Utility
When creating an application with a modular structure, some code should be placed in a project
other than the Web Project, EJB project or Application project. This code could be placed in a
Java project, or “utility” project. The J2EE packaging would suggest that this code be included
in the EAR when the EAR file is created. Within WebSphere Studio Application Developer,
there is no automated way to take code that is in a utility project and include it as part of the
EAR file. This can be done by hand. Export the utility project as a JAR, and then import that
utility project into the Enterprise project. This manual process must be repeated every time the
code in the utility project changes. The mechanics of including the utility project as part of the
enterprise application can be automated by making use of Tim de Boer’s zipCreation WebSphere
Studio Application Developer plug-in. (See References) This utility automatically creates and
maintains a JAR file within one WebSphere Studio Application Developer project by zipping up
the contents of another project. There are a many uses for this plugin, but the most common is to
create a Java project, which will be put into a JAR file and added to the new project. This allows
development of Java utility classes in a separate Java project, but for packaging purposes these
classes automatically become part of the EAR file that is exported. These utility JARs can then
be referenced via the manifest file classpath for any other modules contained in the EAR.
20
While the J2EE specification determines much of the contents of the EAR file, there are several
items in the EAR file that are specific to WebSphere. These items fall into two main categories,
bindings and extensions, and are easily recognized as xmi files with an ibm- prefix: IBM
extensions are in ibm-[type]-ext.xmi files, while bindings are in ibm-[type]-bnd.xmi file,
where [type] is the type of package. These files must be put into your SCM system because
they are part of the application source.
Figure 7 shows extension and binding files for a client JAR file, an EJB JAR file, a WAR file,
and the EAR file. For CMP Entity EJBs, there are two additional files, schema.dbxmi and
map.mapxmi, which specify information about the EJB mapping to the database.
When using
WebSphere Studio Application Developer, these files are created when using a “meet-in-themiddle” mapping strategy. The EJBDeploy process will generate code from these files that
handles the updates, inserts, deletes, etc. to the database. This code is called persister code. In
the case where these files do not exist, such as in a “top-down” mapping strategy, the EJBDeploy
tool will generate these files. It is important that these files be treated as source files, as they are
an integral part of the CMP Entity EJB definition.
Figure 7. Contents of an EAR file
Figure 8 shows the structure of an application.
This application roadmap is critical at the
beginning of the development lifecycle to level-set the development team on what they are
building and how it all fits together. Imagine the development process, as we have described it
so far, without project names already defined within WebSphere Studio Application Developer,
or developers not knowing which project contains their classes.
Some of what appears in Figure 8 is prescribed by J2EE, but much is not. Utility classes
(ServiceFactoryPatterns, ApplicationUtils, ApplicationBusinessLogic) have been put into their
21
own JAR files. ApplicationBusinessLogic.jar is shown as residing in the EJB JAR file, but this
is only a logical representation; in WebSphere Application Server 4.0, the EJB container does
not recognize a JAR file inside the EJB JAR file.
In Figure 8, notice also that there is a dependency between the WAR file and the EJB JAR file.
As mentioned previously, this may have implications for our application. The WAR module will
be a client of the EJB module. When the Web module and the EJB module are not running in
the same process, EJB client stubs must be available for the WAR to resolve its references.
Figure 8. Application Packaging
Figure 9 shows the application as it will be installed into the application server. In a sample or
prototype application, this diagram below would be trivial, but Figure 9 is not trivial. By
showing the application in the required environment, the supporting JAR files are also shown.
Care must be given to not only what goes into the application, but to what is outside of the
application as well. One general rule comes into play here: The EAR file should be the
application. That is, everything required for this application should be contained in the EAR
file. Should there ever be a need to pass the application to another group, sending one allinclusive file is far more desirable than sending several with the possibility of being incomplete.
Application Environment
What about all of the support files that are external to the EAR file? These are to be considered
part of the environment that is required to support the application. In Figure 9 there are several
JAR files that belong to third-party applications. Additionally, these files could be shared across
multiple applications. DB2 and MQSeries, for example, are likely to be used by more than one
application. The CompanyUtil.jar file, which could contain a common framework for building
all applications, could also be shared across multiple applications. Shared code of this nature
poses a concern. When CompanyUtil.jar is updated, all applications using it could be affected,
possibly causing unwanted side effects. This common concern should not necessarily prevent us
from sharing the file, but we need to make sure we understand the risks.
22
Figure 9. EAR Installed into Application Server
Property Files
One interesting problem not directly addressed in the J2EE spec is how to package property files.
A property file is typically used to allow easy modification of an application’s attributes. The
key here is that we need to expect that the properties of an application may change after the
application has been installed. Placing property files inside of the EAR file makes them more
difficult to edit.
This paper has rigorously ignored the “hotdeploy” feature of the WebSphere Application Server,
which allows modification of files within the installedApps directory. While there are situations
where this feature is handy, it must be used carefully. We have described the EAR file as the
primary input to application installation. By modifying items in the installedApps directory, the
original EAR file no longer matches the installed application. This defeats the purpose of
checking the EAR file into SCM, as described earlier. Therefore, putting property files into the
EAR file and modifying them after installation is less than optimal. Note, however, that in
situations where the property files are rarely modified, or where files names would conflict
between applications, it might make sense to put the property files inside the EAR file. In
general, though, modifying anything in the installedApps directory should be avoided.
The question remains, where to put property files? One approach would be to create a separate
standard Java property file for each application (following a standard naming convention) and
place that file on the classpath so it can easily be found by the classloader. This could be done
by putting it in <was>/lib/app, where <was> is the WebSphere Application Server installation
path. Another option would be to put the property file on the JVM classpath. Operations would
be able to easily edit the file (outside of the EAR file) as needed. This has the nice side benefit of
preserving the property file contents when a new EAR is delivered. Of course, if new properties
are added, this process becomes more complicated.
23
Automated Build Process
This paper has described the benefits of maintaining a clear line of responsibility between
development and operations. The ability to reinstall an application from a known point in time
or from a known code base is important to operations. These arguments might lead some to the
conclusion that operations should have responsibility and control over the build process pulling
source code from the SCM, and recreating the EAR file. While pulling a versioned copy of the
source into a WebSphere Studio Application Developer workspace and exporting an EAR file
certainly can achieve this, from an operations perspective it would be better to have a build
process that is external to the development tool. However, placing the build process entirely in
the hands of operations relieves development from an important responsibility.
It would be much more effective to give operations control of the process after development has
fulfilled their contract. It is therefore recommended that consideration be given to placing the
development deliverables into an SCM repository that is within the control of operations. This
way, any particular build that has been deployed can be recovered at any time should this be
necessary. It is ideal when this task is be done by (and is within the control of) operations to
maintain the integrity of the delivered code at all levels.
There is no compelling technical reason to have a separate build process when the development
group is using WebSphere Studio Application Developer, since an automated build process for
J2EE applications is included within WebSphere Studio Application Developer. When it is used
as described here, control of source code (and other application artifacts) is maintained and the
build output is easily extracted. However, some organizations may desire a build process that is
external to the primary development tool. The following section addresses this be describing
how to create ANT scripts to build a J2EE project.
ANT - The Java Build Language
ANT is a scripting language that was made for creating Java builds. Part of the Jakarta open
source project, ANT scripts can be invoked from a command line. (See References for
information on Jakarta/ANT.) Development organizations that use WebSphere Studio
Application Developer, can (and should) create ANT scripts inside WebSphere Studio
Application Developer. ANT scripts can also be invoked directly within WebSphere Studio
Application Developer. Figure 10 shows an ANT script (build.xml), being invoked. The right
side of the figure shows the selection of the targets in the ANT build.
When using ANT as the build tool and WebSphere Studio Application Developer as the
development tool, it makes sense to build and test the ANT scripts within WebSphere Studio
Application Developer. This allows the scripts to be built and tested as part of the development
cycle. The ANT scripts can then be put under version control with all of the other pieces of the
application.
24
Figure 10. Running ANT within WebSphere Studio Application Developer
An ANT script must be able to rebuild an entire application from the Java source code alone.
This means that although WebSphere Studio Application Developer has the ability to compile
Java code incrementally as it is saved, this feature should be disabled so that the ANT script can
be fully tested within WebSphere Studio Application Developer. Remember, though, that the
script must also be tested outside of WebSphere Studio Application Developer to meet the final
objective.
A collision occurs between ANT and the WebSphere Studio Application Developer workbench
when compiling Java source code within WebSphere Studio Application Developer. WebSphere
Studio Application Developer uses its own internal Java compiler, whereas ANT invokes the
javac executable. This conflict results in the following error message, when executing a javac
ANT tag within WebSphere Studio Application Developer:
BuildException: [path]\build.xml:27: Cannot use classic compiler, as it is not
available A common solution is to set the environment variable JAVA_HOME to your jdk
directory.
The solution is to add an attribute telling ANT what Java compiler to use. This is achieved by
using the tag shown in Code Sample 6, before the javac tag is executed. This tag will set a
compiler attribute that is specific to WebSphere Studio Application Developer only when the
ANT script is executed from within the WebSphere Studio Application Developer workbench.
The tag provides a conditional test for setting the compiler attribute: when the
org.eclipse.core.launcher.Main class is available, the Java compiler attribute is set. Outside of
WebSphere Studio Application Developer this class is not available, so the attribute is not set.
By using the tag shown in Code Sample 6, the same ANT script will work both inside and
outside the WebSphere Studio Application Developer environment.
<available
classname="org.eclipse.core.launcher.Main"
property="build.compiler"
value="org.eclipse.pde.internal.core.JDTCompilerAdapter"
25
classpath="${java.class.path}"/>
Code Sample 6. ANT tag for using javac in WebSphere Studio Application Developer
Another benefit of creating and testing ANT scripts from within WebSphere Studio Application
Developer occurs almost by accident. This benefit is the natural tendency developers have to
create ANT build scripts that correspond to each project within WebSphere Studio Application
Developer. It is likely that a developer will want to create one script to build the EJB JAR file
from the EJB project, another script to create a WAR from the Web Application project, and then
another script to create an EAR file for the Enterprise Application project. These ANT build
scripts are then nested such that the EAR building script invokes the scripts that produce the EJB
JAR file and the WAR file. This nesting of ANT scripts is simple to do and is an excellent way
to apply organization and modularity to a set of scripts that will build an entire J2EE application.
Note that it is recommended that ANT scripts be organized in this manner regardless of the tool
being used for development. This modularity of scripts allows the components to be built
separately and improves the ability to reassemble the parts into a different application.
Let’s examine some sample ANT build scripts that demonstrate this hierarchy. (The complete
listing of these ANT scripts is show in Appendix D.) Code Sample 7 shows an ANT build script
that takes all of the EJB classes, compiles them, generates container code (“deploying” them),
and then assembles the resulting Java byte-code into a JAR file. This code snippet shows the
build invoking the EJBDeploy utility. In this script, the actual filename of EJBDeploy is held in
the variable ${ejbdeploy} so the script can be run in a Windows environment with:
<property name="ejbdeploy" value="${was.home}/bin/ejbdeploy.bat"/>
or on a UNIX platform with:
<property name="ejbdeploy" value="${was.home}/bin/ejbdeploy.sh"/>
The value of the ejbdeploy property could be set with a tag (as shown here) at the top of the
ANT script, or it could be set via a command line argument went the ANT script is invoked.
Additionally, the exec tag could make use of the os attribute to test for specific operating
systems. However, it might be easier to just change a property when the script is moved to a
different platform.
<target name="deployed.ejb.jar" depends="ejb.jar">
<echo>.......Generating deploy code for ${ant.project.name}</echo>
<exec executable="${ejbdeploy}" output="ejbdeploy.log">
<arg value="${ejb.jar.dest}/${ejb.jar}"/> <!-- undeployed jar -->
<arg value="${deploy.working.dir}"/> <!-- working dir -->
<arg value="${ejb.jar.dest}/${deployed.ejb.jar}"/> <!-- deployed jar -->
<arg value="-cp ${util.jar.dir}/${utils.jar}"/>
<arg value="-dbvendor ${dbvendor}"/>
<arg value="-trace"/>
</exec>
<echo>.......Finished generating deploy code for ${ant.project.name}</echo>
</target>
Code Sample 7. Excerpt from EJBBuild.xml ANT script
26
Code Sample 8 shows an ANT script, which builds an EAR file for our application. It also
invokes the scripts that build the EJB JAR file and the WAR file.
<!-- Before Creating the EAR, we need to update the WAR & EJB Jar -->
<target name="WARBuild">
<echo message=".....Invoking WAR Build"/>
<ant antfile="${basedir}/MadMoneyWeb/warbuild.xml" target="war"/>
</target>
<target name="EJBBuild">
<echo message=".....Invoking EJB JAR Build"/>
<ant antfile="${basedir}/MadMoneyEJBs/ejbbuild.xml" target="ejb.jar"/>
</target>
Code Sample 8. Excerpt from EARBuild.xml
Code Sample 9 shows the creation of the WAR file for a J2EE 1.2 application. Notice how the
war target depends on the warcompile target.
This illustrates how the entire build is
accomplished from the Java source files.
<target name="warcompile" depends="init,utils.build,ejb.build">
<echo>.....Compiling ${ant.project.name} web Java source.</echo>
<javac srcdir="${web.java.src}"
destdir="${web.classes.dest}"
classpathref="local.classpath"
/>
<echo>.....Finished compiling ${ant.project.name} web Java source.</echo>
</target>
<target name="war" depends="warcompile">
<!-- build a war file from the war directory tree -->
<echo>.....Building ${ant.project.name} WAR file.</echo>
<war basedir="${war.src.tree}" warfile="${war.dest}/${warfile}"
webxml="${war.src.tree}/WEB-INF/web.xml">
</war>
<echo>.....Finished building ${ant.project.name} WAR file.</echo>
</target>
Code Sample 9. Excerpt from WARBuild.xml
27
The preDeploy Tool
Note: The preDeploy tool, as described her, is a technology preview and is unsupported by IBM.
Information about where to obtain the preDeploy tool can be found in the References section.
The preDeploy tool can perform several useful functions not available through any other
WebSphere 4.0 command line tool. A development group that uses WebSphere Studio
Application Developer might not have a need for command line based tools such as preDeploy.
The preDeploy tool would be most useful in an environment where all of the builds are done
external to WebSphere Studio Application Developer. (In this section, the term “deploy” is used
in accordance with the EJB 1.1 specification terminology. That is, “deploying EJBs” means
generating container-specific code for each EJB.)
One use for preDeploy is to create the IBM binding files. Several switches provide control over
creating application bindings. One strategy is similar to what was shown in the wscp script,
above. Each EJB JNDI entry is bound into the namespace with a prefix that is specified when
invoking preDeploy. There is no need to change the JNDI name of each EJB, just the root of the
namespace where each EJB is bound.
Another feature of preDeploy is that it can invoke EJBDeploy. This may not seem very useful,
until you realize that preDeploy can deploy all the EJBs within an EAR file. EJBDeploy can
only deploy EJBs within a single EJB JAR file. Without using preDeploy, each EJB JAR would
need to be deployed separately before it is bundled into the EAR file.
The preDeploy tool can fill some gaps for development groups not using WebSphere Studio
Application Developer.
28
Summary
The development process is just one part of a larger cycle of activities. In this paper, we have
emphasized the need for a consistent, well-planned environment for development. The ideal
WebSphere development environment also includes a thoughtful approach to operations and
testing frameworks. More information about these topics can be found in the white papers Best
Practices: Operations by Peter Van Sickel, and Best Practices: WebSphere Application Testing
by Jeff Garratt.
29
Appendix A
Sample wscp application installation script : installApp.wscp
#
# Created 2002-April-05 by Keys Botzum, IBM Software Services
#
#This WSCP script installs a WAS application into an existing
#WAS environment. It is assumed that the application is
#packaged into an existing EAR file and that certain infrastructure
#components have been prepared in advanced (see below).
############################################################
#These are environment specific variables that the operations team
#would typically provide.
############################################################
#things that need to be created in advance by operations
#set vhost default_host
#set node IBM-5DQQH9GVCF2
#for an app server
#set appServer "/Node:$node/ApplicationServer:Default Server/"
#for a server group
#set appServer "/ServerGroup:app2group/"
#set dataSource "jdbc/J2EELearnDB"
#names to be used by the application
#set earFile c:/temp/learn4.ear
#set appName learn4wscp
#set jndiroot "learn4App"
#example usage
#
# installApp learn4wscp c:/temp/learn4.ear default_host learn4App \
#
IBM-5DQQH9GVCF2 /ServerGroup:app2group/ jdbc/J2EELearnDB
#
proc installApp {appName earFile vhost jndiRoot node appServer dataSource} {
puts
puts
puts
puts
puts
puts
puts
puts
puts
puts
"================================================"
"Beginning install of $appName"
"input EAR file = $earFile"
"target vhost = $vhost"
"target node = $node"
"target app server or server group = $appServer"
"JNDI root for application = $jndiRoot"
"EJB CMP default datasource = $dataSource"
"================================================"
""
#This wscp script is not entirely generic. It makes several simplifying
#assumptions that are dependent on the application design. These
#assumptions are generally followed so this shouldn't be a major problem.
#
#assumes one WAR and one EJB JAR.
#assumes EJB binding information is rooted at 'ejb/' in both the global
# and local namespace.
#assumes the entire EAR is installed to one app server
############################################################
#Variables a developer would typically know
#
30
############################################################
set ejbjar "J2EELearnEJB.jar"
set war "J2EELearnWeb.war"
#EJBs referenced by the WAR
set warrefs "HelloWorldEJB"
#EJB binding and reference information.
#Each list within this list has the following form:
#
EJBNAME EJBREF .... EJBREF
#The EJBNAME is the name of an EJB that should be bound into the
#namespace. The remaining elements are the names of EJBs that are
#referenced by this EJB.
#It is assumed that EJBs are bound into the global namespace
#as $jndiRoot/ejb/EJBNAME and that references to other EJBs are always
#bound into the local namespace as java:comp/env/ejb/EJBREF.
set ejbs [list \
[list HelloWorldEJB PingEJB] \
[list PingEJB UserEJB] \
[list UserEJB] \
]
#############################################################
#
#Now calculate several variables based on the input
#
############################################################
#virtual host for WAR
set virtual [list [list $war $vhost] ]
##references for the WAR to EJBs
set ejbrefs {}
foreach ref $warrefs {
set oneref [list ${war}::ejb/$ref $jndiRoot/ejb/$ref]
lappend ejbrefs $oneref
}
#now, compute the bindings & references variables for EJBs
#this variable is used to define references in the WAR to EJB JNDI names
set ejbbnds {}
foreach ejbpart $ejbs {
set ejbname [lindex $ejbpart 0]
set refs [lrange $ejbpart 1 end]
#
puts $ejbname
#
puts $refs
set onebnd [list ${ejbjar}::$ejbname $jndiRoot/ejb/$ejbname]
lappend ejbbnds $onebnd
if { [llength $refs] > 0 } {
set onerefs {}
foreach ref $refs {
set oneref [list ${ejbjar}::${ejbname}::ejb/$ref $jndiRoot/ejb/$ref ]
lappend ejbrefs $oneref
}
}
}
#puts ""
31
#puts "binding information: $ejbbnds"
#puts ""
#puts "Reference information: $ejbrefs"
############################################
#Install the app
###########################################
EnterpriseApp install /Node:$node/ $earFile -appname $appName -modvirtualhosts
$virtual -defappserver $appServer -ejbnames $ejbbnds -ejbreferences $ejbrefs ejbdatasources [list [list $ejbjar $dataSource]]
puts "Installation Completed Successfully"
puts ""
}
32
Appendix B
Sample wscp operations setup script : ops.wscp
#
# Created 2002-April-05 by Keys Botzum, IBM Software Services
#
#This script is typically edited by operations to define their
#specific environment. It sources the utils and installApp scripts.
##################################################
#Variables to be set by operations
##################################################
set WASROOT "c:/websphere/appserver"
set NODES {IBM-5DQQH9GVCF2}
set SESSIONDB mydb
##database JDBC JAR files
#Most DB JARS need to be visible to the WAS classloader, so they are in lib/ext
set DB2JAR "c:/PROGRAM FILES/SQLLIB/java/db2java.zip"
set ORACLEJAR "$WASROOT/lib/ext/classes12.zip"
#session mgr stuff
set SESSIONDS session
set SESSUSER was
set SESSPASS was
#The list below is optimized for safety, not peformance. Same as medium in
#admin console. Have also disabled allowing overflow.
set SESSATTRS [list "TuningWriteInterval 0" "TuningWriteFrequency 0" \
"TuningWriteContents 0" "TuningInvalidationTimeout 30"\
"TuningScheduleInvalidation true" \
"EnablePersistentSessions true" \
"TuningAllowOverflow false"\
"PersistentDatasourceName $SESSION" \
"PersistentUserId $SESSUSER"\
"PersistentPassword $SESSPASS"]
##################################################
#internal variables unlikely to be changed
set DB2CLASS "COM.ibm.db2.jdbc.DB2ConnectionPoolDataSource"
set ORACLECLASS "oracle.jdbc.pool.OracleConnectionPoolDataSource"
##################################################
source installApp.wscp
source utils.wscp
puts "Environment Set...."
#before running this, a basic install must be completed on every
#node and (optionally) security configured.
proc doglobal {} {
global DB2CLASS DB2JAR ORACLECLASS ORACLEJAR SESSIONDB NODES SESSIONDS
#create common JDBC drivers - rarely app specific
createJDBCDriver DB2Driver $DB2CLASS $DB2JAR $NODES
createJDBCDriver OracleDriver $ORACLECLASS $ORACLEJAR $NODES
#create common session database (might prefer to be app server specific)
createDB2DataSource $SESSIONDS DB2Driver $SESSIONDB
#createOracleDataSource SESSION OracleDriver
"jdbc:oracle:thin:@localhost:1521:db"
33
#
#virtual hosts
createVirtualHost vhost1 {www.big.com:80 www.big.com:443}
createVirtualHost vhost2 {www.other.com:80 www.other.com:443}
createAppServer IBM-5DQQH9GVCF2 foo $SESSATTRS 128 9099
createServerGroup foogroup IBM-5DQQH9GVCF2 foo
cloneServerGroup foogroup IBM-5DQQH9GVCF2 fooclone1
}
proc installApps {} {
installApp learn4wscp c:/temp/learn4.ear default_host learn4App \
IBM-5DQQH9GVCF2 /ServerGroup:foogroup/ jdbc/J2EELearnDB
}
34
Appendix C
Sample wscp operations utility script : util.wscp
#
# Created 2002-April-05 by Keys Botzum, IBM Software Services
#
#This script defines a number of activities that will most
#likely be done before specific applications are installed.
#There is some ambiguity in this as some activities can be
#for a specific application or shared across applications.
#
#Create App Server
#
# TBD - needs to create HTTPS stuff.....
proc getAppAttrs {name sessionattrs maxmem port} {
set minmem [expr $maxmem / 2]
set tattrs [list "Port $port"]
set wattrs [list [list SessionManagerConfig $sessionattrs]\
[list Transports $tattrs]]
set jattrs [list "InitialHeapSize $minmem" "MaxHeapSize $maxmem"]
#1=application visibility
set attrs [list [list WebContainerConfig $wattrs] \
[list ModuleVisibility 1] \
[list JVMConfig $jattrs]]
return $attrs
}
proc createAppServer {node name sessionattrs maxmem port} {
ApplicationServer create "/Node:$node/ApplicationServer:$name/" \
-attribute [getAppAttrs $name $sessionattrs $maxmem $port]
}
#
#Create Server Group
#
proc createServerGroup {name basenode baseapp} {
ServerGroup create "/ServerGroup:$name" \
-baseInstance "/Node:$basenode/ApplicationServer:$baseapp/"
}
#
#Clone Server Group
#
proc cloneServerGroup {name node clonename} {
ServerGroup clone "/ServerGroup:$name/" -node "/Node:$node/" \
-cloneAttrs [list "Name $clonename"]
}
#TBD - why doesn't this work????
#proc createServerGroup {name sessionattrs maxmem port} {
#
set attrs [list EJBServerAttributes [list [getAppAttrs $name $sessionattrs
$maxmem $port]]]
#
ServerGroup create "/ServerGroup:$name/" -serverGroupAttrs $attrs
#}
#
35
# remove app
#
proc removeApp {appName} {
EnterpriseApp remove $appName -recursive
}
#
#create Virtual Host
#
proc createVirtualHost {name aliases} {
VirtualHost create "/VirtualHost:$name/" \
-attribute [list [list "AliasList" $aliases]]
}
#
#create JDBC driver
#
proc createJDBCDriver {name class jar nodes} {
set longname "/JDBCDriver:$name/"
JDBCDriver create $longname -attribute [list "ImplClass $class"]
#install on all nodes (assuming identical)
foreach node $nodes {
JDBCDriver install $longname -node /Node:$node/ -jarFile $jar
}
}
#
#create datasource
#
#assumes naming convention that name is NAME and JNDI name is jdbc/NAME.
#intentionally don't use valid user & password. Not very secure.
proc createOracleDataSource {name jdbcdriver url} {
createDataSource $name $jdbcdriver \
[list "URL $url" "user none" "password none"]
}
proc createDB2DataSource {name jdbcdriver dbname} {
createDataSource $name $jdbcdriver [list [list databaseName $dbname]]
}
proc createDataSource {name jdbcdriver attrs} {
set a [list [list ConfigProperties $attrs] \
[list JNDIName jdbc/$name]]
DataSource create "/JDBCDriver:$jdbcdriver/DataSource:$name/" \
-attribute $a
}
#to prepare for an app
#create app server or server group
#configure session manager
#create & install jdbc driver (is install needed???)
#create datasource (don't specify password for DB)
36
Appendix D
Sample ANT build script : ejbbuild.xml
<?xml version="1.0"?>
<!-- Created 2002-Mar-28 by Peter Van Sickel of IBM Software Services -->
<project name="MadMoneyEJBs" default="deployed.ejb.jar" basedir=".">
<description>
This buildfile is for creating the MadMoneyEJBs.jar file.
It is set up based on the structure of a file system export from
a WSAD project of the same name.
This project has the MadMoneyUtils build and packaging as a
subproject.
In the long run, this should be combined with using CVS and checking
the files out of CVS that will be used in the build.
Need to pass $TEMP in to ant so ejbdeploy has a working directory.
-DTEMP=$TEMP
One of the difficult tasks in setting this up was massaging the
EJB-RDB mapping files that come out of WSAD into something that can
be used with ejbdeploy from the command line.
</description>
<!-- Software locations -->
<property name="was.home" value="w:/was401ae"/>
<!-- properties defining the project tree structure -->
<property name="ejb.base" value="${basedir}/MadMoneyEJBs"/>
<property name="ejb.src" value="${ejb.base}/ejbModule"/>
<property name="ejb.bin" value="${ejb.base}/bin"/>
<property name="build.dir" value="${ejb.base}/bin"/>
<property name="ejb.jar.dest" value="${basedir}/modules"/>
<property name="ejb.jar" value="MadMoneyEJBs.jar"/>
<property name="util.jar.dir" value="${basedir}/modules"/>
<property name="utils.jar" value="MadMoneyUtils.jar"/>
<!-- cleaning EJB-RDB map file related properties -->
<property name="cleanEJB-RDBMaps" value="${basedir}/cleanEJB-RDBMaps.bsh"/>
<!-- ejbdeploy related properties -->
<property name="ejbdeploy" value="${was.home}/bin/ejbdeploy.bat"/>
<property name="deployed.ejb.jar" value="Deployed_${ejb.jar}"/>
<property name="deploy.working.dir" value="${basedir}/temp"/>
<property name="dbvendor" value="DB2UDBWIN_V72"/>
<!-- Jars, classes needed
I need to clean the EJB code of the "link" stuff that
relies on VAJ classes.
-->
<path id="local.classpath">
<pathelement location="${was.home}/lib/j2ee.jar"/>
<pathelement location="${was.home}/lib/csicpi.jar"/> <!-- container support -->
<pathelement location="${was.home}/lib/utils.jar"/> <!-- com.ibm.ejs stuff -->
<pathelement location="${was.home}/lib/ejbcontainer.jar"/>
<pathelement location="${was.home}/lib/ivjejb35.jar"/> <!-- VAJstuff -->
<pathelement location="${was.home}/lib/vaprt.jar"/> <!-- persistence -->
37
<pathelement location="${was.home}/lib/ras.jar"/> <!-- WAS Trace -->
<pathelement location="${util.jar.dir}/${utils.jar}"/> <!-- MadMoneyUtils.jar ->
</path>
<target name="init">
<!-- create the time stamp -->
<tstamp/>
<!-- create the build directory -->
<mkdir dir="${ejb.bin}"/>
<mkdir dir="${ejb.bin}/META-INF"/>
<mkdir dir="${deploy.working.dir}"/>
</target>
<!-- MadMoneyUtils.jar creation. -->
<target name="helpers.build" depends="init">
<echo>.......Building ${ant.project.name} utilities subproject</echo>
<ant antfile="utilbuild.xml" dir="${basedir}" target="utils.jar"/>
<echo>.......Finished building ${ant.project.name} utilities subproject</echo>
</target>
<!-- The EJB-RDB maps that come out of WSAD need some minor fixes
in order to be used by ejbdeploy in a stand-alone environment.
This ejb.map.target uses a bsh script to do the dirty work of
cleaning up the map files and moving them to the META-INF directory
in the EJB module build directory.
-->
<!-- It doesn't seem to be possible to execute a bash command.
I tried what you see here and I tried running bash.exe and using
the -c option to bash.exe to execute a command, but I couldn't get
that to work either. (That seemed to get further, but then getting
the command to bash.exe didn't seem to be possible.)
Instead, run the map cleaning script as part of a wrapper that does
the map cleaning then launches the rest of the build.
<target name="ejb.map.clean" depends="init">
<echo>.......Cleaning up WSAD EJB-RDB Maps for ${ant.project.name}</echo>
<exec executable="${cleanEJB-RDBMaps}" vmlauncher="false" dir="${basedir}">
<arg value="${ejb.base}"/>
<arg value="${build.dir}"/>
</exec>
<echo>.......Finished cleaning up EJB-RDB Maps for ${ant.project.name}</echo>
</target>
-->
<!-- META-INF holds the deployment descriptor, IBM extensions,
and EJB mapping files.
-->
<target name="ejb.compile" depends="init,helpers.build">
<echo>.......Compiling ${ant.project.name} EJBs</echo>
<javac srcdir="${ejb.src}"
excludes="META-INF/**"
destdir="${ejb.bin}"
classpathref="local.classpath"
/>
<echo>.......Finished compiling ${ant.project.name} EJBs</echo>
</target>
<!-- Create the EJB jar. First, copy all the meta files except
the EJB-RDB mapping files, from the EJB source to the build dir,
EJB bin.
38
-->
<target name="ejb.jar" depends="ejb.compile">
<echo>.......Packaging ${ant.project.name}</echo>
<copy todir="${ejb.bin}">
<fileset dir="${ejb.base}/properties"/> <!-- app config files -->
</copy>
<copy todir="${ejb.bin}/META-INF">
<fileset dir="${ejb.src}/META-INF"> <!-- ejb-jar.xml, bnd, ext -->
<exclude name="*/Schema/*"/>
<exclude name="Map.mapxmi"/>
</fileset>
</copy>
<jar jarfile="${ejb.jar.dest}/${ejb.jar}"
basedir="${ejb.bin}">
</jar>
<echo>.......Finished packaging ${ant.project.name}</echo>
</target>
<!-- Run ejbdeploy on the EJB jar. This is the final step in the build.
I've run this without the -cp utils.jar and not gotten an error.
I would have expected to, but maybe since the EJB-RDB mapping doesn't
need to be done, the step where the dependent classpath is needed
doesn't occur.
I get a complaint about using DB2 V72 in the -dbvendor arg, but
it doesn't seem to matter.
-->
<target name="deployed.ejb.jar" depends="ejb.jar">
<echo>.......Generating deploy code for ${ant.project.name}</echo>
<exec executable="${ejbdeploy}" output="ejbdeploy.log">
<arg value="${ejb.jar.dest}/${ejb.jar}"/> <!-- undeployed jar -->
<arg value="${deploy.working.dir}"/> <!-- working dir -->
<arg value="${ejb.jar.dest}/${deployed.ejb.jar}"/> <!-- deployed jar -->
<arg value="-cp ${util.jar.dir}/${utils.jar}"/>
<arg value="-dbvendor ${dbvendor}"/>
<arg value="-trace"/>
</exec>
<echo>.......Finished generating deploy code for ${ant.project.name}</echo>
</target>
<target name="clean" depends="init">
<echo>.......Cleaning ${ant.project.name}</echo>
<delete dir="${ejb.bin}"/>
<delete file="${ejb.dest}/${ejb.jar}"/>
<delete file="${ejb.dest}/${deployed.ejb.jar}"/>
<delete file="ejbbuild.log"/>
<delete file="ejbdeploy.log"/>
<delete dir="${deploy.working.dir}"/>
<echo>.......Finished cleaning ${ant.project.name}</echo>
</target>
</project>
39
Sample ANT build script : warbuild.xml
<?xml version="1.0"?>
<!-- Created 2002-Mar-28 by Peter Van Sickel of IBM Software Services -->
<project name="MadMoneyManager" default="war" basedir=".">
<description>
This buildfile is for creating the MadMoneyWeb.war file.
MadMoneyWeb servlets reference classes in MadMoneyUtils
and MadMoneyEJBs so both of those projects need to be built
in order to compile the servlets in this project.
The MadMoneyWeb.war file is placed in the ./modules directory.
</description>
<!-- Software locations -->
<property name="was.home" value="w:/was401ae/"/>
<!-- properties defining the project tree structure -->
<property name="war.base" value="${basedir}/MadMoneyWeb"/>
<property name="war.src.tree" value="${war.base}/webApplication"/>
<property name="web.java.src" value="${war.base}/source"/>
<property name="web.classes.dest" value="${war.src.tree}/WEB-INF/classes"/>
<property name="warfile" value="MadMoneyWeb.war"/>
<property name="war.dest" value="${basedir}/modules"/>
<property name="ejb.jar.dir" value="${basedir}/modules"/>
<property name="ejb.jar" value="MadMoneyEJBs.jar"/>
<property name="util.jar.dir" value="${basedir}/modules"/>
<property name="utils.jar" value="MadMoneyUtils.jar"/>
<!-- classpath for the web Java classes compilation -->
<path id="local.classpath">
<pathelement location="${was.home}/lib/j2ee.jar"/>
<pathelement location="${was.home}/lib/ras.jar"/> <!-- Trace -->
<pathelement location="${util.jar.dir}/${utils.jar}"/> <!-- MadMoneyUtils.jar ->
<pathelement location="${ejb.jar.dir}/${ejb.jar}"/> <!-- MadMoneyEJBs.jar -->
</path>
<target name="init">
<!-- create the time stamp -->
<tstamp/>
<!-- create all the build directories -->
<mkdir dir="${web.classes.dest}"/>
</target>
<target name="utils.build" depends="init">
<echo>......Building ${ant.project.name} utilities subproject</echo>
<ant antfile="utilbuild.xml" dir="${basedir}" target="utils.jar"/>
<echo>......Finished building ${ant.project.name} utilities subproject</echo>
</target>
<target name="ejb.build" depends="init">
<echo>......Building ${ant.project.name} EJB subproject</echo>
<ant antfile="ejbbuild.xml" dir="${basedir}" target="ejb.jar"/>
<echo>......Finished building ${ant.project.name} EJB subproject</echo>
</target>
<target name="warcompile" depends="init,utils.build,ejb.build">
<echo>.....Compiling ${ant.project.name} web Java source.</echo>
<javac srcdir="${web.java.src}"
40
destdir="${web.classes.dest}"
classpathref="local.classpath"
/>
<echo>.....Finished compiling ${ant.project.name} web Java source.</echo>
</target>
<target name="war" depends="warcompile">
<!-- build a war file from the war directory tree -->
<echo>.....Building ${ant.project.name} WAR file.</echo>
<war basedir="${war.src.tree}" warfile="${war.dest}/${warfile}"
webxml="${war.src.tree}/WEB-INF/web.xml">
</war>
<echo>.....Finished building ${ant.project.name} WAR file.</echo>
</target>
<target name="clean" depends="init">
<echo>.....Cleaning ${ant.project.name}</echo>
<delete dir="${web.classes.dest}"/>
<!-- compiled web code -->
<delete file="${war.dest}/${warfile}"/>
<echo>.....Finished cleaning ${ant.project.name}</echo>
</target>
</project>
41
Topics for Future Consideration
Application Requirements and Design
This document does not address any application requirements gathering and design issues.
Chapter 3 of the Redbook WebSphere v3.5 Handbook (SG-246161) covers this topic nicely.
Migrating the WebSphere Studio Application Developer Workspace
Much of the discussion contained here assumes that the final output from WebSphere Studio
Application Developer is an EAR file or artifacts are shared via an SCM repository.
Consideration should be given to a middle ground. What about sharing WebSphere Studio
Application Developer information amongst loosely coupled individuals? Could (or should) the
workspace be copied directly from machine to machine?
Organizing a Development Effort
This topic could include division of labor for a development team. What are the benefits and
tradeoffs between a Tiered Approach and a Threaded Approach? In the tiered approach each
developer concentrates efforts on a tier of the application (user interface, business logic,
persistence layer, etc.) For the threaded approach, each developer would take a slice of the
application.
WebSphere Studio Application Developer and Meta Data
This paper has just begun to explore the meta data stored by WebSphere Studio Application
Developer and how to best share this information.
J2EE Packaging and WebSphere
While this topic has been discussed at length, there are still many unresolved issues. The future
direction of the J2EE packaging for static content is unclear. Some would argue that this is
outside the scope of the J2EE package specification. Others argue that as caching schemes
(WebSphere’s Dynacache and Edge Server) become more prevalent, the need to serve content
from a Web server is less important. The jury is still out.
Additional tools or scripts may be needed to fully exploit the ability to repackage a J2EE EAR
file. This should include automation around the creation of an EJB client jar.
42
References
Redbooks
SG-246176, WAS 4.0 Administration Redbook.
SG-246134, WebSphere Version 4 Application Development Handbook
SG-246161,WebSphere v3.5 Handbook
On the Web
J2EE Specifications: http://java.sun.com/j2ee/
Concurrent Versions System (CVS): http://www.cvshome.org/
More information about Jakarta ANT can be found at http://jakarta.apache.org/ant/
Information about Extreme programming can be found at:
http://www.extremeprogramming.org/
http://www.xprogramming.com/
The preDeploy tool may be obtained from:
http://lindsey.boulder.ibm.com/wsdd/downloads/predeploy.html
Tim deBoer’s zipCreation WebSphere Studio Application Developer plugin utility can be found
at:
http://www7b.boulder.ibm.com/wsdd/library/techarticles/0112_deboer/deboer2.html
White papers
Best Practices: Operations by Peter Van Sickel
Best Practices: WebSphere Application Testing by Jeff Garratt
Automating the Deployment Process with WebSphere 4.0.1 by Randy Powelson
43
Acknowledgments
Dave Artus
Roland Barcia
Wayne Beaton
Keys Botzum
Kyle Brown
John Martinek
Randy Powelson
Peter Van Sickel
Contact Information
For more information, please contact Matthew A. Oberlin at [email protected].ibm.com.
To learn more about services, visit our website at www.ibm.com/websphere/developer/services.
To engage us, please contact one of our IBM Software Services Sales Specialists listed here:
www.ibm.com/websphere/developer/services/contacts.html.
Trademarks
IBM, DB2, Tivoli, and WebSphere are trademarks or registered trademarks of IBM Corporation in the United States, other countries,
or both.
Windows and Windows NT are registered trademarks of Microsoft Corporation in the United States, other countries, or both.
Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the United
States, other countries, or both.
Other company, product, and service names may be trademarks or service marks of others
Terms and Conditions
Information provided has been developed as a collection of the experiences of technical services
professionals over a wide variety of customer and internal IBM environments, and may be
limited in application to those specific hardware and software products and levels
The information contained in this document has not been submitted to any formal IBM test. The
use of this information or the implementation of any of these techniques is a customer
responsibility and depends on the customer's ability to evaluate and integrate them into the
customer's operational environment. While each item may have been reviewed by IBM for
accuracy in a specific situation, there is no guarantee that the same or similar results will be
obtained elsewhere. Customers attempting to adapt these techniques to their own environments
do so at their own risk, and in some environments may not achieve all the benefits described.
44
The following paragraph does not apply to the United Kingdom or any other country
where such provisions are inconsistent with local law: International Business Machines
Corporation provides this publication “as is” without warranty of any kind, either express
or implied, including, but not limited to, the implied warranties of non-infringement,
merchantability or fitness for a particular purpose. Some states to not allow disclaimer of
express or implied warranties in certain transactions, therefore, this statement may not
apply to you.
This information could include technical inaccuracies or typographical errors. Changes are
periodically made to the information herein; these changes will be incorporated in new editions
of this publication. IBM may make improvements and/or changes in the product(s) and/or the
program(s) described in this publication at any time without notice.
IBM may not offer the products, services, or feature discussed in this document in all countries.
Consult your local IBM representative for information on the products and services currently
available in your area. Any reference to an IBM product, program, or service is not intended to
state or imply that only that IBM product, program, or service may be used. Any functionally
equivalent product, program, or service that does not infringe any IBM intellectual property right
may be used instead. However, it is the user’s responsibility to evaluate and verify the operation
of any non-IBM product, program, or service.
IBM may have patents or pending patent applications covering subject matter described in this
document. The furnishing of this document does not give you any license to these patents. You
can send license inquiries, in writing, to:
IBM Director of Licensing, IBM Corporation, North Castle Drive, Armonk, NY 10504-1785
U.S.A.
Any references in this information to non-IBM Web sites are provided for convenience only and
do not in any manner serve as an endorsement of those Web sites. The materials at those Web
sites are not part of the materials of this IBM product and use of those Web sites is at your own
risk.
Information concerning non-IBM products was obtained from the suppliers of those products,
their published announcements or other publicly available sources. IBM cannot confirm the
accuracy of performance, compatibility or any other claims related to non-IBM products.
Questions on the capabilities of non-IBM products should be addressed to the suppliers of those
products.
All statements regarding IBM's future direction and intent are subject to change or withdrawal
without notice and represent goals and objectives only.
All prices shown are IBM's suggested list prices and are subject to change without notice.
Dealer prices may vary.
45
Any performance date contained in this document was determined in a controlled environment.
Therefore the results obtained in other operating environments may vary significantly. Some
measurements quoted in this document may have been made on development-level systems.
There is no guarantee that these measurements will be the same on generally available systems.
Some measurements quoted in the document may have been estimated through extrapolation.
Actual results may vary. Users of this presentation should verify the applicable for their specific
environment.
COPYRIGHT LICENSE:
This information contains sample application programs in source language, which illustrates
programming techniques on various operating platforms. You may copy, modify, and distribute
these sample programs in any form without payment to IBM, for the purpose of developing,
using, marketing or distributing application programs conforming to the application
programming interface for the operating platforms for which the sample programs are written.
These examples have not been thoroughly tested under all conditions. IBM, therefore, cannot
guarantee or imply reliability, serviceability, or function of these programs.
46
Was this manual useful for you? yes no
Thank you for your participation!

* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project

Download PDF

advertisement