|
Articles Index |
J2EE Index |
J2EE Developer's Corner
Application Clients
By Monica Pawlan
April 6, 2001
Sometimes enterprise applications use a standalone client application for
handling tasks such as system or application administration. For example, a
web-based banking application might use an application client to manually
administer customers and accounts. This capability is useful in the event
the site becomes inaccessible for any reason or a customer prefers to
communicate things like changes to account information by phone.
The Java 2 Enterprise Edition
(J2EE) reference implementation
provides a way for you to write, test, and deploy application clients.
A J2EE application client is a standalone program launched from
the command line or desktop, and typically accesses Enterprise
JavaBean programs
running on the J2EE application server.
This article shows you how to write, deploy, and test a simple
application client. The application client is launched
with the J2EE runclient command to administer
customer information for a web-based banking application. The example
has a Project Swing user interface and is based on the
J2EE SDK 1.3 beta release.
About the Example
The application client for this article references enterprise bean
and utility classes created by a third party and packaged into JAR
files. The enterprise beans and classes in those JAR files handle
customer, account, and transaction operations for a web-based banking
application.
In this example, the application client references the
CustomerControllerEJB bean in the
customerBean JAR file.
CustomerControllerEJB is a session bean that looks up and
communicates with the CustomerEJB entity bean. The data storage
details of the entity bean are hidden from the application client because
the application client calls the session bean methods only. When the user
adds or updates customer data, that data is first handled by the session
bean where it is in a transient state, and passed to the entity bean where
it is in a persistent state for a database read or write operation. If for
any reason a database read or write operation cannot complete, the entire
database transaction is backed out to prevent a condition where only part
of a new record is recorded or only some of the updates for an existing record
are committed. Because the session bean does not have direct access to
the database, no such insurance is needed. Lost data that never made it
to the entity bean can be reentered by the user, and data read from the
database for display in the user interface can be retrieved in its original
state.
To keep the complexity of the user interface code to a minimum,
this application client implements only the three customer functions
shown in Figure 1, which are view customer information, add a new
customer to the database, and update customer information. These
customer functions are implemented as methods available through
the CustomerControllerBean session bean.

Figure 1: Example Application Client
Customer information is stored in the underlying Cloudscape database
that comes with the J2EE installation. To read from and write to this
database, the application client looks up and creates a reference to
the CustomerControllerEJB session bean by way of its
home and remote interfaces. Figure 2 shows how the application client
and session bean work together once they are assembled into a complete
J2EE application and deployed. The container, shown in the box within the
circle is the interface between the session bean and the low-level
platform-specific functionality that supports the session bean.
The container is created during deployment.
Figure 2: Cooperating Classes and Interfaces
The application client does not work directly with the enterprise bean, but
creates an instance of its home interface, CustomerControllerHome.
The home interface extends EJBHome and has a create
method for creating the enterprise bean in its container.
CreateException is thrown if the enterprise bean cannot be
created, and RemoteException is thrown if a communications-related
exception occurs during the execution of a remote method.
When the home interface is created, the J2EE application server creates the
remote interface, CustomerController, and enterprise bean,
CustomerControllerEJB. The remote interface extends
EJBObject and declares methods for creating and managing customer
information. These methods are required to throw
javax.rmi.RemoteException.
The methods declared in the remote interface are implemented in the
enterprise bean class.
The CustomerControllerEJB class provides implementations
for the following methods, some of which are used in the example
code for this article:
createCustomer
removeCustomer
getCustomerOfAccount
getDetails
getCustomerOfLastName
setName
setAddress
Write and Compile
A J2EE application client is written like any other Java programming
language application. You write your class or classes using Java 2
Standard Edition (J2SE) and J2EE
APIs, and compile as you normally would.
The J2EE application client for this article consists of the following
classes: BankApp,
EventHandle,
and DataModel.
You can read a detailed explanation of
the code in Code Walkthrough below. If you want to run
the example, download the appclient
zip file. It contains the application client class files, enterprise bean
JAR files, and scripts for creating the database tables.
The application client classes are not in a package, so you can
compile the BankApp class
as shown below. The EventHandle and DataModel
classes are compiled at the same time because BankApp
references EventHandle and EventHandle
references DataModel.
javac BankApp.java
Start the J2EE Server, Database, and Deploy Tool
To assemble, deploy, and test the J2EE application client, you
start the J2EE server, database, and deploy tool as described
here. First, download and install the
J2SE
and J2EE platforms.
Once you have these platforms set up, you can start the J2EE server,
database, and deploy tool.
In different windows, type the following commands in this order to
start the J2EE server, Deploy tool, and Cloudscape database. Be sure
the J2EE server is completely started before executing the
deploytool and cloudscape commands:
j2ee -verbose
deploytool
cloudscape -start
If that does not work, supply the fully-qualified pathname.
For example, if your j2sdkee1.3 installation
is in a directory called J2EE type this from the
J2EE directory:
Assemble the J2EE Application
After you download and unzip the zip
file and compile the application client code, you can assemble the
J2EE application.
Create a J2EE Application
The first step is to create a J2EE application to house
the application client executables and enterprise bean JAR files.
File menu:
New Application dialog box:
- Application File Name:
appclient.ear
- Application Display Name
appclient
- Click Browse.
- Look in: go to the directory
where you want to save the
appclient.ear file.
- File name:
appclient.ear.
- Click New Application.
- Click OK.
Add Bean JARs to Application
Bean JAR files contain the third-party enterprise beans used
by the application client.
File menu:
- Select Add to Application: EJB JAR.
Add EJB JAR dialog box:
- Look in: go to the directory
where the EJB JAR files are located.
- Select
txBean.jar and click Add EJB JAR.
- Add
accountBean.jar and
customerBean.jar the same way.
Create the Application Client
The application client must contain the application client executables
and any classes those executables reference.
File menu:
- Select New: Application Client
- Introduction dialog box: Read and click Next.
- General dialog box: Create Archive Within Application:
appclient
- Click Add.
Edit Contents of dialog box:
- Available Files: Toggle the directory
icon to the directory where the application
class files are located.
- Select
BankApp.class and click Add.
- Select
BankApp$1.class and click Add.
- Select
BankApp$2.class and click Add
- Select
DataModel.class and click Add
- Select
EventHandle.class and click Add
- Select
MessagesBundle_en_US.properties and click Add
Note:
Because the files for this example are not in a package, the
lower box should display the class names only.
- Click OK.
- Click Next.
General dialog box:
Environment Entries dialog box:
Enterprise Bean References dialog box:
In this screen, you enter the home and remote interfaces for the
CustomerControllerEJB session bean referenced by the
application client. The coded name is the name defined in the
com.sun.ebank.util.CodedNames class for the
CustomerController remote interface.
- Click Add.
- Coded Name:
ejb/customerController
- Type:
session
- Home:
com.sun.ebank.ejb.customer.CustomerController
- Remote:
com.sun.ebank.ejb.customer.CustomerControllerHome
- Click Finish.
Specify JNDI Names
With appclient selected, click the JNDI Names
tab. Fill in the JNDI Name column as shown in Figure 4. The
order may be a
little different on your own display, but make sure you map the
JNDI name you provide opposite the exact Component and
Referenced By column as shown here. An explanation of these
mappings immediately follows.
Figure 4: Specifying JNDI Names
A JNDI name is the name the J2EE server uses to look up
enterprise beans. In your code when you look up an enterprise
bean, you supply statements similar to those shown below.
The actual lookup takes place three lines down where the
getCustomerControllerHome method is called
on the EJBGetter class. The EJBGetter
class is a utility class that retrieves a coded JNDI name
from the src.com.sun.ebank.util.CodedNames class.
In this example, the application client is looking up the coded
name for the CustomerController remote interface.
try {
customerControllerHome =
EJBGetter.getCustomerControllerHome();
customer = customerControllerHome.create();
} catch (Exception NamingException) {
NamingException.printStackTrace();
}
|
If you look at the last line of the bottom table in the figure
above, you see that BankApp (the display name
for the main class for the application client) references
ejb/customerController, which is
the coded name defined in the CodedNames class for
the CustomerController remote interface. Your job
is to supply a JNDI name in the last column.
The JNDI name that you supply is stored in the J2EE application
deployment descriptor and the J2EE server uses it to look up the
CustomerControllerBean. If you look at the second row
from the top in the top table in the figure above, you see that
CustomerControllerBean is mapped to the same JNDI name as
is ejb/customerController in the last line of the
bottom table. It does not matter what JNDI name you supply, as long as
you use the same name for the remote interface lookup as you use for
its corresponding bean. So, looking at the table, you can say that
the application client (BankApp) looks up the
CustomerController remote interface, which uses the
JNDI name of MyCustomerController, and the J2EE server
uses the MyCustomerController JNDI name to find the
corresponding CustomerControllerBean object.
The other rows in the top table have the mappings for the other
enterprise beans. All of these beans are stored in the JAR files you
added to the J2EE application during assembly. Their implementations
have coded names for looking up either other enterprise beans or the
database driver.
The JNDI name for the database driver is mapped in the bottom
table and is jdbc/Cloudscape. This name is the default
coded name supplied in the ~/j2sdkee1.3/config
file. You can use a different JNDI name for the database driver
if you change the coded name in the ~/j2sdkee1.3/config file.
Verify the J2EE Application
Before you deploy the J2EE application, it is a good idea
to verify that the bean and application client code is
compliant with the J2EE specification.
Tools menu:
- Select Verifier:
- Verify Specification Compliance dialog box: Click OK.
- When you see the message appclient: There were no failed tests,
verification has completed and you can click OK.
- Use the Close window menu to dismiss the dialog box.
Deploy the J2EE Application
Tools Menu:
Introduction dialog box:
- Object to Deploy:
appclient.
- Target Server:
localhost.
- Return Client Jar: checked.
By default, the pathname for the returned jar file
is the location where the EAR file is stored and
the application client name with
Client.jar appended
as follows: ~/appclientClient.jar.
- Click Next.
JNDI Names dialog box:
- Make sure all the JNDI names are
correct as shown in the table above.
- Click Next.
Review dialog box:
Create the Database Tables
So the enterprise beans can write to and read from the database, you
create the appropriate tables. To make things easy, the
database tables are created with the following two scripts. These
scripts create all the tables used by all the third-party
enterprise beans; however, this example as it stands really only
needs a database table for customer information. If you take this
example and later expand it to use some of the other enterprise beans,
the tables will be in the database ready for data.
Put these files in the same directory anywhere on your system, make sure
the Cloudscape database is running, and execute the cloudUtil
script with create-table.sql parameter as follows:
cloudUtil.sh
create-table.sql
Note:
Your class path should point to J2sdkee1.3/lib/system/tools.jar,
and you might need to set J2EE_HOME to point to your
j2sdkee1.3 installation.
Test the J2EE Application
To launch and test the example application client,
set the APPCPATH environment variable to
point to the directory where you stored the appclient.ear
file and type the following at the command line:
runclient -client appclient.ear -name BankApp en US
The -client appclient.ear parameter is the
name of the J2EE application EAR file, and the
-name BankApp parameter is the display name
of the application client.
At run time, you'll need to set the APPCPATH environment variable to the
client stub JAR, which contains the EJB class files. This is the path
specified during deployment when you checked the box Return Client JAR. By
default, the pathname for the returned jar file is the location where the
EAR file is stored and the application client name with Client.jar appended
as follows: ~/appclientClient.jar.
The en and US parameters passed to the
runclient command are the language and country
codes. The language and country codes in this example tell the
application to use the English language (en) from the
United States (US). See Internationalization
below for more information on internationalizing and localizing an
application.
When the login box appears, type in guest for the user
name, and
guest123 for the password, and click OK. The next thing
you see is the application shown in Figure 5.
Figure 5: J2EE Application Client
Code Walkthrough
This section walks you through the code for the three classes
that comprise the J2EE application client. Discussions on
object-oriented program design and internationalization are
included. You can, of course, skip these sections if you
are already familiar with this material.
The Classes and their Relationship
The J2EE application client for this article is broken into
the following three classes. Their relationship is depicted in
Figure 6.
BankApp builds the initial user interface,
creates the EventHandle object, and provides methods for
the EventHandle and DataModel
objects to call to update the user interface.
EventHandle listens for button clicks by the user,
takes action based on which button the user clicks, creates the
DataModel object, calls methods in the DataModel
object to write data to and read data from the underlying database, and
calls methods in the BankApp object to update the user
interface when actions complete.
DataModel retrieves data from the user interface, performs
data checks, writes valid data to and reads stored data from the underlying database, and
calls methods in the BankApp object to update the user interface based on the
success of the database read or write operation.
Figure 6: Relationships among Classes
Object-Oriented Program Design
Organizing the application client code into classes according to function is a
modular approach to application design that makes the application code easier
to read, update, and maintain. For example, if you decide to remove the
application client from the J2EE environment and run it as a stand alone
application that accesses customer data in a file instead of a database, you
only have to modify method implementations in the DataModel
class. As long as you do not change the method signatures, you do not have
to modify method signatures or implementations in other classes that call
DataModel methods.
Another name for modularized programming like this is object-oriented programming.
If you are new to object-oriented programming with the Java programming language,
you might forget to design your application to use a separate class for each
function and might not vigilant about making sure each class defines only one
kind of function. You end up with one large class that combines functions and is
essentially a procedural program wrapped up in one class. If you think you might be
guilty of this, you are not alone. I have been guilty of it too.
There are a lot of texts that expound upon the concepts and benefits of object-oriented
programming, but what I found to be the most enlightening was seeing a working
program that does not use good object-oriented form and then seeing how to change
it so it does. In that spirit, here is the BankAppNotOO
code all in one class. The following discussions explain how to
use a modular object-oriented approach to break this one very large class into three
separate, organized, and smaller classes.
BankApp Class
The BankApp class creates the user interface,
is the class with the main method and provides
protected methods for the other BankApp
application classes to call.
Main Method
The main method creates instances of the BankApp
and EventHandle classes. The application is internationalized.
The language and country variables passed to the
BankApp constructor specify the language to use where
en means English and US means United States. These
values mean the application uses United States English as opposed to
Australian or United Kingdom English.
The values for the language and country codes are retrieved from the
args parameter passed to the main method.
The args parameter gets its values when the application
starts from values passed to the runclient command described
in Test the J2EE Application above.
public static void main(String args[]) {
String language, country;
if(args.length != 2) {
language = new String("en");
country = new String("US");
} else {
language = new String(args[0]);
country = new String(args[1]);
}
frame = new BankApp(language, country);
frame.setTitle("Banking Administration Client");
WindowListener l = new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
};
frame.addWindowListener(l);
frame.pack();
frame.setVisible(true);
//Create event handling object
EventHandle ehandle = new EventHandle(frame,
messages);
}
|
Constructor
The BankApp constructor creates the initial user interface, which
consists of three buttons that let the user view customer information, add
a new customer to the database, or update an existing customer's information.
The internationalization code creates a Locale from the
language and country parameters, and uses
the Locale to create a ResourceBundle.
The other parameter to the ResourceBundle is the first
part of the name of the properties file where the translated text is
stored. In this example, that file is
MessagesBundle_en_US.properties, which you added to the
application client during assembly.
public BankApp(String language, String country) {
//Internationalization variables
Locale currentLocale;
currentLocale = new Locale(language, country);
messages = ResourceBundle.getBundle(
"MessagesBundle", currentLocale);
//Create initial UI (Panel 1)
getContentPane().setLayout(new GridLayout(1,2));
p1 = new JPanel();
p1.setLayout(new GridLayout(11,1));
p2 = new JPanel();
p1.setBackground(Color.white);
p2.setBackground(Color.white);
getContentPane().add(p1);
getContentPane().add(p2);
view = new JButton(messages.getString(
"ViewButton"));
add = new JButton(messages.getString(
"AddButton"));
update = new JButton(messages.getString(
"UpdateButton"));
messlab = new JLabel();
messlab2 = new JLabel();
messlab3 = new JLabel();
messlab4 = new JLabel();
messlab5 = new JLabel();
messlab6 = new JLabel();
p1.add(view);
p1.add(add);
p1.add(update);
p1.add(new JLabel());
p1.add(messlab);
p1.add(messlab2);
p1.add(messlab3);
p1.add(messlab4);
p1.add(messlab5);
p1.add(messlab6);
//Create Panel 2 buttons so EventHandle
//constructor can add as action listeners
OK = new JButton(
messages.getString("OKButton"));
cancel = new JButton(messages.getString(
"CancelButton"));
//Add functionality to close window
addWindowListener(new WindowAdapter() {
public void windowClosing(
WindowEvent event) {
System.exit(0);
}
});
}
|
Internationalization
In an internationalized program, string values are read from
a properties file that contains translations for the language
in use in the form of key and value pairs. So, instead of creating
strings directly in your code, you create a ResourceBundle
that indicates the file where the translations are and read the
translations (values) from that file using the corresponding key.
Here are the key and value pair definitions for the properties file used
in this example:
ViewButton=View Customer Information
AddButton=Add New Customer
UpdateButton=Update Customer Information
OKButton=OK
CancelButton=Cancel
ViewButtonMess=View Customer Information
AddButtonMess=Add New Customer
UpdateButtonMess=
Update Customer Information
EnterCustIDMess=Enter Customer ID:
RemoteException=Remote Exception
CustomerException=Customer
NotFoundException=not found
StateLimitException=
State has two-letter limit
MILimitException=MI has one-letter limit
MissingRequiredException=
Missing required information
FnameLab=First name (Required):
LnameLab=Last name (Required):
MiLab=MI (Required):
StreetLab=Street (Required):
CityLab=City (Required):
StateLab=State (Required):
ZipLab=Zip:
PhoneLab=Phone:
EmailLab=Email:
|
So, for example, instead of creating the view button like this:
view = new JButton(
"View Customer Information")
you would do it like this:
view = new JButton(
messages.getString("ViewButton")).
In this example, ViewButton is the key in the
MessagesBundle_en_US.properties file with a corresponding
value of View Customer Information.
This approach makes it easy to localize application text to the
language spoken by the majority of its users. For example, if you have
another file named MessagesBundle_fr_FR.properties with
French values for the keys, you could supply fr FR
to the runclient command instead of en US
to launch the application client to read in French from France
instead of United States English.
See the
Internationalization
lesson in
Essentials of the
Java Programming Language: A Hands-On Guide for more information
Class Methods
The BankApp class provides methods that other objects call when
they need to update the user interface. These methods are as follows:
clearMessages()
resetPanelTwo()
createPanel2Labels(),
createROFields(
String first, String last, String mid,
String str, String cty, String st,
String zp, String tel,
String mail)
createEditableFields(String first,
String last, String mid,
String str, String cty, String st, String zp, String tel,
String mail.
The implementations are very straight-forward, so rather than show it
all here, you can browse the BankApp class file.
EventHandle Class
The EventHandle class
implements the ActionListener interface, which provides
a method interface for handling action events. Like all other interfaces
in the Java programming language, ActionListener defines a set of
methods, but does not implement their behavior. Instead, you provide
the implementations because they take application-specific actions.
The ActionListener interface has only one method,
the actionPerformed method. This method handles
action events generated by the BankApp class when
users interact with the user interface by clicking buttons.
Figure 7 illustrates how the EventHandle class interacts
with the BankApp and DataModel classes,
and the following sections detail the illustration.
Figure 7: Class Interactions
Constructor
Events generated by the BankApp class are handled
in the EventHandle class, so the EventHandle
class not only implements the ActionListener
interface with code to handle the events, but also listens for
the button events generated by the BankApp class.
To set up this connection, the EventHandle
constructor receives an instance of the BankApp class,
assigns it to its private instance variable, calls the
addActionListener methods on the BankApp
buttons, and passes the addActionListener methods an
instance of itself (this) to add the EventHandle
action listener to the buttons.
public EventHandle(BankApp frame,
ResourceBundle messages) {
this.frame = frame;
this.messages = messages;
this.dataModel = dataModel;
frame.view.addActionListener(this);
frame.add.addActionListener(this);
frame.update.addActionListener(this);
frame.OK.addActionListener(this);
frame.cancel.addActionListener(this);
dataModel = new DataModel(frame, messages);
}
|
The constructor also receives an instance of the ResourceBundle
class and assigns it to its private instance variable so the
EventHandle object has access to the application client's
localized text.
actionPerformed Method
The actionPerformed method handles the button events
generated by the BankApp class. Its implementation
uses a series of if statements to find out which
button generated the event and takes the appropriate action.
The bodies of the if statements change message
text and the Panel 2 display in BankApp, and call
methods in the DataModel class when data needs to be
written to or read from the database, or when the Panel 2 display
in BankApp needs to be updated in preparation for
an add, view, or update operation.
public void actionPerformed(
ActionEvent event) {
Object source = event.getSource();
//View customer data
if(source == frame.view) {
frame.clearMessages()
String vbutton =
messages.getString( "vIewButton");
frame.messlab5.setText(" " + vbutton);
mess = new String(messages.getString(
"EnterCustIDMess"));
returned = JOptionPane.showInputDialog(frame,
mess);
if(returned != null) {
which = 3;
dataModel.createCustInf(
which, returned);
}
}
//Add new customer
if(source == frame.add){
frame.clearMessages();
String abutton =
messages.getString("AddButton");
frame.messlab5.setText(" " + abutton);
which = 1;
dataModel.createCustInf(which, returned);
}
//Update customer data
if(source == frame.update){
frame.clearMessages();
String ubutton = messages.getString(
"UpdateButton");
frame.messlab5.setText(" " + ubutton);
mess = new String(messages.getString(
"EnterCustIDMess"));
returned = JOptionPane.showInputDialog(frame,
mess);
if(returned != null) {
which = 2;
dataModel.createCustInf(which, returned);
}
}
//Process data
if(source == frame.OK) {
if(which == 3) { //view data
frame.resetPanelTwo();
//add or update data
} else if((which == 1) || (which == 2)) {
//Test data and write to database
int complete = dataModel.checkData(returned,
which);
//If data okay, clear Panel 2
if(complete == 0) {
if(which == 1) {
JOptionPane.showMessageDialog(frame,
dataModel.custID, "Customer ID",
JOptionPane.PLAIN_MESSAGE);
}
frame.resetPanelTwo();
}
//If errors, redisplay data to user
//and leave error messages on display
if(complete == 1) {
frame.createEditableFields(dataModel.first,
dataModel.last, dataModel.mid,
dataModel.str, dataModel.cty,
dataModel.st, dataModel.zp,
dataModel.tel, dataModel.mail);
}
}
}
//Clear data on cancel button press
if(source == frame.cancel) {
frame.resetPanelTwo();
}
}
|
DataModel Class
The DataModel class
provides methods for reading data from the database,
writing data to the database, retrieving data from the
user interface, and checking that data before it is
written to the database.
Figure 8 illustrates how the DataModel class interacts
with the BankApp and EventHandle classes,
and the following sections detail the illustration.
Figure 8: Class Interactions
Constructor
The constructor receives an instance of the BankApp class
and assigns it to its private instance variable so the DataModel
object can display error messages in the user interface when its
checkData or writeData method detects errors.
It also receives an instance of the ResourceBundle
class and assigns it to its private instance variable so the
DataModel object has access to the application client's
localized text.
Because the DataModel class interacts with the database, the
constructor also has the code to establish a connection with the
remote interface for the CustomerController enterprise
bean and use the remote interface to create an instance of the
CustomerControllerEJB enterprise bean.
public DataModel(BankApp frame,
ResourceBundle messages) {
this.frame = frame;
this.messages = messages;
//Look up and create CustomerController bean
try {
customerControllerHome =
EJBGetter.getCustomerControllerHome(
);
customer = customerControllerHome.create();
} catch (Exception NamingException) {
NamingException.printStackTrace();
}
}
|
Methods
The getData method retrieves data from the user interface
text fields and uses the String.trim method to remove extra
control characters such as spaces and returns.
Its one parameter is a JTextfield so any instance of
the JTextfield class can be passed in for processing.
This polymorphic approach saves the series of if statements
used in the BankAppNotOO version.
private String getData(JTextField component) {
String text, trimmed;
if(component.getText().length() > 0) {
text = component.getText();
trimmed = text.trim();
return trimmed;
} else {
text = null;
return text;
}
}
|
The checkData method
stores data retrieved by the getData method
and checks the data to be sure all required fields have data,
the middle initial is no longer than one character, and the state is no
longer than two characters. If everything checks out, the writeData
method is called. If there are errors, they are printed to the user interface
in the BankApp object.
protected int checkData(
String returned, int which) {
int i, j, k;
this.which = which;
this.returned=returned;
last = getData(frame.lname);
first = getData(frame.fname);
mid = getData(frame.mi);
str = getData(frame.street);
cty = getData(frame.city);
st = getData(frame.state);
zp = getData(frame.zip);
tel = getData(frame.phone);
mail = getData(frame.e);
frame.messlab.setText(null);
frame.messlab2.setText(null);
frame.messlab3.setText(null);
frame.messlab4.setText(null);
frame.messlab6.setText(null);
//Check for data in required fields
if((last != null) && (first != null)
&& (str != null) && (cty != null)
&& (st != null)) {
i = 0;
} else {
frame.messlab6.setText(" " +
messages.getString(
"MissingRequiredException"));
i = 1;
}
//Check middle initial length
if(frame.mi.getText().length() > 1) {
frame.messlab2.setText(" " +
messages.getString("MILimitException"));
j = 1;
} else {
j = 0;
}
//Check state length
if(frame.state.getText().length() > 2) {
frame.messlab3.setText(" " +
messages.getString("StateLimitException"));
k = 1;
} else {
k = 0;
}
if((i == 0) && (j == 0) && (k == 0)) {
//Write data to database
int success = writeData();
return success;
} else {
return 1;
}
}
|
The writeData method determines whether the operation
is an add or an update and calls methods on the CustomerControlerEJB
enterprise bean as appropriate.
If the add or update operation fails, it writes error messages to the
BankApp user interface.
private int writeData() {
if(which == 2){ //Update customer information
try {
customer.setName(last, first, mid, returned);
customer.setAddress(str, cty, st, zp, tel,
mail, returned);
return 0;
} catch (RemoteException ex) {
frame.messlab.setText(" " +
messages.getString(
"RemoteException"));
return 1;
} catch (CustomerNotFoundException ex) {
frame.messlab4.setText(" " +
messages.getString(
"CustomerException") +
" " + returned + " " +
messages.getString(
"NotFoundException"));
return 1;
}
}
if(which == 1) { //Add new customer information
try {
custID = customer.createCustomer(
last, first, mid,
str, cty, st, zp, tel, mail);
return 0;
} catch (RemoteException ex) {
frame.messlab.setText(" " +
messages.getString(
"RemoteException"));
return 1;
}
}
return 0;
}
|
The createCustInf method is called by the EventHandle
class to refresh the Panel 2 display in the event of a view, update, or
add action event.
- For a view event, this method gets the
customer information for the specified customer from the database and
passes it to the
createROFields method in the BankApp
class for display in read-only fields.
- For an update event, this method gets the customer information
for the specified customer from the database and passes it to the
createEditableFields method in the BankApp
class for display in editable fields.
-
For an add event, this method calls the
createEditableFields method in the BankApp
with null data to create empty editable fields for the user to enter
customer data.
protected void createCustInf(int which,
String returned) {
CustomerDetails details = null;
//View Data
if((which == 3) && (returned.length() > 0)) {
try {
details = customer.getDetails(returned);
frame.createROFields(details.getFirstName(),
details.getLastName(),
details.getMiddleInitial(),
details.getStreet(
), details.getCity(),
details.getState(), details.getZip(),
details.getPhone(),
details.getEmail());
} catch (RemoteException ex) {
frame.messlab.setText(" Remote Exception");
} catch (CustomerNotFoundException ex) {
frame.messlab4.setText(" " +
messages.getString("CustomerException") +
" " + returned + " " +
messages.getString("NotFoundException"));
frame.resetPanelTwo();
}
}
//Update Data
if((which == 2) && (returned.length() > 0)) {
try {
details = customer.getDetails(returned);
frame.createEditableFields(
details.getFirstName(),
details.getLastName(),
details.getMiddleInitial(),
details.getStreet(), details.getCity(),
details.getState(), details.getZip(),
details.getPhone(),
details.getEmail());
} catch (RemoteException ex) {
frame.messlab.setText(" Remote Exception");
} catch (CustomerNotFoundException ex) {
frame.messlab4.setText(" " +
messages.getString("CustomerException") +
" " +
returned + " " +
messages.getString("NotFoundException"));
frame.resetPanelTwo();
}
}
//Add Data
if(which == 1) {
frame.createEditableFields(
null, null, null, null,
null, null, null, null, null);
}
}
|
Conclusion
You might want to include an application client with your J2EE application
for handling system or application administration. Deploying and running
an application client is slightly different from deploying and running
other J2EE components, but the code to connect to the enterprise beans
running on the J2EE server is the same for all J2EE components.
The enterprise beans used for this article are part of a larger banking
application is part of the
J2EE Tutorial. The J2EE
Tutorial is an excellent resource for the J2EE platform, tools, and APIs.
For information on the banking application, please see
Duke's Bank
Application.

Monica Pawlan is a manager and writer at Sun Microsystems, Inc., who enjoys learning and writing about new Java platform technologies. She also likes to garden, play guitar, and travel.
Hava a question?
Use Java Online Support.
1 As used on this web site, the terms "Java
virtual machine" or "JVM" mean a virtual machine for the Java
platform.
|
|