'All Contents'에 해당되는 글 59건

  1. 2008.03.10 Tomcat에서 DBCP 설정 1
  2. 2008.03.10 iBatis에서 Procedure 사용시 Commit 되지 않는 문제 3
  3. 2008.03.10 Anti Pattern 이란 무엇?
Dev Note/Tomcat2008. 3. 10. 19:23
Java로 Web Service를 하기 위해 DB Connection Pool을 사용하지 않는 것은 이제 상상도 할 수가 없다. Java로 된 DB Connection Pool API는 수도 없이 많다. 대부분의 상용 WAS에서는 자기네 벤더에서 제공하는 DB Connection Pool이 있고, 그 외에도 무료로 제공되는 많은 DB Connection Pool 들이 있다. DBCP도 그중 하나이다.

DBCP(DataBase Connection Polling Service)는 Apache의 많은 Project 중 하나로 DataBase의 연결을 Pool에 생성해 놓고 재활용 및 관리를 가능하게 해주는 API이다. 여기서는 DBCP를 Tomcat에서 사용하기 위한 설정에 대해서 알아본다.

-- DBCP Install
Jakarta Commons DBCP Download : http://commons.apache.org/dbcp/downloads.html
Jakarta Commons Pool Download : http://commons.apache.org/pool/downloads.html
   è Generic object pooling component
Jakarta Commons Collections Download : http://commons.apache.org/downloads/download_collections.cgi
   è Extends or augments the Java Collections Framework
Java JDBC Driver : 사용하는 DataBase의 벤더가 제공하는 Driver를 사용한다.
Jakarta Commons DBCP API Document : http://commons.apache.org/dbcp/apidocs/index.html

위의 파일들을 다운 받은 jar 파일을 $CATALINA_HOME/common/lib 폴더에 복사한다. /WEB-INF/lib에 넣어두어도 무방하나, DB Connection Pool은 거의 모든 Context에서 사용하므로 모든 Context path의 /WEB-INF/lib 밑에 넣어두는 것보다는 $CATALINA_HOME/common/lib 밑에 넣어두는 것이 좋다.

-- DBCP Configuration Parameters
* Mandatory Parameters *
username : JDBC Driver로 연결할 DataBase의 사용자 계정 이름
password : JDBC Driver로 연결할 DataBase의 사용자 계정 비밀번호
url : JDBC Driver로 연결할 DataBase의 URL
driverClassName : DataBase 연결에 사용할 JDBC Driver Class 이름 (full path)

* Option Parameters - () 안의 값은 Default *
connectionProperties : DataBase 연결 시 사용할 Properties (charset 등 ...)

defaultAutoCommit (true) : DB Connection의 기본 auto-commit 상태 값
defaultReadOnly (driver default) : Pool에 DB Connection이 생성 될 때의 기본 read-only 상태 값
defaultTransactionIsolation (driver default) : Pool에 DB Connection이 생성 될 때의 기본 TransactionIsolation 상태 값. (NONE, READ_COMMITTED, READ_UNCOMMITED, REPEATABLE_READ, SERIALIZABLE 중 하나)
defaultCatalog (driver default) : Pool에 DB Connection이 생성 될 때의 기본 catalog 값

initialSize (0) : pool이 최초 동작할 때 생성 할 DB Connection
maxActive (8) : pool 내에서 동시에 최대로 생성할 수 있는 DB Connection (음수는 no limit)
maxIdle (8) : pool 내에서 대기 상태로 존재할 수 있는 최대 DB Connection (음수는 no limit)
minIdle (0) : pool 내에서 대기 상태로 존재할 수 있는 최소 DB Connection
maxWait (indefinitely) : pool에 DB Connection이 반환 될 최대 milliseconds (음수는 indefinitely)

validationQuery : DB Connection이 caller에게서 주거나 반환되기 전 해당 Connection이 사용가능한지 확인하는 쿼리
testOnBorrow (true) : DB Connection의 사용가능 여부 확인을 Caller가 pool에서 가져가지 전에 확인하는 것으로 지정한다. 만약 확인결과 사용불가능이면 pool에서 제거하고 다른 Connection을 준다.
NOTE - 이 값이 true이면 validationQuery 값은 non-null string 이어야 한다.
testOnReturn (false) : DB Connection의 사용가능 여부 확인 Caller가 pool에 반환하기 전에 확인하는 것으로 지정한다. 만약 확인결과 사용불가능이면 pool에서 제거하고 다른 Connection을 준다.
NOTE - 이 값이 true이면 validationQuery 값은 non-null string 이어야 한다.
testWhileIdle (false) : 유효하지 않은 DB Connection은 pool에서 제거한다.
NOTE - 이 값이 true이면 validationQuery 값은 non-null string 이어야 한다.
timeBetweenEvictionRunsMillis (-1) :  사용되지 않는 DB Connection을 추출하는 Thread의 실행주기 (단위 : milliseconds)
numTestsPerEvictionRun (3) : 사용되지 않는 Connection을 몇개 검사할지 지정
minEvictableIdleTimeMillis (1000 * 60 * 30) : 지정 시간만큼 대기상태인 DB Connection만 추출한다. (단위 : milliseconds)
poolPreparedStatements (false) : PreparedStatements가 풀링되어야 하는지 아닌지 설정
maxOpenPreparedStatements (unlimited) : open 된 최대 PreparedStatements 수

removeAbandoned (false) : 대기 상태로 removeAbandonedTimeout 이상동안 유지되면 Connection을 버릴 것인지 여부 설정
removeAbandonedTimeoout : removeAbandoned 하기 위한 Timeout (단위 : second)
logAbandoned : removeAbandoned에 의해 Connection을 버릴 때 로그로 남길지 여부

-- 설정 예
Context에 설정하기
   <Context path="dbcp" docBase="/home/app/www-root/dbcp/webapp" debug="5"
         reloadable="false" crossContext="true">
      <Resource name="jdbc/dbcpDB" auth="Container" type="javax.sql.DataSource"
            factory="org.apache.commons.dbcp.BasicDataSourceFactory"
            driverClassName="해당 JDBC Driver Class (oracle.jdbc.driver.OracleDriver)"
            url="jdbc:oracle:thin:@xxx.xxx.xxx.xxx:[port]:[sid]"
            username="DataBase User Name (scott)" password="DataBase User Password (tiger)"
            initialSize="1" maxActive="10" maxIdle="5" maxWait="15000"
            removeAbandoned="true" removeAbandonedTimeout="120" logAbandoned="false"
      />
   />

web.xml 추가 설정
   <resource-ref>
      <description>DB Connection</description>
      <res-ref-name>jdbc/dbcpDB</res-ref-name> <!-- Context에 설정한 DBCP의 이름 -->
      <res-tyep>javax.sql.DataSource</res-type>
      <res-auth>Container</res-auth>
   </resource-ref>

이상이 DBCP 설정 방법 중 Context에 설정하는 방법에 대해 알아보았다. DBCP를 설정하는 방법은 여기서 소개한것 외에도 많다. 다음에는 DBCP를 web.xml에 Servlet 처럼 등록하여 설정정보를 암호화하여 로딩하는 방법과 예제 소스를 다뤄볼 것이다.
Posted by as.wind.914
Framework/iBatis2008. 3. 10. 16:35
얼마전 프로젝트를 진행 중 iBatis에서 Procedure를 사용하고 Transaction을 Commit을 했으나, Commit이 되지 않고 모두 Rollback이 되는 현상이 발생을 했다.
아직 이 문제가 발생하는 근본 원인은 파악을 하지 못했고, 해결 방안만 찾아 사용했다. 해결 방안을 간단하게 정리한다. 소개는 실제 사용한 소스가 아니니 참고하세요.

-- 환경 설정
iBatis : iBatis 2.3.0
DB : Oracle 10g
Framework : Webwork 2.2.6, Spring 2.0.7

-- Commit 되지 않는 문제 발생
iBatis를 통해 실행 할 Procedure는 아래와 같이 TB_EMP 테이블에 NO와 NAME을 받아서 INSERT하는 Procedure이다. 그리고, Procedure 내에서 Transaction을 관리하지 않고, iBatis에서 Transaction을 관리하게 구성되어 있다.

Oracle Procedure :
   CREATE OR REPLACE PROCEDURE SP_TEST
      (p_no IN TB_EMP.NO%TYPE, p_name IN TB_EMP.NAME%TYPE)
   IS
   BEGIN
      INSERT INTO TB_EMP (NO, NAME) VALUES (p_no, p_name);
   END;

iBatis SqlMap :
   <parameterMap id="empMap" parameterClass="java.util.Map">
      <parameter property="no" mode="IN" jdbcType="DECIMAL" javaType="int" />
      <parameter property="name" mode="IN" jdbcType="VARCHAR" javaType="java.lang.String" />
   </parameterMap>
   <procedure id="insertEmpInfo" parameterMap="empMap" >
      { CALL SP_TEST(?, ?) }
   </procedure>

Execute Java Source :
   private SqlMapClient sqlMapClient = null;

   public void insertEmpInfo(int no, String name) throws SQLException {
      java.util.Map<String, Object> conditionMap = new java.util.HashMap<String, Object>();
      conditionMap.put("no", no);
      conditionMap.put("name", name);

      try {
         // start transaction
         sqlMapClient .startTransaction();
         
         sqlMapClient.queryForObject("insertEmpInfo", conditionMap);

         // commit transaction
         sqlMapClient.commitTransaction();
      } finally {
         // rollback and end transaction
         sqlMapClient.endTransaction();
      }
   }

위와 같은 코드에서는 Exception이 발생하지 않고 commitTransaction()이 실행 되었음에도 실제 DataBase에는 commit이 되어 있지 않고 rollback이 되어 있다.
그러나 같은 Transaction에서 Procedure와 함께 INSERT, UPDATE, DELETE Query가 실행이 되면 commit은 정상적으로 된다. 함께 실행되는 Query가 Procedure 전, 후 어디에서 실행이 되어도 상관은 없다. 왜 commit 되지 않는 것인가 원인은 아직 찾지를 못했고, 해결 방안만을 찾았다.

-- 해결 방안
   public void insertEmpInfo(int no, String name) throws SQLException {
      java.util.Map<String, Object> conditionMap = new java.util.HashMap<String, Object>();
      conditionMap.put("no", no);
      conditionMap.put("name", name);

      try {
         // start transaction
         sqlMapClient .startTransaction();
         
         sqlMapClient.queryForObject("insertEmpInfo", conditionMap);

         // commit transaction
         sqlMapClient.commitTransaction(); è sqlMapClient.getCurrentConnection().commit();
      } finally {
         // rollback and end transaction
         sqlMapClient.endTransaction();
      }
   }

위와 같이 sqlMapClient의 Session에 저장 된 Connection을 commit() 하는 것이 아니라 sqlMapClient의 현재 User Connection을 가져와 강제로 commit()을 해주면 INSERT, UPDATE, DELETE Query를 같이 사용하지 않는 경우에도 commit이 정상 처리 된다.

이 문제는 일부 JDBC Driver에서만 발생하는 문제인지, iBatis의 문제인지는 아직 확인을 하지 못 했다.
Posted by as.wind.914
Dev Note/Design Pattern2008. 3. 10. 00:33

Design Pattern이 있고, 그 이면에는 Anti Pattern이 있다.

-- Anti Pattern 이란?
Anti Pattern을 간단히 얘기하면 패턴과 반대로 프로그래밍을 하는 과정에서 프로그래머들이 흔히 범하기 쉬운 바람직하지 않은 방법들과 그로 인한 폐해를 의미한다.

Design Pattern의 출현은 프로그래머들에게 지금까지 있었던 가장 효과적인 가이드 라인을 제공해 주었다. 이들 패턴들은 개발 중에 나타나는 복잡한 구조와 문제들을 정리해 주었고, 소프트웨어 개발을 편리하게 도와주는 방안도 다양하게 개발되어 왔다. 그러나, 이들을 도입하여도 프로젝트는의 상황은 반드시 좋아지는 것은 아니다. 많은 단체 또는 개인이 창의적이고 혁신적인 방법을 개발하여 제공하여도 소프트웨어 개발의 성공 가능성은 낮은 것이 현실이다.

수많은 소프트웨어 개발 프로젝트가 진행이 된다. 그러나 그 수많은 프로젝트 중 거의 대부분이 취소되거나, 최초 예상했던 예산과 기간보다 더 많은 비용이 투입된다. 많은 프로젝트의 실패들과 옳바르지 못한 개발 산출물들은 성공한 프로젝트의 것과 마찬가지로 아주 중요한 가치를 지니는데, 왜냐하면 이것들이 모든 프로그래머들에게 가지지 말아야 할 방향들을 알려주고 있기 때문이다. 이들이 바로 Anti Pattern 이다.

-- Anti Pattern이 발생하는 측면

  1. 관리상의 Anti Pattern : 프로젝트의 프로세스와 프로젝트 구성원의 잘못된 관리
  2. 아키텍쳐 상의 Anti Pattern : 설계와 구조상의 잘못 된 정의
  3. Object 설계 Anti Pattern : Object를 설계상의 잘못 된 정의
  4. 방법론 상의 Anti Pattern : 개발 방법론 상의 절못 된 정의 또는 잘못 된 진행
  5. 설정관리 상의 Anti Pattern : Version 또는 Configuration 관리 상의 잘못 된 정의
  6. 개발상의 Anti Pattern : 코드 상의 잘못 된 사례

-- 잘 알려진 Anti Pattern

  1. 스파게티 코드 (Spaghetti Code - 개발) : 개발과정에서 다양한 기능들을 추가 또는 유지보수에 의해 코드가 복잡하게 꼬여 버린다.
  2. 스토브 파이프 시스템 (Stovepipe System - 아키텍쳐) : 다양한 솔루션들을 확실한 추상화의 개념 없이 임의로 묶어 하나의 제품을 만들면 신뢰성이 떨어지며 유지보수가 힘든 제품이 탄생된다.
  3. 분석 마비 (Analysis Paralysis - 관리) : 분석 단계에서 완벽하고 완전한 분석을 꿈꾸면 이는 소프트웨어 개발 진행을 마비 시켜 버린다.
  4. 신의 객체 (God Object - Object 설계) : 하나의 객체에 너무 많은 기능과 인터페이스를 담아버리면 복잡하고 유지보수하기 힘들고 상호 의존성이 높은 클래스가 탄생된다.
  5. Singletonitis (Object 설계) : Singleton Pattern의 불필요하게 남용
  6. Yo-yo Problem(Object 설계) : 구조를 지나치게 분리하게 되면, 더욱 이해하기 힘든 구조가 된다. 
  7. Action at a Distance (개발) : 예상되지 않은 상호작용은 시스템을 광범위하게 구분해 버린다. 
  8. 변조 된 요구사항 (Phony Requirements - 아키텍쳐) : 프로젝트에서 모든 요구사항이 문서로 상세화 되지 않고, 유선이나 개개인의 구두로 전달이 된다면 변조(허위) 된 요구사항이 발생된다.
  9. 애매한 관점 (Ambiguous Viewpoint - 개발) : 명확하지 않은 관점에서 모델링을 수행하면 객체 모델링에서 객체들을 정확하게 정의 할 수 없다.
이상 Anti Pattern에 대해 간략하게 알아봤는데, 추가로 Anti Pattern에 대해 좀더 이해하고, 이를 개발과정에 반영하기를 원한다면 다음의 책들을 참조하기 바란다.

Anti Pattern wiki article : http://en.wikipedia.org/wiki/Anti-pattern
Anti Patterns : Refactoring Software, Architectures, and Projects in Crisis,1998 by William J.Brown & …
Anti Patterns and Patterns in Software Configuration Management, 1998 by William J.Brown & …
Posted by as.wind.914