JNDI와 커넥션풀, 커넥션풀 직접만들기

JNDI와 커넥션풀, 커넥션풀 직접만들기

JNDI: JNDI는 자바 네이밍 디렉토리 인터페이스(Java Naming and Directory Interface)의 약자이다. 한 서비스가 다른 서비스를 탐색할 때 유용하게 사용한다.

저장되는 공간이 폴더 형태로 되어있고, 각 객체가 폴더 내에 저장된다. “저장된 개체의 주소값을 넘겨달라”가 룩업이다.

쉽게 생각하면 탐색기라고 할 수 있다. Context 새 개체를 만들고, comp/env에 접근한 뒤, 그 안에서 지정한 이름(jdbc/oracle)의 데이터 소스를 얻어온다. 그리고 나서 겟 커넥션을 하면 된다.

그 전에는 연결->사용->해제 였으나, 이제는 연결->사용-> “반환”으로 바뀐다.(커넥션풀)


​커넥션풀 직접 만들기

여기서부터는 커넥션풀의 원리를 이해하기 위해 직접 만들어본다. 새로 만든 소스에 대한 설명은 노란색 표 안에 넣어두었다. 실제 커넥션풀 소스는 파란색 표 안에 넣어두었다.

(1) server.xml

서버에 대한 정보를 가지고 있는 xml파일. 오라클 드라이버 주소부터 커넥션풀 갯수까지 각종 서버정보를 기입해놓는다.

<?xml version=”1.0″ encoding=”UTF-8″?>
<context>
        <Resource
          name=”jdbc/oracle”
          type=”com.sist.DataSource”
          driverClassName=”oracle.jdbc.driver.OracleDriver”
          url=”jdbc:oracle:thin:@localhost:1521:ORCL”
          password=”tiger”
          username=”scott”
          maxActive=”10″
          maxIdle=”5″
          maxWait=”-1″
        />
</context>

 

 

설명>

server는 xml파일로 저장된다. 여기서 Resource는 테이블에 해당된다. name, type, driverClassName, url, password, username, maxActive, maxIdle, maxWait은 컬럼에 해당된다.

name은 “jdbc/oracle”

type 은 DataSource로 한다. 기존의 커넥션풀을 쓰려면 type=”javax.sql.DataSource” 을 써야 한다.

driverClassName 은 오라클 드라이버 이름을 넣는다.

url 은 오라클 드라이버 주소를 넣는다.

password는 오라클 패스워드

username은 오라클 아이디

maxActive는 커넥션 풀 최대갯수

maxIdle 커넥션 풀 유지갯수

maxWait -1이면 무한정 대기(1000이면 1초 대기)

 

 

원래 코드>

<Context docBase=”프로젝트이름” path=”/프로젝트이름” reloadable=”true” source=”org.eclipse.jst.jee.server:프로젝트이름”>
        <Resource
          name=”jdbc/oracle”
          type=”javax.sql.DataSource”
          auth=”Container”
          driverClassName=”oracle.jdbc.driver.OracleDriver”
          url=”jdbc:oracle:thin:@localhost:1521:ORCL”
          password=”tiger”
          username=”scott”
          maxActive=”10″
          maxIdle=”5″
          maxWait=”-1″
        />
</Context>

 

 

(2) DataSource.java

서버.xml의 정보를 실제로 저장해두는 영역. 일종의 DTO라고 생각하면 쉽다.

import java.sql.Connection;
import java.sql.DriverManager;

public class DataSource {
   private String driverClassName;
   private String url;
   private String username;
   private String password;
   private int maxActive;
   private int maxIdle;
   private int maxWait;

 

   //이 아래로는 게터세터
   public String getDriverClassName() {
      return driverClassName;
   }
   public void setDriverClassName(String driverClassName) {
      this.driverClassName = driverClassName;
   }
   public String getUrl() {
      return url;
   }
   public void setUrl(String url) {
      this.url = url;
   }
   public String getUsername() {
      return username;
   }
   public void setUsername(String username) {
      this.username = username;
   }
   public String getPassword() {
      return password;
   }
   public void setPassword(String password) {
      this.password = password;
   }
   public int getMaxActive() {
      return maxActive;
   }
   public void setMaxActive(int maxActive) {
      this.maxActive = maxActive;
   }
   public int getMaxIdle() {
      return maxIdle;
   }
   public void setMaxIdle(int maxIdle) {
      this.maxIdle = maxIdle;
   }
   public int getMaxWait() {
      return maxWait;
   }
   public void setMaxWait(int maxWait) {
      this.maxWait = maxWait;
   }
}

 

설명>

DataSource에는 커넥션에 필요한 기본적인 변수들을 넣어놓는다. 일단 필수적인 변수들로

   private String driverClassName;
   private String url;
   private String username;
   private String password;
   private int maxActive;
   private int maxIdle;
   private int maxWait;

을 선언한다.

그리고 게터/세터를 적용한다.

 

(3) MyHandler.java

마이 핸들러는 xml을 한 줄씩 읽어와 해시 맵에 저장하는 역할을 한다. 다시 말해, xml에서 get한 엘리먼트들을 데이터 소스에 넣어 맵으로 만드는 기능을 가졌다.

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.util.*;

public class MyHandler extends DefaultHandler {
   Map map=new HashMap();
   @Override
   public void startElement(String uri, String localName, String qName,
         Attributes attributes) throws SAXException {
      try {
         if(qName.equals(“Resource”))
         {
            String name=attributes.getValue(“name”);
            String type=attributes.getValue(“type”);
            String driver=attributes.getValue(“driverClassName”);
            String url=attributes.getValue(“url”);
            String pwd=attributes.getValue(“password”);
            String user=attributes.getValue(“username”);
            String ma=attributes.getValue(“maxActive”);
            String mi=attributes.getValue(“maxIdle”);
            String mw=attributes.getValue(“maxWait”);
           
            Class clsName=Class.forName(type);
            DataSource ds=(DataSource)clsName.newInstance();
            ds.setDriverClassName(driver);
            ds.setUrl(url);
            ds.setUsername(user);
            ds.setPassword(pwd);
            ds.setMaxActive(Integer.parseInt(ma));
            ds.setMaxWait(Integer.parseInt(mw));
            ds.setMaxIdle(Integer.parseInt(mi));
            map.put(name, ds);
         }
      } catch (Exception e) {
         System.out.println(e.getMessage());
      }
   }
}

설명>

(1)MyHandler는 DefaultHandler를 상속받는다. 그 후 해시 맵을 우선 선언한다.

public class MyHandler extends DefaultHandler {
   Map map=new HashMap();

 

(2) startElement를 오버라이딩한다. startElement는 xml에서 한 줄을 읽어오는 역할이다.

startElement는 한 줄이 시작하는 시점이다.

매개변수는 String uri, String localName, String qName, Attributes attributes가 있다.

uri는 파일경로명.

qName은 태그이름. 테이블에 해당하는 <>안의 단어이다.

Attributes는 속성명. 컬럼에 해당하는 <>안의 단어이다.
   @Override
   public void startElement(String uri, String localName, String qName,
         Attributes attributes) throws SAXException {
      try {

         //한 줄이 시작하는 시점에 큐네임(태그이름)이 Resource라면
         if(qName.equals(“Resource”))
         {

            //변수에 어트리뷰트(속성명)기억해둔다.
            String name=attributes.getValue(“name”);
            String type=attributes.getValue(“type”);
            String driver=attributes.getValue(“driverClassName”);
            String url=attributes.getValue(“url”);
            String pwd=attributes.getValue(“password”);
            String user=attributes.getValue(“username”);
            String ma=attributes.getValue(“maxActive”);
            String mi=attributes.getValue(“maxIdle”);
            String mw=attributes.getValue(“maxWait”);
           

            //데이터 소스를 선언하고, 데이터소스에 각 변수를 대입한다.

            //cf)데이터 소스를 선언할 때 type을 메모리 할당한 후 데이터소스로 변환해야 한다.**
            Class clsName=Class.forName(type); //**
            DataSource ds=(DataSource)clsName.newInstance(); //**
            ds.setDriverClassName(driver);
            ds.setUrl(url);
            ds.setUsername(user);
            ds.setPassword(pwd);
            ds.setMaxActive(Integer.parseInt(ma));
            ds.setMaxWait(Integer.parseInt(mw));
            ds.setMaxIdle(Integer.parseInt(mi));
            map.put(name, ds); //맵에 ‘이름’을 ‘키’로, ‘데이터소스’를 ‘키 값’으로 넣는다. 해시 맵은 ‘키’로 ‘키 값’을 불러올 수 있다.
         }
      } catch (Exception e) {
         System.out.println(e.getMessage());
      }
   }
}

 

 

(4) Context.java

컨텍스트 클래스는 마이핸들러로 맵을 선언한다. (앞서, 마이핸들러는 get한 엘리먼트들을 데이터소스에 넣어 맵으로 만드는 기능이었다) 컨텍스트 클래스의 lookup 메소드는 키 이름(ex: “jdbc/oracle”)를 넘기면 맵(Object)를 반환해준다.

import java.util.*;
import java.io.*;

import javax.xml.parsers.SAXParserFactory;

import com.sun.org.apache.xerces.internal.parsers.SAXParser;

public class Context {
   Map map=new HashMap();
   public Context(){
      try{
         SAXParserFactory spf=SAXParserFactory.newInstance();
         javax.xml.parsers.SAXParser sp=spf.newSAXParser();//SAXParser sp=spf.newSAXParser();
         MyHandler mh=new MyHandler();
         String path=”C:\\webDev2\\workspace\\JNDIProject\\src\\com\\sist\\server.xml”;
         sp.parse(new File(path),mh);
         map=mh.map;
      }catch(Exception ex){}
     
   }
   
public Object lookup(String name){
      return map.get(name);
   }
}

 

설명>

Context 클래스:
(1) 해시 맵을 선언한다.  Map map=new HashMap();

(2) 삭스 파서 팩토리(spf)를 선언 SAXParserFactory spf=SAXParserFactory.newInstance();
(3) 삭스 파서 팩토리로부터 삭스파서(sp)를 선언 javax.xml.parsers.SAXParser sp=spf.newSAXParser();//SAXParser sp=spf.newSAXParser();
(4) 마이 핸들러(mh) 메모리할당 MyHandler mh=new MyHandler();
(5) 삭스파서(sp)로 파스(parse)한다. 파스할 때 서버패스와 마이 핸들러가 필요하다. sp.parse(new File(path),mh);

     cf) String path=”C:\\webDev2\\workspace\\JNDIProject\\src\\com\\sist\\server.xml”;
(6) 마이 핸들러로 맵을 선언한다. map=mh.map;

설명>

lookup 메소드:

public object: 객체(Object)를 반환한다. 여기서 객체는 해쉬 맵이다.

return map.get(name): 해쉬맵의 키 값에 따른 키를 가져온다. 예를 들면 “jdbc/oracle” 이라는 이름(키)의 맵을 반환한다.

 
 

(5) MainClass

커메인 클래스는 직접 만드는 커넥션 풀에만 존재하고, 실제 커넥션 풀에는 존재하지 않는다.

실제 커넥션 풀에는 getConnection()이라는 이름의 메소드를 만들면 된다.

import java.sql.*;

public class MainClass {

 public static void main(String[] args) {
  try{
          Context c=new Context();
          DataSource ds=(DataSource)c.lookup(“jdbc/oracle”);
          Connection conn=ds.getConnection();
         
          System.out.println(ds.getUrl());
          System.out.println(ds.getDriverClassName());
          System.out.println(ds.getPassword());
          System.out.println(ds.getUsername());
  }
  catch(Exception ex){System.out.println(ex.getMessage());}
 }

}

설명>

(1) Context를 새로 선언한다.

(2) ‘데이터 소스’에 해시맵을 넣는다. ‘컨텍스트.룩업‘은 name에 대한 해시 맵을 반환해주는 메소드다.

(3) ‘데이터 소스.겟커넥션‘으로 연결한다.

 

 원래 소스> 

public void getConnection(){
    try{
     Context init=new InitialContext();//탐색기를 여는 것과 같음
     Context c=(Context)init.lookup(“java://comp/env”);//C드라이브에 접근하는것과 같음
     DataSource ds=(DataSource)c.lookup(“jdbc/oracle”);//그 안에서 파일 하나를 얻어온다.
     conn=ds.getConnection();
    }catch(Exception ex){System.out.println(ex.getMessage());}
 }

.