Log4j로 검색한 결과 :: 시소커뮤니티[SSISO Community]
 
SSISO 카페 SSISO Source SSISO 구직 SSISO 쇼핑몰 SSISO 맛집
추천검색어 : JUnit   Log4j   ajax   spring   struts   struts-config.xml   Synchronized   책정보   Ajax 마스터하기   우측부분

회원가입 I 비밀번호 찾기


SSISO Community검색
SSISO Community메뉴
[카페목록보기]
[블로그등록하기]  
[블로그리스트]  
SSISO Community카페
블로그 카테고리
정치 경제
문화 칼럼
비디오게임 스포츠
핫이슈 TV
포토 온라인게임
PC게임 에뮬게임
라이프 사람들
유머 만화애니
방송 1
1 1
1 1
1 1
1 1
1

Log4j로 검색한 결과
등록일:2008-06-09 13:29:41
작성자:
제목:JNDI Datasource HOW-TO 문서 ( 자카르타 서울 수리바다님 번역)


JNDI Datasource HOW-TO

목차 Table of Contents

소개 Introduction
데이타베이스 커넥션풀(DBCP) 설정 Database Connection Pool (DBCP) Configurations
Tyrex 커넥션 풀 Tyrex Connection Pool
DBCP를 사용하지 않는 방법들 Non DBCP Solutions
OCI 클라이언트로 Oracle8i 연결 Oracle 8i with OCI client
일반적으로 일어날 수 있는 문제 Common Problems

소개 Introduction

JNDI Datasource 설정은 JNDI-Resources-HOWTO에 상세하게 나와있지만 tomcat-user메일링리스트에서 보면 각각의 설정이 다소 까다롭다고 합니다.

많이 쓰이는 데이타베이스에 대하여 tomcat-user 메일링리스트에 올려진 몇개의 설정 예제가 여기 있습니다.

이들 간단한 메모들은 mysql 설정과 tomcat-user메일링리스트의 피드백에서 나온 것임을 알아야 합니다. 상황에 따라 다릅니다. 만일 좀 더 많은 사람에게 도움이 될 만한 테스트해본 설정이 있거나 어쨌건 이 섹션에 좋은 내용이 될 만한 것이라면 알려주십시오.

데이타베이스 커넥션풀(DBCP) 설정 Database Connection Pool (DBCP) Configurations

DBCP는 JDBC 2.0을 지원합니다. JVM 1.4를 사용하는 시스템에서는 DBCP는 JDBC 3.0을 지원할 것입니다. JVM 1.4에서 DBCP와 JDBC 3.0의 특징을 사용해보았으면 알려주십시오.

설정매개변수 모두를 보려면 DBCP Javadocs에서 BasicDataSource 클래스를 보십시오.

설치

DBCP는 Jakarta-Commons 데이타베이스 커넥션 풀을 사용하는데 그것은 여러개의 Jakarta-Commons 컴포넌트들에 의존합니다.

  • Jakarta-Commons DBCP 1.0
  • Jakarta-Commons Collections 2.0
  • Jakarta-Commons Pool 1.0
이 jar 파일들은 JDBC 드라이버 jar 파일과 함께 $CATALINA_HOME/common/lib에 설치하여야 합니다.
주의:써드 파티 드라이버는 zip 파일이 아닌 jar 파일이어야 하며 Tomcat은 $CATALINA_HOME/common/lib/*.jar만을 클래스패스에 추가합니다.
주의: 이 jar 파일들을 WEB-INF/lib, $JAVA_HOME/jre/lib/ext나 다른 곳에 설치하지 마십시오. $CATALINA_HOME/common/lib 외에 다른 위치에 설치했을때 문제가 생길 수 있습니다.

데이타베이스 커넥션 풀이 새는 것을 막는 방법

데이타베이스 커넥션 풀은 데이타베스의 커넥션들의 풀을 만들고 관리합니다. 이미 있는 커넥션을 재생하여 재사용하는 것은 새 커넥션을 가져오는 것보다 효율적입니다.

커넥션 풀링에는 한가지 문제가 있습니다. 웹 애플리케이션은 명시적으로 ResultSet, Statement, Connection을 닫아야 합니다. 웹 애플리케이션에서 이들 자원들을 닫지 못하게 되면 다시 재사용할 수가 없으며 데이타베이스 커넥션 풀 "구멍"이 생깁니다. 이것은 결국 웹 애플리케이션에서 더 이상 쓸 수 있는 데이타베이스 커넥션이 없을 때 커넥션 에러를 내게 될 것입니다.

이에 대한 해결책이 있습니다. Jakarta-Commons DBCP는 이렇게 버려진 커넥션을 추척하고 복구하도록 설정할 수 있습니다. 복구할 뿐만 아니라 이들 자원들을 열고서는 닫지 않았던 코드를 찾아 추적 결과를 만들어내기도 합니다.

버려진 커넥션이 제거되고 재생되도록 DBCP DataSource를 설정하기 위해 DBCP DataSource ResourceResourceParams에 아래 매개변수를 추가하면 됩니다. :

            
              removeAbandoned
              true
            
사용할 수 있는 커넥션이 부족해진다면 DBCP는 버려진 커넥션을 찾아 복구하고 재생합니다. 디폴트는 false로 되어 있습니다.

커넥션이 버려졌다고 간주되기 전에 사용되지 않은 시간(초)를 설정하기 위해 removeAbandonedTimeout를 설정하십시오.

            
              removeAbandonedTimeout
              60
            
버려진 커넥션을 제거하는데 기본적으로 정해진 타임아웃 시간은 300초입니다.

만일 커넥션 자원을 낭비한 코드 위치의 로그를 남기려고 한다면 logAbandoned 패러미터를 true로 할 수 있습니다.

            
              logAbandoned
              true
            
기본은 false로 되어 있습니다.

MySQL DBCP 사용예

0. 소개

MySQL과 mm.mysql JDBC 드라이버가 잘 연동된다고 보고된 버전은 다음과 같습니다.

  • MySQL 3.23.47, MySQL 3.23.47 using InnoDB, MySQL 4.0.1alpha
  • mm.mysql 2.0.14 (JDBC Driver)
새로 MySQL mm.mysql 3.0 driver를 테스트해봤다면 알려주시기 바랍니다.

1. MySQL 설정

달리하면 문제를 일으킬 수도 있으므로 이 지시를 반드시 따르십시오.

test 유저, 새 데이타베이스, 테스트 테이블 하나를 만드십시오. mySQL 유저는 할당된 패스워드가 있어야합니다. 비어있는 패스워드를 가지고서는 드라이버가 연결에 실패할 것입니다.

mysql> GRANT ALL PRIVILEGES ON *.* TO javauser@localhost 
    ->   IDENTIFIED BY 'javadude' WITH GRANT OPTION;
mysql> create database javatest;
mysql> use javatest;
mysql> create table testdata (
    ->   id int not null auto_increment primary key,
    ->   foo varchar(25), 
    ->   bar int);
주의: 위의 유저는 테스트가 끝나면 제거되어야 합니다.

다음으로 testdata 테이블에 테스트 데이타를 넣으십시오.

mysql> insert into testdata values(null, 'hello', 12345);
Query OK, 1 row affected (0.00 sec)

mysql> select * from testdata;
+----+-------+-------+
| ID | FOO   | BAR   |
+----+-------+-------+
|  1 | hello | 12345 |
+----+-------+-------+
1 row in set (0.00 sec)

mysql>

2. server.xml 설정

$CATALINA_HOME/conf/server.xml에 리소스 선언을 추가하여 Tomcat에서의 JNDI 데이타소스를 설정하십시오.

이것을 examples 컨텍스트의 와 localhost 정의를 닫는 태그인 사이에 추가하십시오.



  

  

  
    
      factory
      org.apache.commons.dbcp.BasicDataSourceFactory
    

    
    
      maxActive
      100
    

    
    
      maxIdle
      30
    

    
    
      maxWait
      10000
    

    
    
     username
     javauser
    
    
     password
     javadude
    

    
    
       driverClassName
       org.gjt.mm.mysql.Driver
    

    
    
      url
      jdbc:mysql://localhost:3306/javatest?autoReconnect=true
    
  

3. web.xml 설정

이제 테스트 애플리케이션에 대한 WEB-INF/web.xml을 만드십시오.


    

  MySQL Test App
  
      DB Connection
      jdbc/TestDB
      javax.sql.DataSource
      Container
  

4. 테스트 코드

이제 이후에 사용할 간단한 test.jsp 파일을 만드십시오.


  
    
  
  

  <%
    foo.DBTest tst = new foo.DBTest();
    tst.init();
  %>

  

Results

Foo <%= tst.getFoo() %>
Bar <%= tst.getBar() %>

새로 만들어진 데이타소스와 커넥션풀을 실제로 사용할 자바 클래스를 만드십시오. 주의: 이 코드는 실무에서 쓰일 만한 코드는 아닙니다. 단지 테스트 목적으로만 사용되는 것입니다. :-)

package foo;

import javax.naming.*;
import javax.sql.*;
import java.sql.*;

public class DBTest {

  String foo = "Not Connected";
  int bar = -1;
    
  public void init() {
    try{
      Context ctx = new InitialContext();
      if(ctx == null ) 
          throw new Exception("Boom - No Context");

      DataSource ds = 
            (DataSource)ctx.lookup(
               "java:comp/env/jdbc/TestDB");

      if (ds != null) {
        Connection conn = ds.getConnection();
              
        if(conn != null)  {
            foo = "Got Connection "+conn.toString();
            Statement stmt = conn.createStatement();
            ResultSet rst = 
                stmt.executeQuery(
                  "select id, foo, bar from testdata");
            if(rst.next()) {
               foo=rst.getString(2);
               bar=rst.getInt(3);
            }
            conn.close();
        }
      }
    }catch(Exception e) {
      e.printStackTrace();
    }
 }

 public String getFoo() { return foo; }
 public int getBar() { return bar;}
}

마지막으로 웹 애플리케이션을 $CATALINA_HOME/webappsDBTest.war라는 이름의 war 파일이나 DBTest라는 서브 디렉토리로 배치하십시오.

배치되고 나면 실제로 실행이 되는지 보기 위해 브라우저에서 http://localhost:8080/DBTest/test.jsp를 열어보십시오.

오라클 8i

0. 소개

글을 쓰는 이는 오라클 DBA가 아니기 때문에 이 섹션에 대한 어떤 비평도 고맙게 여길 것입니다. :-)

오라클은 일상적으로 아는 것 외에 mySQL 설정과 그다지 다르지 않습니다. 먼저 기본적으로 Tomcat은 $CATALINA_HOME/common/lib에 설치된 *.jar 파일들만을 읽어들이기 때문에 classes111.zipclasses12.zip 파일들은 .jar 확장자로 이름이 바뀔 필요가 있습니다. jar 파일은 zip 파일과 동일하기 때문에, unzip을 실행하고 다시 jar로 묶을 필요는 없으며 단지 이름만 바꾸면 됩니다. 또한 Tomcat 4.0의 몇몇 앞의 버전들은 JDK 1.4와 함께 사용되었을 때 classes12.zip 파일은 압축을 풀고 클래스 계층에서 javax.sql.*를 제거하고 다시 jar로 묶지 않으면 classes12.zip를 로드하지 못할 것입니다.

1. server.xml 설정

위에서 mysql 설정과 똑같은 방법으로 server.xml 파일에 데이타소스를 정의하면 됩니다. 한 예로 mysid라는 sid에 있는 myschema라는 스키마에 thin 드라이버를 사용하여 scott/tiger로 연결하는 myoracle이라는 데이타소스를 정의해보겠습니다. (주의: thin 드라이버를 사용할 때 sid는 tns 이름과 같은 것이 아닙니다.)

OCI 드라이버를 사용하려면 URL 문자열에 thin을 oci로 바꾸어 주어야 합니다.

 


  
    factory
    org.apache.commons.dbcp.BasicDataSourceFactory
  
  
    driverClassName
    oracle.jdbc.driver.OracleDriver
  
  
    url
    jdbc:oracle:thin:myschema@127.0.0.1:1521:mysid
  
  
    username
    scott
  
  
    password
    tiger
  
  
    maxActive
    20
  
  
    maxIdle
    10
  
  
    maxWait
    -1
  

2. web.xml 설정

애플리케이션의 web.xml 파일을 만들때 반드시 DTD에 정의된 순서대로 엘리먼트를 작성해야 합니다.


 Oracle Datasource example
 jdbc/myoracle
 javax.sql.DataSource
 Container

3. 코드 예제

(필요한 DB 인스턴스와 테이블 등을 만들었다고 가정하고) Datasource 코드를 조금 바꾸어서 위의 예제 애플리케이션을 사용할 수 있습니다.

Context initContext = new InitialContext();
Context envContext  = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/myoracle");
Connection conn = ds.getConnection();
//etc.
PostgreSQL

0. 소개

PostgreSQL는 오라클과 동일한 방법으로 설정이 됩니다. 차이만을 여기서 다시 얘기하겠습니다. 이 부분은 아직 테스트하지는 않았으며 피드백을 주시면 고맙겠습니다.

1. server.xml 설정

 


  
    factory
    org.apache.commons.dbcp.BasicDataSourceFactory
  
  
    driverClassName
    org.postgresql.Driver
  
  
    url
    jdbc:postgresql://127.0.0.1:5432/mydb
  
  
    username
    myuser
  
  
    password
    mypasswd
  
  
    maxActive
    20
  
  
    maxIdle
    10
  
  
    maxWait
    -1
  
 

2. web.xml 설정


 postgreSQL Datasource example
 jdbc/mydb
 javax.sql.DataSource
 Container

Tyrex 커넥션 풀 Tyrex Connection Pool
소개

Tomcat 4.1은 Tyrex 1.0을 사용하여 트랜잭션 관리와 리소스 설정 지원 기능을 제공합니다. 이는 사용자가 표준 javax.transaction.UserTransaction뿐 아니라 JNDI 네임스페이스로부터도 JTA/JCA 리소스를 얻을 수 있도록 합니다..

필요한 jar 설치

웹 애플리케이션이 Tyrex를 사용하도록 하기 위해서 webapp와 Tomcat은 Tyrex jar와 Tyrex jar가 필요로 하는 jar 파일들에 접근할 필요가 있습니다. 아래에 필요한 jar 파일들과 이를 구할 수 있는 곳의 리스트가 있습니다.

다음 jar 파일들은 http://tyrex.exolab.org에서 구할 수 있는 Tyrex 바이너리 배포판에 포함되어 있습니다.

  • tyrex-1.0.jar
  • ots-jts_1.0.jar
  • jta_1.0.1.jar
  • xerces-J_1.4.0.jar
다음 두 jar 파일도 역시 필요합니다.

이들 여섯 개의 jar 파일들은 Tomcat과 웹 애플리케이션에서 읽을 수 있도록 $TOMCAT_HOME/common/lib에 넣어야 합니다.

Tyrex 설정

Tyrex 문서(http://tyrex.exolab.org)는 Tyrex를 설정하는 방법에 대해 아주 상세하게 설명을 하고 있습니다. 한 예로서 Tyrex 도메인 설정 XML 파일에 쓰여진 아래의 Tyrex 설정을 사용해 볼 것입니다.


  myDomain
  
    
      myDatasource
      /home/david/mm.mysql-2.0.14-bin.jar
      org.gjt.mm.mysql.jdbc2.optional.MysqlXaDataSource
      
        david
        secret
        localhost
        3306
        daviddb
      
    
  

몇 가지 유의사항이 있습니다.

  • jar 파일의 절대경로를 써야 합니다.(상대경로로는 Tyrex는 현 디렉토리만을 찾는데 이는 우리가 원하는 것은 아닐 것입니다.) URL을 써넣어도 됩니다.
  • config 엘리먼트 안에 포함된 모든 엘리먼트들은 표준 설정자 메소드(setter)를 사용하여 datasource 클래스에 매개변수로 전달됩니다.
  • http://tyrex.exolab.org/configuration.html에서 Tyrex를 설치하는 더 나은 설명뿐 아니라 더 많은 설정 옵션을 볼 수 있습니다.

이 XML 설정 파일은 Tomcat의 클래스로더가 getResource() 메소드를 사용하여 찾을 수 있는 위치에 있으면 됩니다. 이는 웹애플리케이션 아래의 WEB-INF/classes 디렉토리도 ?I찮다는 얘기입니다.

톰캣 설정

Tyrex XML 설정파일을 만들었다면 JNDI 네임스페이스에 Tyrex 리소스를 등록하여야 합니다. 이것은 Tomcat의 server.xml 파일에서 하면 됩니다. 두가지 중요한 패러미터가 쓰여져야 하는데 도메인 설정 파일 이름(tyrexDomainConfig)과 사용될 Tyrex 도메인 이름(tyrexDomainName)입니다. 이는 다음과 같이 환경 매개변수로서 세팅하면 됩니다.



(웹 애플리케이션의 엘리먼트 아래에) 이제 리스소를 설정해야 합니다.



  
    name
    myDataSource
  

몇 가지 지적할 것이 있습니다.

  • 리스소의 type은 Tyrex를 어떻게 설정하였는지와 상관없이 tyrex.resource.Resource이어야 합니다.
  • 오직 하나의 ResourceParam 패러미터 name(Tyrex 설정 파일에 쓰여진 리소스 이름)만이 필요합니다.
  • Tomcat/JNDI 리소스와 Tyrex 리소스 간의 차이를 주목하십시오.(처음 보면 혼동이 될 것입니다.)

애플리케이션 작성

Tyrex 리소스를 사용하는 것은 이제 상대적으로 간단할 것입니다. 데이타소스를 얻기 위해서 단지 JNDI를 사용하기만 하십시오.

InitialContext initCtx = new InitialContext();
DataSource ds = (DataSource) initCtx.lookup("java:comp/env/my-datasource");
Connection conn = ds.getConnection();
...and so on.

Tyrex는 표준 위치에서의 JNDI(java:comp/UserTransaction)에서 얻을 수 있는 javax.transaction.UserTransaction도 제공하고 있습니다.

DBCP를 사용하지 않는 방법들 Non DBCP Solutions

이런 방법들은 데이타베이스에 매번 커넥션을 연결하여 사용하는 것이거나(테스트 외에는 권장하지 않습니다.) 다른 풀링 기술을 사용하는 것입니다.

OCI 클라이언트로 Oracle8i 연결 Oracle 8i with OCI client
소개

OCI 클라이언트를 사용하여 JNDI 데이타소스를 생성하는 것을 자세히 설명하지는 않으며 위의 Oracle and DBCP solution과 결합될 수 있는 부분입니다.

OCI 드라이버를 사용하기 위하여 먼저 오라클 클라이언트가 설치되어야 합니다. 씨디에서 Oracle8i(8.1.7)를 설치하고 otn.oracle.com에서 적당한 JDBC/OCI 드라이버(Oracle8i 8.1.7.1 JDBC/OCI Driver)를 다운로드하십시오..

Tomcat에서 사용하기 위해서 classes12.zipclasses12.jar로 이름을 바꾼 다음에, $CATALINA_HOME/common/lib에 복사를 하십시오. 사용하고 있는 Tomcat과 JDK 버전에 따라서는 javax.sql.* 클래스를 빼야 할 수도 있습니다.

결합하기 Putting it all together

$PATHLD_LIBRARY_PATHocijdbc8.dll.so이 있는지($ORAHOME\bin에 있는 파일) 확인하고 System.loadLibrary("ocijdbc8");를 사용하는 간단한 테스트 프로그램을 이용하여 네이티브 라이브러리가 로드되는지도 확인하십시오.

이제 다음 라인들을 포함한 간단한 테스트 서블릿이나 jsp를 작성하십시오.

DriverManager.registerDriver(new
oracle.jdbc.driver.OracleDriver());
conn =
DriverManager.getConnection("jdbc:oracle:oci8:@database","username","password");

먼저 UnsatisfiedLinkError가 나오면 다음과 같은 문제가 있다는 것을 말합니다.

  • JDBC 클래스 파일과 오라클 클라이언트 버전이 서로 맞지 않는 것입니다. 이는 필요한 라이브러리 파일을 찾을 수 없다는 얘기입니다. 예를 들어 8.1.5 오라클 클라이언트 버전을 가지고서 8.1.6 오라클 버전에 있는 classes12.zip을 사용하고 있을 수도 있는 것입니다. classesXXXs.zip 파일과 오라클 클라이언트 프로그램은 일치해야 합니다.
  • $PATHLD_LIBRARY_PATH 문제
  • otn에서 다운로드한 드라이버는 인식하지 않고 $ORAHOME\jdbc\lib에 있는 classes12.zip 파일이 잘 작동한다고 알려지고 있습니다.

다음으로 ORA-06401 NETCMN: invalid driver designator 에러를 경험할 수도 있습니다.

오라클 문서에서는 "로그인(연결) 문자열이 유효하지 않은 드라이버를 지정해서 생기는 에러이며 문자열을 고쳐서 다시 연결을 시도하라"고 되어 있습니다. 데이타베이스 연결 문자열(host:port:SID 형태)을 다음과 같이 바꾸십시오. (description=(address=(host=myhost)(protocol=tcp)(port=1521))(connect_data=(sid=orcl)))

TNS이름을 정리하는 것이 정말로 필요한 것 같지는 않습니다. 물론 저는 오라클 DBA가 아니기 때문에 확실한 것은 아닙니다.(역자주: 믿지는 마십시오.)

일반적으로 일어날 수 있는 문제 Common Problems

데이타베이스를 사용하는 웹 애플리케이션에서 겪게되는 일반적인 문제와 이를 해결하는 방법에 대한 팁들을 이제 얘기하도록 하겠습니다.

가끔 데이타베이스 연결이 되지 않는 문제 Intermittent dB Connection Failures

Tomcat은 JVM내에서 구동합니다. JVM은 주기적으로 더 이상 사용하지 않는 자바 객체를 제거하기 위해 가비지 컬렉션(GC)을 실행합니다. JVM이 Tomcat내 코드의 GC를 실행하면 Tomcat은 동작을 멈춥니다. 데이타베이스 연결 최대시간이 가비지 컬렉션 시간보다 작다면 데이타베이스 연결 에러를 보게 될 것입니다.

카비지 콜렉션이 얼마나 오래 걸리는지에 대한 데이타를 모으기 위해서는 Tomcat이 시작될 때 CATALINA_OPTS-verbose:gc 변수를 추가하십시오. verbose gc 옵션이면 $CATALINA_BASE/logs/catalina.out 로그 파일이 가비지 컬렉션이 얼마나 걸리는지를 포함한 모든 데이타를 포함할 것입니다.

JVM이 99%로 잘 조정되었을 때 GC는 일초 이하가 걸릴 것입니다. 그 외에도 몇 초밖에 걸리지 않습니다. 아주 드물게 GC에 10초 이상 걸릴 수도 있습니다.

데이타베이스 연결 초과시간을 반드시 10초에서 15초 사이로 정하십시오. DBCP에서는 maxWait 매개변수를 사용하여 설정하십시오.

임의 연결 닫힘 오류 Random Connection Closed Exceptions

이것은 한 요청이 커넥션풀에서 디비 연결을 하나 가져와서 그것을 두번 닫으려고 할때 일어날 수 있습니다. 커넥션풀을 사용할 때는 연결을 닫으면 다른 요청에서 재사용하기 위해 풀로 돌아가는 것이지 커넥션을 닫는 것이 아닙니다. 그리고 Tomcat은 동시적인 요청들을 처리할 다중 스레드를 사용합니다. Tomcat에서 이런 에러가 일어날 수 있는 일련의 이벤트의 예가 있습니다.

  쓰레드 1에서 실행되는 요청 1이 디비 연결을 얻습니다.

  요청 1이 디비 연결을 닫습니다.

  JVM이 쓰레드2로 실행 스레드를 전환합니다.

  쓰레드 2에서 실행되는 요청 2가 디비 연결(요청 1이 막 반납한 같은 연결)을 얻습니다.

  JVM은 실행 쓰레드를 스레드 1로 다시 전환합니다.

  요청 1은 마지막에 디비 연결을 두 번 닫습니다.

  JVM은 실행 쓰레드를 또 다시 쓰레드 2로 전환합니다.

  쓰레드 2의 요청 2는 디비 연결을 사용하고자 하지만 요청 1이 이미 닫았기 때문에 실패하게 됩니다.
이제 커넥션 풀에서 얻은 디비 연결을 사용하는 적절한 코드 예를 보이겠습니다.
  Connection conn = null;
  Statement stmt = null;  // Or PreparedStatement if needed
  ResultSet rs = null;
  try {
    conn = ... get connection from connection pool ...
    stmt = conn.createStatement("select ...");
    rs = stmt.executeQuery();
    ... iterate through the result set ...
    rs.close();
    rs = null;
    stmt.close();
    stmt = null;
    conn.close(); // Return to connection pool
    conn = null;  // Make sure we don't close it twice
  } catch (SQLException e) {
    ... deal with errors ...
  } finally {
    // Always make sure result sets and statements are closed,
    // and the connection is returned to the pool
    if (rs != null) {
      try { rs.close(); } catch (SQLException e) { ; }
      rs = null;
    }
    if (stmt != null) {
      try { stmt.close(); } catch (SQLException e) { ; }
      stmt = null;
    }
    if (conn != null) {
      try { conn.close(); } catch (SQLException e) { ; }
      conn = null;
    }
  }