Tomcat 서버 하나에 여러개의 war 파일을 배포하여 사용하며 각 application 별로 Spring의 DBCP 를 이용하여 Connection Pooling 을 한다.
문제점
- db 접속 정보(id/pw) 를 한곳에서 관리할 수 없다.
- db 접속 정보가 변경되면 각 war 별로 참조하는 파일의 내용을 변경해 줘야 한다. ( ex 암호화 되어 있는 properties 파일 )
- 개별 connection pooling 의 경우 실수가 발생할 가능성이 높으며 불필요한 서버 리소스가 낭비 된다.
[방안]
- WAS 에서 제공하는 connection pooling 을 사용 한다.
- 각 war 에서는 JNDI 로 Lookup 하여 연결 한다.
- 주의사항 : 접속 하는 데이터베이스는 하나가 된다.
ex) aaa.war 에서는 test 라는 데이터베이스안에 있는 테이블만 사용
bbb.war 에서는 real 이라는 데이터베이스 안에 있는 테이블만 사용
위와 같은 경우 query 에 반드시 aaa.tableName 으로 처리 해줘야 한다.
이전 처럼 개별로 connection 을 관리할 경우에는 connection 자체를 aaa 또는 bbb 로 하기 때문에 table name 만 써도
문제되지 않았지만 이부분은 수정이 되어야 한다.
[처리 방법 정리]
1. Tomcat 설정
server.xml 의 GlobalNameingResource 에 JNDI 내용 추가
<GlobalNamingResources> <Resource name="jdbc/testJNDI" factory="com.yhkim.global.datasource.CustomDataSource" auth="Container" type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000" username="" password="" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://127.0.0.1:3306/databaseName?zeroDateTimeBehavior=convertToNull"/> </GlobalNamingResources> |
노란색 부분을 눈여겨 보자.
일반적인 JNDI 이용 시 factory 부분은 org.apache.tomcat.jdbc.pool.DataSourceFactory 를 사용한다.
하지만 그럴 경우 username 과 password 부분을 암호화 해서 사용할 수 없다.
따라서 custom factory class 를 작성하여 username 와 password 를 복호화 할 수 있도로 한다.
2. CustomDataSource class 작성
apache 의 common-dbcp 를 이용하였다.
아래 java project 에서 build-path 에 common-dbcp-x.x.jar 파일을 포함하고
아래 project 를 globalds.jar (이름은 마음데로) 파일로 export 하여 Tomcat/lib 에 포함시킨다.
../conf/dbinfo.properties 는 즉 설치되는 Tomcat/conf/ 경로가 된다.
Tomcat/conf/에 dbinfo.properties 를 만들고 아래 내용을 넣는다. (테스트를 위해 암호화가 아닌 base64 encoding 을 해도 무관)
username=암호화된아이디
password=암호화된비밀번호
public class CustomDataSource extends BasicDataSourceFactory { public Object getObjectInstance(Object obj, Name name, Context nameCtx, @SuppressWarnings("rawtypes") Hashtable environment) throws Exception { if(obj instanceof Reference) { setUsername((Reference)obj); setPassword((Reference)obj); } return super.getObjectInstance(obj, name, nameCtx, environment); } private void setUsername(Reference ref) throws Exception { findDecryptAndReplace("username", ref); } private void setPassword(Reference ref) throws Exception { findDecryptAndReplace("password", ref); } private void findDecryptAndReplace(String refType, Reference ref) throws Exception { int idx = find(refType, ref); String decrypted = decrypt(refType); replace(idx, refType, decrypted, ref); } private void replace(int idx, String refType, String newValue, Reference ref) throws Exception { ref.remove(idx); ref.add(idx, new StringRefAddr(refType, newValue)); } private String decrypt(String key)throws Exception { return getDecViaSeed(getProperty(key)); } private int find(String addrType, Reference ref) { try{ @SuppressWarnings("rawtypes") Enumeration enu = ref.getAll(); for(int i = 0; enu.hasMoreElements(); i++) { RefAddr addr = (RefAddr)enu.nextElement(); if(addr.getType().compareTo(addrType) == 0) return i; } }catch(Exception e){ e.printStackTrace(); } return 0; } public String getDecViaSeed(String str) { String strDecrypt = ""; try { // strDecrypt = 복호화 처리 // 실제 테스트를 위해서는 간단히 BASE64 정도로 테스트 해봐도 된다. } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return strDecrypt; } public String getProperty(String key) { String rtnStr = ""; try { Properties prop = new Properties(); prop.load(new FileInputStream("../conf/dbinfo.properties")); rtnStr = prop.getProperty(key); } catch ( IOException ioe ) { ioe.printStackTrace(); return null; } catch ( Exception e ) { e.printStackTrace(); } return rtnStr; } } |
3. Tomcat lib 에 필요한 jar 파일 추가
2번에서 생성한 globalds.jar 과
commons-dbcp.jar
commons-collections.jar
commons-pool.jar
파일을 Tomcat/lib 경로에 포함한다.
4. Application 에서 JNDI 이용하기
web application은 Spring + MyBatis 로 구성되어 있으며
jdbc 설정.xml 에 아래와 같이 datasource 및 transaction 부분을 설정 한다. (transaction 처리는 상황에 따라 다르게 처리 가능)
bold 처리된 부분만 보면 된다. jndiName 은 tomcat 의 server.xml 에 있는 이름과 동일 해야 한다.
<beans:bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <beans:property name="jndiName" value="jdbc/testNDI"/> <beans:property name="resourceRef" value="true"/> </beans:bean> <beans:bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" lazy-init="true"> <beans:property name="dataSource" ref="dataSource"/> <beans:property name="configLocation" value="classpath:mybatis/mybatis-configuration.xml" /> <beans:property name="mapperLocations" value="classpath:mybatis/query.xml" /> <beans:property name="transactionFactory"> <beans:bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" /> </beans:property> </beans:bean> <beans:bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"> <beans:constructor-arg ref="sqlSessionFactory" /> </beans:bean>
<beans:bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <beans:property name="dataSource" ref="dataSource" /> </beans:bean> <beans:bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <beans:property name="transactionManager" ref="transactionManager" /> </beans:bean> |
5. Application 의 context.xml 설정
META-INF 폴더 밑에 context.xml 파일이 없다면 생성하고 있다면 아래 내용을 추가 한다.
<?xml version="1.0" encoding="UTF-8"?> <Context path="/applicationName"> <WatchedResource>WEB-INF/web.xml</WatchedResource> <ResourceLink global="jdbc/testJNDI" name="jdbc/testJNDI" type="javax.sql.DataSource"/> </Context> |
'Web Programming > server' 카테고리의 다른 글
톰캣 404 (0) | 2018.09.04 |
---|---|
A child container failed during start (0) | 2018.09.04 |