Title: Come Relax PoolSide Understanding, Creating and Using JDBC Connection Pools AOTC Spring 2003
1Come Relax Pool-Side - Understanding, Creating
and Using JDBC Connection Pools AOTC Spring 2003
- Lisa Spory - Northrop Grumman Mission Systems
- Paul Druker - Metaway Corporation
Come Relax Pool-side - Understanding, Creating
and Using JDBC Connection Pools
2Agenda
- JDBC Introduction
- Basic JDBC Connection
- Example 1
- Data Sources
- Example 2
- Connection Pooling Concepts
- Oracle Connection Pooling
- Example 3
- Oracle Connection Caching
- Example 4
3JDBC API Introduction
- Developed by Sun Microsystems as a standard means
of connecting to databases using the Java
programming language - Interfaces and Classes that provide the
contract for working with databases - No method bodies in core JDBC classes
- Third-party vendors, e.g. Oracle, provide
implementation - Currently at JDBC 3.0
- Oracle 9.2 ships with the JDBC 2.0 - Focus of
discussion
4JDBC API Introduction - Cont
- Core JDBC Packages
- java.sql (JDBC 1.0) - Connection and data access
interfaces/classes - Connection
- ResultSet
- Statement
- javax.sql (JDBC 2.0) - Optional Packages
- Interfaces and classes for Connection Pooling and
Data Sources - JDBC 3.0 - More extensions, mostly transaction
related - Introduces support for savepoints
5JDBC API - Database Drivers
- Oracle provides four JDBC Drivers
- Thin Driver
- Thin driver is lightest of all the Oracle drivers
and requires the least amount of system resources - Suitable for applets and web pages
- OCI Driver
- More resource-intensive than the thin driver
- Driver requires client installation
- Suitable for programs deployed on the middle tier
- Server-side Internal Driver
- Server-side Thin Driver
6JDBC API - Database Driver Types
- Four types of Drivers
- Type 1 - Provides access to a database bridging
between JDBC and ODBC - Type 2 - Performs native calls from Java to data
access APIs written in other languages - OCI Driver
- Type 3 - translates JDBC calls into a database
independent net protocol, which is then
translated to a database protocol by a net server
piece of middleware - Type 4 - Written in Java, establishes a
communication directly to the database via a Java
Socket - Thin Driver
7JDBC API - Basic JDBC Connection
- Goal is to create a java.sql Connection object
- Step 1 - Import packages
- Step 2 - Register the driver (Oracle Thin Driver)
- Step 3 - Open a connection to the database by
providing connection information - Step 4 - Use the Connection
- JDBC URL - Two Formats with Thin Driver
- jdbcoraclethin_at_HOST_NAMEPORTSERVICE_NAME
- jdbcoraclethin_at_(DESCRIPTION (ADDRESS_LIST
(ADDRESS (PROTOCOL TCP)(HOSTltIPgt)(PORT1521))
)(CONNECT_DATA (SERVICE_NAME ltSIDgt))
8JDBC API - Example 1 Basic Connection
- MyDriverManagerConnection.java
- import java.sql.
- import oracle.jdbc.
- public class MyDriverManagerConnection
- public static void main(String args)
- try
- DriverManager.registerDriver(
- new oracle.jdbc.OracleDriver())
- Connection conn DriverManager.getConnectio
n( - JDBC URL,User_Name,Password)
- catch (Exception e)
- e.printStackTrace()
-
-
-
9JDBC API - Using the Basic Connection
10JDBC API - Data Sources
- Data Sources were introduced with JDBC 2.0
Optional package - Serve as a replacement for DriverManager
- Sun will deprecate DriverManager eventually
- Using a DataSource decouples the application code
from vendor-specific JDBC driver class names, and
allows logical names to be used in place of URLs
and other connection properties - A JDBC DataSource class implements the
javax.sql.DataSource interface
11JDBC API - Data Sources
- Oracle implements javax.sql.DataSource with the
OracleDataSource class - In the oracle.jdbc.pool package
- Overloads the generic getConnection() methods to
return a more specialized OracleConnection object - Provides a set of properties that can be used to
set up database connection parameters - These properties follow the JavaBeans design
pattern public getter and setter methods
12JDBC API - Example 2 Connection Using an
OracleDataSource
- MyDataSourceConnection.java
- import java.sql.
- import oracle.jdbc.pool.
- public class MyDataSourceConnection
- public static void main(String args)
- try
- / Create an OracleDataSource Object /
- OracleDataSource myDataSouce new
OracleDataSource() -
- / Set the Data Source properties using
"setter" methods / - myDataSource.setServerName("IP")
- myDataSource.setDatabaseName("SID")
- myDataSource.setDriverType("thin")
- myDataSource.setPortNumber(1521)
- myDataSource.setUser("User_Name")
- myDataSource.setPassword("Password")
- / Get the Connection /
13Data Sources and JNDI
- Java Naming and Directory Interface (JNDI)
- Provides generic naming and directory
functionality for Java programs - Allows data sources to be registered centrally
and then looked up by application code - Removes exact connection details from the
application code - Data Sources are registered with JNDI in the
application deployment descriptor, e.g. web.xml - JNDI lookup to retrieve the data source by
logical Data Source name
14Connection Pooling
- What is a Pooled Connection and why do I need a
Connection Pool for my Web Application? - Connection myConn new Connection(.) represents
a new, physical database connection - Resources used to create the connection, maintain
it, and finally release it when no longer used - Cost of new Connections is particularly high for
web-based applications - Intermittent requests for database information
- High and difficult to predict usage volumes
- A Connection Pool is a set of database
connections that are reserved for use by clients
and then returned for reuse.
15Connection Pooling
- Support for Connection Pooling introduced with
JDBC 2.0 Optional Packages (javax.sql) - The Optional packages are included with the
classes111.zip and classes12.zip files from
Oracle - Manually with JDBC 1.0 - Lots of work
- See the book Professional Oracle 8i Application
Programming with Java, PL/SQL, and XML by Wrox
Press for a detailed example of Connection
Pooling with JDBC 1.0
16Connection Pooling and DataSources
- Connection Pooling makes heavy use of Data
Sources to retrieve Connection objects - A ConnectionPoolDataSource class is used to
retrieve a PooledConnection, which is used to
retrieve a Connection
17Connection Pooling and DataSources
- The Connection object returned from a
PooledConnection is not a new physical
connection. - The Connection is temporary handle or wrapper on
an underlying physical connection in the pool - Just as the getConnection() method of
PooledConnection does not actually open a new
database connection, calling close() on this
Connection object does not close the physical
connection, it simply releases the temporary
handle and returns the connection to the pool.
18Connection Pooling and Event Listeners
- How does a PooledConnection return a wrapper, and
not a real Connection object? - Magic is accomplished via Event Listeners
- An object can be attached to another object to
serve as an event listener. - The event listener listens for a specific
event, and when the event occurs, the event
listener executes a pre-specified action - The class implementing the DataSource interface
that produces the PooledConnection object
registers itself as an event listener through the
addConnectionEventListener() method of the
PooledConnection
19Connection Pooling and Event Listeners
- The connection event listener (DataSource) is
informed about what happened to the connection
wrapper - The PooledConnection can return a connection to
the pool when the close() method is called,
rather that closing the physical connection. - When you create a connection pool using a
ConnectionPoolDataSource, it is imperative that
the DataSource interface is implemented in order
to ensure proper registration of the event
listener
20Oracle Connection Pooling
- Oracle provided JDBC extensions
- oracle.jdbc.pool package
- Oracle extensions provide implementations to the
JDBC 2.0 Connection Pooling framework - OracleConnectionPoolDataSource class implements
the ConnectionPoolDataSource interface as well as
extends the OracleDataSource class - Satisfies the requirement to implement the
DataSource interface via inheritance
21Oracle Connection Pooling
Oracle Connection Pooling Class Diagram
22Oracle Connection Pooling
- Steps to set up an Oracle Connection Pool
- Create a connection pool data source object by
creating a new OracleConnectionPoolDataSource - Set the attributes on the data source necessary
to define the connection parameters using the
setter methods defined for the DataSource
interface - e.g. setServerName("IP")
- Create a pooled connection object by calling the
getPooledConnection()method of the
OracleConnectionPoolDataSource object. This
method is defined in the ConnectionPoolDataSource
interface.
23Oracle Connection Pooling
- Steps to set up an Oracle Connection Pool (cont)
- Request a Connection instance from the pooled
connection object. - Remember, it is only a handle to the underlying
physical connection that is returned, not the
physical connection itself - When finished with the connection instance,
return it to the pool using the close() method. - Close the pooled connection object.
- Now lets see it work!
24Oracle Connection Pooling - Example 3 Creating
an Oracle Connection Pool
- MyConnectionPool.java
- import java.sql.
- import javax.sql.
- import oracle.jdbc.pool.
- public class MyConnectionPool
- public static void main (String args)
- try
- // Step 1 create a connection pool data
source object - OracleConnectionPoolDataSource
myPoolDataSource new - OracleConnectionPoolDataSource()
- //Step 2 set up the data source
attributes - myDataSource.setServerName("IP")
- myDataSource.setDatabaseName("SID")
- myDataSource.setDriverType("thin")
- myDataSource.setPortNumber(1521)
- myDataSource.setUser("User_Name")
25Oracle Connection Pooling - Example 3 Creating
an Oracle Connection Pool
-
- MyConnectionPool.java
- //Step 4 request a Connection instance from
the pooled connection object - Connection myConnection myPooledConnection.
getConnection() - // Use the Connection here to access the
database. - //Step 5 release the handle on the
Connection instance - myConnection.close()
-
- //Step 6 close the pooled connection object
- myPooledConnection.close()
- catch (Exception e)
- e.printStackTrace()
-
-
-
26Oracle Connection Caching
-
- An Oracle connection pool encapsulates a single
physical connection - A connection cache provides a means for wrapping
and managing multiple physical connections - Connection caching uses connection pooling since
each physical database connection in the cache is
represented using a pooled connection object - The Oracle extension packages include
Oracle-specific connection caching interfaces
such as OracleConnectionCache to define the
Oracle connection-caching framework
27Oracle Connection Caching
- Client calls the getConnection() method of the
connection cache - The connection cache first checks if there are
any available pooled connections in the cache - If a connection pool instance is not available, a
new one is created - Next, the cache checks if any of the
PooledConnection objects in the cache have a
connection instance available - If there are not any free connection instances,
then the cache will create a new PooledConnection
object - Each connection pool instance created for the
cache has an event listener
28Oracle Connection Caching
- Client calls the close() method on the connection
instance - an event is triggered on the event listener
associated with the PooledConnection instance
that created the connection wrapper - At this point, the connection instance for the
PooledConnection is freed, but the
PooledConnection remains in the cache
29Oracle Connection Caching Implementation
- Oracle offers a simple implementation of
connection caching and connection event listeners
that can be used for basic connection caching
functionality. - The OracleConnectionCacheImpl class implements
the OracleConnectionCache interface, and also
extends the OracleDataSource class - remember, for the event listener magic to work
you must implement the DataSource interface
30Oracle Connection Caching Implementation
Oracle Connection Caching Class Diagram
31Managing an Oracle Connection Caching
- Two ways to manage a Connection Cache
- Define the minimum and maximum number of
PooledConnection objects in the cache - The maximum and minimum number of
PooledConnection objects allowed in the
connection cache is set by using the
setMaxLimit() and setMinLimit() methods of the
OracleConnectionCacheImpl class - Define the behavior of the cache when a
connection request cannot be met by current cache
size by choosing a connection cache scheme
32Managing an Oracle Connection Caching
- Three types of Connection Cache Schemes
- Dynamic
- Cache will create another PooledConnection object
- When the connection instance that caused the new
PooledConnection object to be created is closed,
the related PooledConnection object is closed as
well - Fixed Wait
- Requesting client is forced to wait until one of
the existing PooledConnection objects has a free
connection instance
33Managing an Oracle Connection Caching
- Three types of Connection Cache Schemes (cont)
- Fixed with no Wait
- Immediately rejects the request if no connection
instances are available and returns null - The connection cache scheme is set using the
setCacheScheme() method
34Oracle Connection Caching Implementation
- Steps to implement an Oracle Connection Cache
- Create an OracleConnectionCacheImpl object
- Use the default constructor
- - OR -
- Pass an already setup ConnectionPoolDataSource
object into the OracleConnectionCacheImpl
constructor - - OR -
- Use the setConnectionPoolDataSource(ConnectionPool
DataSource d) method of the OracleConnectionCacheI
mpl class. - If either of these alternate approaches is taken,
then skip Step 2.
35Oracle Connection Caching Implementation
- Steps to implement an Oracle Connection Cache
(Cont) - Set the attributes on the data source necessary
to define the connection parameters - The OracleConnectionCacheImpl is also the
DataSource (since it extends OracleDataSouce), so
there is no need to create another object - This is accomplished by using the setter
methods defined for the DataSource interface,
e.g. setServerName("IP address")
36Oracle Connection Caching Implementation
- Steps to implement an Oracle Connection Cache
(Cont) - Request one or more Connection instances from the
connection cache by calling the getConnection()
method of the OracleConnectionCacheImpl class - When finished with the connection instance,
return the connection to the pool using the
close() method. - Close the connection cache
37Oracle Connection Caching - Example 4 The
HRServlet
- Example 4 ties together the concepts of Oracle
Connection Caching - A simple Servlet that will produce a web page
containing a table of employee data for a given
department - Based upon the HR sample schema shipped with
Oracle 9.2
38Oracle Connection Caching - Example 4 The
HRServlet
- SQLgt desc employees
- Name Null?
Type - -----------------------------------------
-------- --------------- - EMPLOYEE_ID NOT
NULL NUMBER(6) - FIRST_NAME
VARCHAR2(20) - LAST_NAME NOT
NULL VARCHAR2(25) - EMAIL NOT
NULL VARCHAR2(25) - PHONE_NUMBER
VARCHAR2(20) - HIRE_DATE NOT
NULL DATE - JOB_ID NOT
NULL VARCHAR2(10) - SALARY
NUMBER(8,2) - COMMISSION_PCT
NUMBER(2,2) - MANAGER_ID
NUMBER(6) - DEPARTMENT_ID
NUMBER(4)
39Oracle Connection Caching - Example 4 The
HRServlet
- SQLgt desc departments
- Name Null?
Type - -----------------------------------------
-------- --------------- - DEPARTMENT_ID NOT
NULL NUMBER(4) - DEPARTMENT_NAME NOT
NULL VARCHAR2(30) - MANAGER_ID
NUMBER(6) - LOCATION_ID
NUMBER(4)
40Oracle Connection Caching - Example 4 The
HRServlet
- Key concepts of the HR example
- The Servlet makes use of a data access class,
which encapsulates the database retrieval logic
and makes use of the pooled connection retrieved
from the cache - In this manner, the Servlet is only responsible
for properly displaying employee data returned by
the data access class and presentation specific
logic is not mixed with data access specific
logic. - Connection Caching Implementation hidden from
data access class - Data retrieval implementation hidden from the HR
servlet
41Oracle Connection Caching - Example 4 The
HRServlet
Example 4 Class Diagram
42Oracle Connection Caching - Example 4 The
HRServlet
- ConnectionConstants
- The ConnectionConstants interface holds the
connection details to achieve simple abstraction - Could be replaced by a properties file or JNDI
lookup functionality.
public interface ConnectionConstants public
static final String SERVER_NAME "IP" public
static final String DATABASE_NAME "SID"
public static final String DRIVER_TYPE "thin"
public static final int PORT_NUMBER 1521
public static final String USER "User_Name"
public static final String PASSWORD
"Password" public static final String
CACHE_SCHEME "DYNAMIC_SCHEME" public static
final int MAX_CACHE_SIZE 20 public static
final int MIN_CACHE_SIZE 5
43Oracle Connection Caching - Example 4 The
HRServlet
- DBConnectionManager
- Provides the user interface for clients
requesting connection instances through a
getConnection() method - The client class is the HRDataAccessor class
- Uses the Singleton design pattern to ensure only
1 instance of the DBConnectionManager - Necessary so clients get Connection instances
from the same cache - The Singleton design pattern constrains a class
to a single instance, accessible to clients by
calling the static getInstance() method - A Singleton class cannot be instantiated directly
by the client because the class constructors are
made private
44Oracle Connection Caching - Example 4 The
HRServlet
- DBConnectionManager (cont)
import java.util. import java.io. import
java.sql. import javax.sql. import
oracle.jdbc.pool. import oracle.jdbc.pool.Oracle
ConnectionCacheImpl public class
DBConnectionManager private static
DBConnectionManager instance private static
OracleConnectionCacheImpl connectionCache
private static ConnectionConstants constants
private DBConnectionManager () throws
SQLException init() public static
DBConnectionManager getInstance()
throws SQLException if ( instance null)
instance new DBConnectionManager()
return instance
45Oracle Connection Caching - Example 4 The
HRServlet
- DBConnectionManager (cont)
private void init() throws SQLException //
Create an OracleConnectionCacheImpl object
connectionCache new OracleConnectionCacheImpl()
//Set the physical connection attributes by
using ConnectionConstants connectionCache.setSe
rverName(ConnectionConstants.SERVER_NAME)
connectionCache.setDatabaseName(ConnectionConstant
s.DATABASE_NAME) connectionCache.setPortNumber
(ConnectionConstants.PORT_NUMBER)
connectionCache.setDriverType(ConnectionConstants.
DRIVER_TYPE) connectionCache.setUser(Connectio
nConstants.USER) connectionCache.setPasswo
rd(ConnectionConstants.PASSWORD) //Set
the max and min size and connection caching
scheme connectionCache.setMaxLimit(ConnectionCo
nstants.MAX_CACHE_SIZE) connectionCache.setMin
Limit(ConnectionConstants.MIN_CACHE_SIZE)
connectionCache.setCacheScheme(ConnectionConstants
.CACHE_SCHEME)
46Oracle Connection Caching - Example 4 The
HRServlet
- DBConnectionManager (cont)
public static Connection getConnection()
throws SQLException Connection conn
connectionCache.getConnection() if (conn
null) throw new SQLException
("Maximum number of connections in pool
exceeded") return conn public
void close() throws SQLException
connectionCache.close()
47Oracle Connection Caching - Example 4 The
HRServlet
- HRDataAccessor
- Provides data access to the HR Servlet
- Retrieves an instance of the DBConnectionManager
to get a Connection, prepares the JDBC Statement,
executes the query, and returns the ResultSet as
a basic Java List object - The getEmployees()method performs the task of
retrieving the employee details for a specified
department name. - Returns a java.util.List in order to fully
insulate the HR Servlet from the fact that the
employee information was retrieved from a
database
48Oracle Connection Caching - Example 4 The
HRServlet
import java.util. import java.sql. public
class HRDataAccessor private
DBConnectionManager connManager private
Connection conn private PreparedStatement
statement private ResultSet rs public
List getEmployees (String departmentName) throws
Exception ArrayList outputList new
ArrayList() try try
connManager DBConnectionManager.getInstance()
catch (Exception e) e.printStackTrace()
49Oracle Connection Caching - Example 4 The
HRServlet
conn DBConnectionManager.getConnection()
statement conn.prepareStatement (
"select e1.employee_id, e1.first_name,
e1.last_name, " " e2.first_name
man_first, e2.last_name man_last " "
from employees e1, employees e2, departments d "
" where e1.manager_id e2.employee_id
" " and e1.department_id
d.department_id " " and
d.department_name ?") statement.setString
(1,departmentName) rs statement.executeQu
ery()
50Oracle Connection Caching - Example 4 The
HRServlet
while (rs.next()) List rowList new
ArrayList() rowList.add(rs.getString("employee_
id")) rowList.add(rs.getString("first_name"))
rowList.add(rs.getString("last_name"))
rowList.add(rs.getString("man_first"))
rowList.add(rs.getString("man_last"))
outputList.add(rowList) rs.close()
statement.close() conn.close() catch
(SQLException e) throw new Exception
(e.getMessage()) return outputList
51Oracle Connection Caching - Example 4 The
HRServlet
- HRServlet
- The HRServlet gets the department name from the
http request, gets the employee data from the
HRDataAccessor class by calling the
getEmployees() method, and displays the results
in an HTML table. - Has no knowledge of Connection Caching, or any
JDBC classes or interfaces - Gets back a generic List of Employee data
52Oracle Connection Caching - Example 4 The
HRServlet
- import java.io.
- import java.util.
- import javax.servlet.
- import javax.servlet.http.
- public class HRServlet extends HttpServlet
- public void init()
-
- protected void doGet (HttpServletRequest
request, - HttpServletResponse
response) - throws
ServletException, IOException - ServletOutputStream out
- String departmentName
- out response.getOutputStream()
- List employeeList
-
53Oracle Connection Caching - Example 4 The
HRServlet
- //Get the departmentName id from the
HttpServletRequest - departmentName request.getParameter("departm
ent") - //Set to empty string if null, otherwise trim
- departmentName
- (departmentName null) ? ""
departmentName.trim() - //Create the HTML page headers using the
output stream - out.println("lthtmlgt")
- out.println("ltheadgt")
- out.println("lttitlegt" departmentName "
Employeeslt/titlegt") - out.println("lt/headgt")
- out.println("ltbodygt")
- //Display the page header
- out.println("lth1gt" departmentName "
Employeeslt/h1gtltbrgt") -
54Oracle Connection Caching - Example 4 The
HRServlet
- try
- HRDataAccessor dataAccess new
HRDataAccessor() - employeeList dataAccess.getEmployees(depar
tmentName) -
- catch (Exception e)
- out.println("Error retrieving database
connection") - out.println("lt/bodygt")
- out.println("lt/htmlgt")
- return
-
- if (employeeList null)
- out.println("No Data Found for Department "
- departmentName)
- out.println("lt/bodygt")
- out.println("lt/htmlgt")
- return
-
55Oracle Connection Caching - Example 4 The
HRServlet
- //Start the table of employees
- out.print ("lttable width\"400\" border\"0\"
") - out.print ("cellpadding\"3\"
cellspacing\"1\" ") - out.println ("bgcolor\"000000\"gt")
- out.println("lttr bgcolor\"F4F4F4\"gt")
- out.println("lttdgtltbgtEmployee IDlt/bgtlt/tdgt")
- out.println("lttdgtltbgtEmployee Namelt/bgtlt/tdgt")
- out.println("lttdgtltbgtEmployee's
Managerlt/bgtlt/tdgt") - out.println("lt/trgt")
- Iterator empIter employeeList.iterator()
- while (empIter.hasNext())
- ArrayList row (ArrayList)empIter.next()
- Iterator rowIter row.iterator()
- out.println("lttr bgcolor\"F4F4F4\"gt")
-
56Oracle Connection Caching - Example 4 The
HRServlet
- while (empIter.hasNext())
- ArrayList row (ArrayList)empIter.next()
- Iterator rowIter row.iterator()
- out.println("lttr bgcolor\"F4F4F4\"gt")
- while (rowIter.hasNext())
- out.println("lttdgtltbgt"
rowIter.next().toString() "lt/bgtlt/tdgt") - out.println("lttdgtltbgt"
rowIter.next().toString() - ", "
rowIter.next().toString() "lt/bgtlt/tdgt") - out.println("lttdgtltbgt"
rowIter.next().toString() - ", "
rowIter.next().toString() "lt/bgtlt/tdgt") -
- out.println("lt/trgt")
-
-
-
57Oracle Connection Caching - Example 4 The
HRServlet
- out.println("lt/tablegt")
- out.println("lt/bodygt")
- out.println("lt/htmlgt")
-
- public void destroy()
- super.destroy()
-
-
-
58Oracle Connection Caching - Example 4 The
HRServlet
- HRServlet (cont)
- Running the HRServlet yields the final output
59Conclusion
- The JDBC 2.0 API support for connection pooling,
paired with the Oracle extensions for connection
caching, provides a relatively simple,
easy-to-use mechanism for implementing connection
pooling in a web application. - Presentation provided an example of using the
OracleConnectionCacheImpl connection caching
implementations to deploy a basic Servlet.
60References
Carnell, John Holm, Bjarki Horton, Ann and
others. Professional Oracle 8i Application
Programming with Java, PL/SQL, and XML.
Birmingham, UK Wrox Press Ltd, 2000. Price,
Jason. Oracle 9i JDBC Programming. Berkley, Ca
Oracle Press, McGraw-Hill/Osbourne, 2002. Hom,
Bjarki Carnell, John Goodman, Jaeda, and
others. Oracle 9i Java Programming Solutions for
Developers Using PL/SQL and Java. Birmingham, UK
Wrox Press Ltd, 2001. Oracle9i JDBC Developers
Guide and Reference. Release 2 (9.2). Part No.
A96654-01 Oracle9i Sample Schemas. Release 2
(9.2). Part No. A96539-01 http//devtrends.oracle.
com Cameron ORourkes Developer Trends Website
http//java.sun.com/products/jdbc/ Sun
Microsystems JDBC Data Access API documentation
and tutorials Special thanks to Stephen Gingras,
Northrop Grumman Mission Systems, for technical
Java consultation
61Questions?
- For more detailed discussion of the materials
presented, and complete code examples, please see
the full document file at http//www.metaway.com