1. Trang chủ
  2. » Công Nghệ Thông Tin

JNDI and Connection Pooling

16 354 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề JNDI and Connection Pooling
Thể loại Chapter
Định dạng
Số trang 16
Dung lượng 45,3 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

DataSource properties follow the JavaBeans design pattern, and therefore, the following getter/setter methods are in a DataSource object: public synchronized void setDatabaseNameString d

Trang 1

An object-oriented programming language derives its strength from two areas First, you have the constructs of the programming language itself that allow you to write well-structured objects to extend that language Second, you have the extensive libraries of APIs that have been written to provide standard functionality Think for a moment about how APIs are created A software engineer does not just wake up one morning and have an entire API worked out in every detail Instead, an API's design is based on the experiences of professionals like you, who, over time, have gained insight through problem solving as to what is needed in an API to make it a useful part of developing an application Accordingly, over time, an API evolves through this community process to better fit the needs of the programming community

When it comes to the JDBC API, specifically the DriverManager facility, there is an evolution taking place In Chapter 4, we needed to put a significant amount of code around

DriverManager to implement a sharable connection facility It took even more work to make our sharable connections cacheable With the Java 2 Enterprise Edition (J2EE), a framework has been defined for sharing and caching connections This framework is the JDBC 2.0 Extension API In this chapter, we'll cover the JDBC 2.0 Extension API, which is a another set of JDBC interfaces, along with Oracle's implementation of these interfaces We'll also look at a functional caching object using Oracle's connection caching implementation Let's begin our journey through the new API with a look at the generic source for database connections, the DataSource class

7.1 DataSources

A DataSource object is a factory for database connections Oracle's implementations of data sources are database connection objects that encapsulate the registration of the appropriate database driver and the creation of a connection using predetermined parameters DataSource objects are typically bound with the Java Naming and Directory Interface (JNDI), so they can be allocated using a logical name at a centrally managed facility such as an LDAP directory

7.1.1 OracleDataSources

Oracle implements the DataSource interface with class OracleDataSource Table 7-1 lists the standard properties implemented by a DataSource object

Table 7-1 Standard DataSource properties

databaseName String Oracle SID

dataSourceName String Name of the underlying DataSource class

description String Description of the DataSource

networkProtocol String For OCI driver, determines the protocol used

password String Oracle password

portNumber int Oracle listener port number

serverName String DNS alias or TCP/IP address of the host

user String Oracle username

Trang 2

DataSource properties follow the JavaBeans design pattern, and therefore, the following

getter/setter methods are in a DataSource object:

public synchronized void setDatabaseName(String databseName )

public synchronized String getDatabaseName( )

public synchronized void setDataSourceName(String dataSourceName) public synchronized String getDataSourceName( )

public synchronized void setDescription(String description)

public synchronized String getDescription( )

public synchronized void setNetworkProtocol(String networkProtocol) public synchronized String getNetworkProtocol( )

public synchronized void setPassword(String password)

public synchronized void setPortNumber(int portNumber)

public synchronized int getPortNumber( )

public synchronized void setServerName(String ServerName)

public synchronized String getServerName( )

public synchronized void setUser(String user)

public synchronized String getUser( )

The OracleDataSource class has an additional set of proprietary attributes These are listed in Table 7-2

Table 7-2 OracleDataSource properties

Property Data

driverType String

kprb for server-side internal connections oci8 for client-side OCI driver

thin for client- or server-side Thin driver

url String A convenience property incorporating properties, such as

PortNumber, user, and password, that make up a database URL tnsEntryName String TNS names address for use with the OCI driver

And these are the OracleDataSource property getter/setter methods:

public synchronized void setDriverType(String dt)

public synchronized String getDriverType( )

public synchronized void setURL(String url)

public synchronized String getURL( )

public synchronized void setTNSEntryName(String tns)

public synchronized String getTNSEntryName( )

Common sense prevails with these settings For example, there is no getPassword( ) method, because that would create a security problem In addition, the properties have a specific

precedence If you specify a url property, then any properties specified in the url override those that you specify by any of the other setter methods If you do not set the url property but instead specify the tnsEntryName property, then any related setter methods are overridden by the values in the TNS entry name's definition Likewise, if you are using the OCI driver and specify a network protocol of IPC, then any communication properties are ignored because the IPC protocol establishes a direct connection to the database Finally, a username and password passed in the getConnection( ) method override those specified in any other way Note that you must always specify a username and password with whatever means you choose

Trang 3

7.1.2 Getting a Connection from a DataSource

To get a connection from a DataSource use one of the two available getConnection( ) methods:

public Connection getConnection( )

throws SQLException

public Connection getConnection(String username, String password) throws SQLException

The first method creates a new Connection object with the username and password settings from the DataSource The second method overrides the username and password in the

DataSource

Now that you have an understanding of data sources, let's look at Example 7-1, which is an application to test the Thin driver using a DataSource

Example 7-1 An application using a DataSource to connect

import java.sql.*;

import oracle.jdbc.pool.*;

class TestThinDSApp {

public static void main (String args[])

throws ClassNotFoundException, SQLException {

// These settings are typically confi gured in JNDI,

// so they are implementation-specific

OracleDataSource ds = new OracleDataSource ( );

ds.setDriverType("thin");

ds.setServerName("dssw2k01");

ds.setPortNumber(1521);

ds.setDatabaseName("orcl"); // sid

ds.setUser("scott");

ds.setPassword("tiger");

Connection conn = ds.getConnection( );

Statement stmt = conn.createStatement( );

ResultSet rset = stmt.executeQuery(

"select 'Hello Thin driver data source tester '||" +

"initcap(USER)||'!' result from dual");

if (rset.next( ))

System.out.println(rset.getString(1));

rset.close( );

stmt.close( );

conn.close( );

}

}

First, our test application, TestThinDSApp, creates a new OracleDataSource object and then initializes its properties that are relevant to the Thin driver The OracleDataSource object implements the DataSource interface, so OracleDataSource is also considered to be a DataSource object Next, the program gets a connection from the DataSource using the getConnection( ) method Finally, just to prove everything is working OK, the application queries the database, and closes the connection

Trang 4

So what have we accomplished using an OracleDataSource object? Recall that in Chapter 4

we established a connection using the following code:

Class.forName("oracle.jdbc.driver.OracleDriver");

Connection conn =

DriverManager.getConnection(

"jdbc:oracle:thin:@dssw2k01:1521:orcl","scott","tiger");

Now, using an OracleDataSource object, our code to establish a connection looks like: OracleDataSource ds = new OracleDataSource( );

ds.setDriverType("thin");

ds.setServerName("dssw2k01");

ds.setPortNumber(1521);

ds.setDatabaseName("orcl"); // sid

ds.setUser("scott");

ds.setPassword("tiger");

Connection conn = ds.getConnection( );

What's going on here? Our code is actually longer, which doesn't seem to improve things much, does it? But from another perspective, using a DataSource does represent an improvement, because a DataSource implements the Serializable interface, which means it can be bound using JNDI to a directory service What does that mean? It means we can define our connection parameters once, in one place, and use a logical name to get our connection from JNDI How does this help us? Let's say, for example, that we have 1,000 programs that use the specific connection parameters shown in Example 7-1 Let's further assume that we now have to move our database to another host If you wrote your programs using the DriverManager facility, you'll need to modify and compile all 1,000 programs However, if you used a DataSource bound to a directory using JNDI, then you need to change only one entry in the directory, and all the programs will use the new information

7.1.3 Using a JNDI DataSource

Let's take a look at a couple of sample applications that illustrate the power and utility of using data sources that are accessed via JNDI The examples use Sun's file-based JNDI

implementation You can download the class files for Sun's JNDI filesystem implementation at http://java.sun.com/products/jndi/index.html

First, the program in Example 7-2, TestDSBind, creates a logical entry in a JNDI directory to store our DataSource It uses Sun's JNDI filesystem implementation as the directory After that, we'll look at another program that uses the DataSource created by the first

My DataSource bind program, TestDSBind, starts by creating a Context variable named ctx Next, it creates a Properties object to use in initializing an initial context In layman's terms, that means it creates a reference to the point in the local filesystem where our program should store its bindings The program proceeds by creating an initial Context and storing its reference in ctx Next, it creates an OracleDataSource and initializes its properties Why an OracleDataSource and not a DataSource? You can't really use a DataSource for binding; you have to use an OracleDataSource, because the setter/getter methods for the properties are implementation- or vendor-specific and are not part of the DataSource interface Last, the program binds our OracleDataSource with the name joe by calling the Context.bind( ) method

Example 7-2 An application that binds a JNDI DataSource

import java.sql.*;

Trang 5

import java.util.*;

import javax.naming.*;

import oracle.jdbc.pool.*;

public class TestDSBind {

public static void main (String args [])

throws SQLException, NamingException {

// For this to work you need to create the

// directory /JNDI/JDBC on your filesystem first

Context ctx = null;

try {

Properties prop = new Properties( );

prop.setProperty(

Context.INITIAL_CONTEXT_FACTORY,

"com.sun.jndi.fscontext.RefFSContextFactory");

prop.setProperty(

Context.PROVIDER_URL,

"file:/JNDI/JDBC");

ctx = new InitialContext(prop);

}

catch (NamingException ne) {

System.err.println(ne.getMessage( ));

}

OracleDataSource ds = new OracleDataSource( );

ds.setDriverType("thin");

ds.setServerName("dssw2k01");

ds.setPortNumber(1521);

ds.setDatabaseName("orcl");

ds.setUser("scott");

ds.setPassword("tiger");

ctx.bind("joe", ds);

}

}

Create a directory, JNDI, on your hard drive, and then create a subdirectory, JDBC, in your JNDI directory Compile Example 7-2 and execute it Assuming you get no error messages, you should find a bindings file in your new JDBC subdirectory This file holds the values for a

serialized form of your DataSource logically named joe This means that we can later retrieve a new connection by referencing a resource named joe

Now that we have a directory entry, let's test it with our next program, TestDSLookup, in

Example 7-3 First, TestDSLookUp creates an initial context just like TestDSBind did Next, it uses the Context.lookup( ) method to look up and instantiate a new DataSource from our serialized version of joe Finally, the program queries the database and closes the connection Pretty cool huh? When using DriverManager, you typically must specify the JDBC driver and database URL in your source code By using a DataSource together with JNDI, you can write code that is independent of a JDBC driver and of a database URL

Example 7-3 An application that uses a JNDI DataSource

import java.sql.*;

import javax.sql.*;

import javax.naming.*;

Trang 6

import java.util.*;

public class TestDSLookUp {

public static void main (String[] args)

throws SQLException, NamingException {

Context ctx = null;

try {

Properties prop = new Properties( );

prop.setProperty(

Context.INITIAL_CONTEXT_FACTORY,

"com.sun.jndi.fscontext.RefFSContextFactory");

prop.setProperty(

Context.PROVIDER_URL,

"file:/JNDI/JDBC");

ctx = new InitialContext(prop);

}

catch (NamingException ne) {

System.err.println(ne.getMessage( ));

}

DataSource ds = (DataSource)ctx.lookup("joe");

Connection conn = ds.getConnection( );

Statement stmt = conn.createStatement( );

ResultSet rset = stmt.executeQuery(

"select 'Hello Thin driver data source tester '||" +

"initcap(USER)||'!' result from dual");

if (rset.next( ))

System.out.println(rset.getString(1));

rset.close( );

stmt.close( );

conn.close( );

}

}

7.1.4 Caveats

I hope you can appreciate the long-term gain of using DataSources with JNDI rather than embedding connections in your code:

• It makes your code independent of a JDBC driver

• It makes your code independent of a database URL

• It allows you to look up the driver and URL in one operation from anywhere on the network

DataSource objects do, however, have a few drawbacks One is that you can't use Oracle Advanced Security with the Thin driver, because there is no way to set the oracle.net

properties for data encryption and integrity This is because oracle.net properties are not a part of the standard, nor are they part of Oracle's implementation-specific set of DataSource properties Another drawback to using DataSources is that you have to make the investment in

an LDAP directory to truly leverage the use of JNDI, and that can be quite costly

In addition to the drawbacks I've mentioned, there are a few DataSource behaviors you should

be aware of One concerns the logging feature There are two methods you can use to set and

Trang 7

get the log writer for a DataSource A log writer is a PrintWriter object used by the driver to write its activities to a log file They are:

public synchronized void setLogWriter(PrintWriter pw)

throws SQLException

public synchronized PrintWriter getLogWriter( )

throws SQLException

As with the DriverManager facility, logging is disabled by default You will always need to call the setLogWriter( ) method after a DataSource has been instantiated, even if you set the log writer before you bind it to a directory Why? Because the PrintWriter you specify in the setLogWriter( ) method is transient and therefore cannot be serialized A second behavior you should be aware of is that when DataSource logging is enabled, it bypasses

DriverManager's logging facility

There are also two methods you can use to set and get the login timeout, which is the amount of time that an idle connection should be kept open The methods are:

public synchronized void setLoginTimeout(int seconds) throws SQLException

public synchronized int getLoginTimeout( )

throws SQLException

Now that you have a firm grasp of how and when to use a DataSource object, let's continue our investigation of the JDBC 2.0 Extension API with a look at the connection pooling interface

ConnectionPoolDataSource

7.2 Oracle's Connection Cache

Recall that in Chapter 4 we talked about a cached pool of connections used by servlets When a servlet needed a connection, it drew one from the pool The servlet did its work, and when it was done, it returned the connection back to the pool The benefit of using cached connections is that

a servlet does not need to go through the resource-intensive task of opening a new database connection each time the servlet is invoked Also in Chapter 4, I showed a rudimentary

connection caching tool Rudimentary as it was, it still required a fair bit of rather complex code to implement As part of the JDBC 2.0 Extension API, Oracle provides a ready-made connection cache interface along with a sample implementation Instead of wasting your precious time doing something that has already been done for you, you can use Oracle's connection cache

immediately and in turn concentrate on the business problem at hand

At the heart of Oracle's connection caching framework is the connection pool data source It's important you understand what that is and how it works before getting into the details of the

connection cache framework itself

7.2.1 ConnectionPoolDataSources

A ConnectionPoolDataSource is a DataSource that can be pooled Instead of returning a Connection object as a DataSource object does, a ConnectionPoolDataSource returns a PooledConnection object A PooledConnection object itself holds a physical database

connection that is pooled In turn, a PooledConnection returns a Connection object This single layer of indirection allows a ConnectionPoolDataSource to manage

PooledConnection objects

You can use a PooledConnection to add or remove ConnectionEventListeners A

ConnectionEventListener is any Java program thread that wishes to be notified whenever a connection is opened or closed When a Connection is received from or returned to a

PooledConnection, the appropriate ConnectEvent event is triggered to close or return the

Trang 8

Connection object to its associated pool In this case, the Connection object is not the same implementation of the Connection interface utilized by DriverManager Instead, it's a logical implementation managed by the PooledConnection object

The ConnectionPoolDataSource interface is implemented by the class

Oracle-ConnectionPoolDataSource, which extends OracleDataSource This means that all the methods from the OracleDataSource class and ConnectionPoolDataSource interface are available in an OracleConnectionPoolDataSource

The OraclePooledConnection class implements the PooledConnection interface and also provides the following five constructors:

public OraclePooledConnection( )

throws SQLException

public OraclePooledConnection(String url)

throws SQLException

public OraclePooledConnection(String url, String user, String passw ord) throws SQLException

public OraclePooledConnection(Connection pc)

public OraclePooledConnection(Connection pc, boolean autoCommit)

The OracleConnectionEventListener class implements the ConnectionEventListener interface It also provides the following two constructors and one additional method:

public OracleConnectionEventListener( )

public OracleConnectionEventListener(DataSource ds)

public void setDataSource(DataSource ds)

Collectively, these JDBC classes and interfaces, along with Oracle's implementation of them, provide a framework for connection caching However, the topic of how they can be used to build

a connection cache is well beyond the scope of this book Besides, Oracle already provides a connection cache interface and sample implementation Let's look at how you can leverage those

in your programs

7.2.2 Connection Cache Implementation

Let's start our discussion of Oracle's connection cache implementation by defining a few

important terms:

Connection pool

A pool of one or more Connections that use the same properties to establish a physical connection to a database By "properties," I mean things such as databaseName, serverName, portNumber, etc

Connection cache

A cache of one or more physical connections to one or more databases

Pooled connection cache

A cache of one or more connections to the same database for the same username Oracle's connection cache interface is named OracleConnectionCache Together, the

interface and its implementation provide a cache of physical connections to a particular database for a specified username

7.2.2.1 The OracleConnectionCache interface

Oracle's OracleConnectionCache interface defines the following three methods to aid you in managing a connection pool cache:

Trang 9

public void close( )

throws SQLException

public void closePooledConnection(PooledConnection pc)

throws SQLException

public void reusePooledConnection(PooledConnection pc)

throws SQLException

These methods perform the following functions:

close( )

Used to close a logical connection to the database obtained from a

Pooled-Connection A logical connection is a connection that has been allocated from a pool When a logical connection is closed, the connection is simply returned to the pool It may physically remain open but is logically no longer in use

closePooledConnection( )

Used to remove the associated PooledConnection from a connection pool

reusePooledConnection( )

Used to return a PooledConnection to a connection pool

7.2.2.2 The OracleConnectionCacheImpl class

The OracleConnectionCacheImpl class extends OracleDataSource and implements the OracleConnectionCache interface Beyond what OracleConnectionCacheImpl inherits from OracleDataSource and the methods it implements from the OracleConnectionCache interface, the OracleConnectionCacheImpl class provides the following constants,

constructors, and methods:

public final int DYNAMIC_SCHEME

public final int FIXED_RETURN_NULL_SCHEME

public final int FIXED_WAIT_SCHEME

public OracleConnectionCacheImpl( )

throws SQLException

public OracleConnectionCacheImpl(ConnectionPoolDataSource cpds)

throws SQLException

public int getActiveSize( )

public int getCacheSize( )

public void setCacheScheme(int cacheScheme);

public int getCacheScheme( )

public void setConnectionPoolDataSource (ConnectionPoolDataSource cpds) throws SQLException

public void setMinLimit(int minCacheSize)

public int getMinLimit( )

public void setMaxLimit(int maxCacheSize)

public int getMaxLimit( )

The first three constants are used with the setCacheScheme( ) method to specify the caching scheme to be used by a given connection cache implementation Caches usually employ a minimum and maximum number of connections as part of a resource strategy The minimum value keeps a minimum number of connections on hand to speed up the connection process A cache uses the maximum value to limit the amount of operating-system resources utilized This prevents the cache from growing beyond its host's ability to provide resources The

setCacheScheme( ) method's constants control the behavior of the cache when the specified maximum connection limit has been exceeded The values are defined as follows:

DYNAMIC_SCHEME

Trang 10

The cache will create connections above the specified maximum limit when necessary but will in turn close connections as they are returned to the cache until the number of connections is within the maximum limit Connections will never be cached above the maximum limit This is the default setting

FIXED_RETURN_NULL_SCHEME

The cache will return a null connection once the maximum connection limit has been exceeded

FIXED_WAIT_SCHEME

The cache will wait until there is a connection available and will then return it to the calling application

The OracleConnectionCacheImpl class implements two constructor methods, and there are three ways that you can use them to initialize a cache:

1 You can use the default constructor and set the connection properties individually after you've instantiated an object of the class

2 You can use the default constructor to instantiate an object of the class Then you can create a ConnectionPoolDataSource object, initialize it, and pass it as a parameter

to the setConnectionPoolDataSource( ) method

3 You can create and initialize a ConnectionPoolDataSource object and then pass it

as a parameter to the second form of the OracleConnectionCacheImpl constructor The other methods implemented by the OracleConnectionCacheImpl class are

straightforward getter and setter methods They do exactly what their names indicate

7.2.3 A Connection Caching Example

Now that you have an idea of what an OracleConnectionCacheImpl object can do, let's rewrite our caching object from Chapter 4 using Oracle's caching implementation We'll build a new caching object named OCCIConnection that will use the OracleConnectionCacheImpl class to create a modular caching module The overall development process that we'll follow for this example is:

1 We'll create a program that allows us to create a connection pool data source and bind it

to our JNDI directory

2 We'll create a class to implement and manage connection caches

3 We'll test the connection cache using one servlet that retrieves and uses connections and another that displays the current status of the cache

7.2.3.1 Creating and binding a ConnectionPoolDataSource

In my opinion, there's no advantage to using a DataSource unless you also utilize JNDI, so our examples here will once again use Sun's filesystem implementation of JNDI First, we'll create a program named OCCIBind, shown in Example 7-4, to bind a ConnectionPoolDataSource

to our JNDI directory OCCIBind is similar to the TestDSBind program shown in Example 7-2, but this time, we bind an OraclePoolConnectionSource

Example 7-4 An application that binds a ConnectionPoolDataSource

import java.sql.*;

import java.util.*;

Ngày đăng: 29/09/2013, 09:20

TỪ KHÓA LIÊN QUAN

w