Configuring a Data Source

Chapter 10. External Services

Topics in This Chapter

  • "Database Access with JDBC" on page 451

  • "Configuring a Data Source" on page 457

  • "An Introduction to LDAP" on page 473

  • "Managing Configuration Information" on page 483

  • "Container-Managed Authentication and Authorization" on page 505

  • "Using Web Services" on page 516

In this chapter, you learn how to access external services from your JSF application. We show you how to connect to databases, directory services, and web services. Our primary interest lies in the clean separation between the application logic and the configuration of resources.

Database Access with JDBC

In the following sections, we give you a brief refresher of the JDBC (Java Database Connectivity) API. We assume that you are familiar with basic SQL (Structured Query Language) commands. A more thorough introduction to these topics can be found in Horstmann and Cornell, 2004, 2005. Core Java 2, vol. 2, chap. 4. For your convenience, here is a brief refresher of the basics.

Issuing SQL Statements

To issue SQL statements to a database, you need a connection object. There are various methods of obtaining a connection. The most elegant one is to use a data source.

  DataSource source = . . .   Connection conn = source.getConnection();

The section "Accessing a Container-Managed Resource" on page 462 describes how to obtain a data source in the GlassFish and Tomcat containers. For now, we assume that the data source is properly configured to connect to your favorite database.

Once you have the Connection object, you create a Statement object that you use to send SQL statements to the database. You use the executeUpdate method for SQL statements that update the database and the executeQuery method for queries that return a result set.

  Statement stat = conn.createStatement();   stat.executeUpdate("INSERT INTO Users VALUES ('troosevelt', 'jabberwock')");   ResultSet result = stat.executeQuery("SELECT * FROM Users");     

The ResultSet class has an unusual iteration protocol. First you call the next method to advance the cursor to the first row. (The next method returns false if no further rows are available.) Then you call the getString method to get a field value as a string. For example,

  while (result.next()) {      username = result.getString("username");      password = result.getString("password");      . . .   }

When you are done using the database, be certain that you close the connection. To ensure that the connection is closed under all circumstances, even when an exception occurs, wrap the query code inside a try/finally block, like this:

  Connection conn = source.getConnection();   try {     . . .   }   finally {      conn.close();   }

Of course, there is much more to the JDBC API, but these simple concepts are sufficient to get you started.

Note

Here we show you how to execute SQL statements from your web application. This approach is fine for lightweight applications that have modest storage requirements. For complex applications, you would want to use an object-relational mapping technology such as JPA (the Java Persistence Architecture) or Hibernate.


Connection Management

One of the more vexing issues for the web developer is the management of database connections. There are two conflicting concerns. First, opening a connection to a database can be time consuming. Several seconds may elapse for the processes of connecting, authenticating, and acquiring resources to be completed. Thus, you cannot simply open a new connection for every page request.

On the flip side, you cannot keep open a huge number of connections to the database. Connections consume resources, both in the client program and in the database server. Commonly, a database puts a limit on the maximum number of concurrent connections that it allows. Thus, your application cannot simply open a connection whenever a user logs on and leave it open until the user logs off. After all, your user might walk away and never log off.

One common mechanism for solving these concerns is to pool the database connections. A connection pool holds database connections that are already opened. Application programs obtain connections from the pool. When the connections are no longer needed, they are returned to the pool, but they are not closed. Thus, the pool minimizes the time lag of establishing database connections.

Implementing a database connection pool is not easy, and it certainly should not be the responsibility of the application programmer. As of version 2.0, JDBC supports pooling in a pleasantly transparent way. When you receive a pooled Connection object, it is actually instrumented so that its close method merely returns it to the pool. It is up to the application server to set up the pool and to give you a data source whose getConnection method yields pooled connections.

Each application server has its own way of configuring the database connection pool. The details are not part of any Java standard the JDBC specification is completely silent on this issue. In the next section, we describe how to configure GlassFish and Tomcat for connection pooling. The basic principle is the same with other application servers, but of course the details may differ considerably.

To maintain the pool, it is still essential that you close every connection object when you are done using it. Otherwise the pool will run dry, and new physical connections to the database will need to be opened. Properly closing connections is the topic of the next section.

Plugging Connection Leaks

Consider this simple sequence of statements:

  DataSource source = ...   Connection conn = source.getConnection();   Statement stat = conn.createStatement();   String command = "INSERT INTO Users VALUES ('troosevelt', 'jabberwock')";   stat.executeUpdate(command);   conn.close();

The code looks clean we open a connection, issue a command, and immediately close the connection. But there is a fatal flaw. If one of the method calls throws an exception, the call to the close method never happens!

In that case, an irate user may resubmit the request many times in frustration, leaking another connection object with every click.

To overcome this issue, always place the call to close inside a finally block:

  DataSource source = ...   Connection conn = source.getConnection();   try {      Statement stat = conn.createStatement();      String command = "INSERT INTO Users VALUES ('troosevelt', 'jabberwock')";      stat.executeUpdate(command);   }   finally {      conn.close();   }     

This simple rule completely solves the problem of leaking connections.

The rule is most effective if you do not combine this try/finally construct with any other exception handling code. In particular, do not attempt to catch a SQLException in the same try block:

  // we recommend that you do NOT do this   Connection conn = null;   try {      conn = source.getConnection();      Statement stat = conn.createStatement();      String command = "INSERT INTO Users VALUES ('troosevelt', 'jabberwock')";      stat.executeUpdate(command);   }   catch (SQLException) {      // log error   }   finally {      conn.close(); // ERROR   }     

That code has two subtle mistakes. First, if the call to getConnection throws an exception, then conn is still null, and you can't call close. Moreover, the call to close can also throw a SQLException. You could clutter up the finally clause with more code, but the result is a mess. Instead, use two separate try blocks:

  // we recommend that you use separate try blocks   try {      Connection conn = source.getConnection();      try {         Statement stat = conn.createStatement();         String command = "INSERT INTO Users VALUES ('troosevelt', 'jabberwock')";         stat.executeUpdate(command);      }      finally {         conn.close();      }   }   catch (SQLException) {      // log error   }     

The inner try block ensures that the connection is closed. The outer try block ensures that the exception is logged.

Note

Of course, you can also tag your method with throws SQLException and leave the outer try block to the caller. That is often the best solution.


Using Prepared Statements

A common optimization technique for JDBC programs is the use of the Prepared-Statement class. You use a prepared statement to speed up database operations if your code issues the same type of query multiple times. Consider the lookup of user passwords. You will repeatedly need to issue a query of the form

  SELECT password FROM Users WHERE username=...

A prepared statement asks the database to precompile a query that is, parse the SQL statement and compute a query strategy. That information is kept with the prepared statement and reused whenever the query is reissued.

You create a prepared statement with the prepareStatement method of the Connection class. Use a ? character for each parameter:

  PreparedStatement stat = conn.prepareStatement(      "SELECT password FROM Users WHERE username=?");

When you are ready to issue a prepared statement, first set the parameter values.

  stat.setString(1, name);

(Note that the index value 1 denotes the first parameter.) Then issue the statement in the usual way:

  ResultSet result = stat.executeQuery();

At first glance, it appears as if prepared statements would not be of much benefit in a web application. After all, you close the connection whenever you complete a user request. A prepared statement is tied to a database connection, and all the work of establishing it is lost when the physical connection to the database is terminated.

However, if the physical database connections are kept in a pool, then there is a good chance that the prepared statement is still usable when you retrieve a connection. Many connection pool implementations will cache prepared statements.

When you call prepareStatement, the pool will first look inside the statement cache, using the query string as a key. If the prepared statement is found, then it is reused. Otherwise, a new prepared statement is created and added to the cache.

All this activity is transparent to the application programmer. You request PreparedStatement objects and hope that, at least some of the time, the pool can retrieve an existing object for the given query.

Caution

You cannot keep a PreparedStatement object and reuse it beyond a single request scope. Once you close a pooled connection, all associated PreparedStatement objects also revert to the pool. Thus, you should not hang on to PreparedStatement objects beyond the current request. Instead, keep calling the prepareStatement method with the same query string, and chances are good that you will get a cached statement object.


Note

Even if you are not interested in performance, there is another good reason to use prepared statements: to guard against SQL injection attacks.



Core JavaServerT Faces
Core JavaServer(TM) Faces (2nd Edition)
ISBN: 0131738860
EAN: 2147483647
Year: 2004
Pages: 84

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net