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.
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
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.
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
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 SDK installation
is in a directory called JavaEE
type this from the
JavaEE
directory:
javadkee/bin/j2ee -verbose javadkee/bin/deploytool javadkee/bin/cloudscape -start
javadkee\bin\j2ee -verbose javadkee\bin\deploytool javadkee\bin\cloudscape -start
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:
appclient.ear
appclient
appclient.ear
file.appclient.ear
.Bean JAR files contain the third-party enterprise beans used by the application client.
File menu:
Add EJB JAR dialog box:
txBean.jar
and click Add EJB JAR. accountBean.jar
and
customerBean.jar
the same way.The application client must contain the application client executables and any classes those executables reference.
File menu:
appclient
Edit Contents of dialog box:
BankApp.class
and click Add. BankApp$1.class
and click Add.BankApp$2.class
and click AddDataModel.class
and click AddEventHandle.class
and click AddMessagesBundle_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.
General dialog box:
BankApp
.
When an application consists of more than one class, one and only
one of those classes has a main
method so the Java
virtual machine1 can launch it.
BankApp
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.
ejb/customerController
session
com.sun.ebank.ejb.customer.CustomerController
com.sun.ebank.ejb.customer.CustomerControllerHome
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 ~/javadkee/config
file. You can use a different JNDI name for the database driver
if you change the coded name in the ~/javadkee/config
file.
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:
Tools Menu:
Introduction dialog box:
appclient
.localhost
.Client.jar
appended
as follows: ~/appclientClient.jar
.JNDI Names dialog box:
Review dialog box:
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.
Shift-click to download:
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 toJ2sdkee/lib/system/tools.jar
, and you might need to setJ2EE_HOME
to point to your javadkee installation.
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
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 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
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.
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.
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); }
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); } }); }
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.
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 straightforward, so rather than show it all here, you can browse the BankApp class file.
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
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.
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(); } }
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
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(); } }
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.
createROFields
method in the BankApp
class for display in read-only fields.createEditableFields
method in the BankApp
class for display in editable fields.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); } }
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, which 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.
1 As used on this web site, the terms "Java virtual machine" or "JVM" mean a virtual machine for the Java platform.
© 1994-2005 Sun Microsystems, Inc.