티스토리 툴바


Closing a statement you left open, please do your own housekeeping

2011/01/26 20:55 | Posted by 포데브(엄지사랑) 엄지사랑
환경: JDK1.6.0_21 + JBoss EAP 4.3.2 + Spring 2.5.6 + iBATIS 2.3.4

19:50:53,405 WARN  [WrappedConnection] Closing a statement you left open, please do your own housekeeping
java.lang.Throwable: STACKTRACE
at org.jboss.resource.adapter.jdbc.WrappedConnection.registerStatement(WrappedConnection.java:872)
at org.jboss.resource.adapter.jdbc.WrappedStatement.<init>(WrappedStatement.java:62)
at org.jboss.resource.adapter.jdbc.WrappedPreparedStatement.<init>(WrappedPreparedStatement.java:56)
at org.jboss.resource.adapter.jdbc.WrappedConnection.prepareStatement(WrappedConnection.java:241)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy$TransactionAwareInvocationHandler.invoke(TransactionAwareDataSourceProxy.java:225)


Closing statements and ResultSets is important if you want to use prepared statement caching and/or don't won't to leak cursors in your database.


만약 prepared statement 캐슁을 사용하길 원하면서 데이터베이스의 커서가 누출(leak) 되지 않게 하려면 statements 와 ResultSets 을 닫아주는 것은 중요하다.


위의 링크를 참고해 보면 Connection leak 등을 추적하기 위한 용도로 사용하는 건데 사실 실제 소스코드를 보면 connection을 닫으려고 시도하고 있다. 만약 
Exception trying to close statement:
와 같은 경고를 만났다면 Connection leak이 발생할 수는 있다. 그러나 

Closing a statement you left open, please do your own housekeeping

와 같은 경고는 Connection을 생성한 곳에서 닫아야 한다는 원칙을 개발자에게 상기시켜주면서 해당 컨넥션을 닫아 주려고 시도한다.

결론은 트랜잭션이 제대로 설정되어 있지 않아서 Statement에 대해서 commit 이나 rollback이 발생하지 않았으므로 확인하라는 내용이다.

역시 경고는 무시하면 안된다. 

저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License

No JTA TransactionManager found at fallback JNDI location

2010/12/31 19:47 | Posted by 포데브(엄지사랑) 엄지사랑
Jetty 6.1.11 + Spring 2.5.6에서 트랜잭션관리자를 <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" /> 와 같이 선언하고 로그레벨을 DEBUG 모드로 실행하면 다음과 같은 에러들을 볼 수 있다.

[DEBUG] (JtaTransactionManager.java:717) - No JTA TransactionManager found at fallback JNDI location [java:comp/TransactionManager]
javax.naming.NameNotFoundException; remaining name 'TransactionManager'

...

[DEBUG] (JtaTransactionManager.java:717) - No JTA TransactionManager found at fallback JNDI location [java:appserver/TransactionManager]
javax.naming.NameNotFoundException; remaining name 'appserver/TransactionManager'

...

(JtaTransactionManager.java:717) - No JTA TransactionManager found at fallback JNDI location [java:pm/TransactionManager]
javax.naming.NameNotFoundException; remaining name 'pm/TransactionManager'

...

(JtaTransactionManager.java:717) - No JTA TransactionManager found at fallback JNDI location [java:/TransactionManager]
javax.naming.NameNotFoundException; remaining name 'TransactionManager'

그러나 위의 예외메시지는 무시해도 될 것 같다. 
만약 JBoss에서 실행하면 java:/TransactionManager 은 설정되는 것을 확인했다. 

[DEBUG] (JtaTransactionManager.java:676) - JTA UserTransaction found at default JNDI location [java:comp/UserTransaction]

Jetty에서는 [java:comp/UserTransaction]만 설정하는 것같다.

JtaTransactionManager.java 파일의 내용을 보면 아래와 같은데 그냥 정보성으로 출력만 한다.
/**
* Fallback JNDI locations for the JTA TransactionManager. Applied if
* the JTA UserTransaction does not implement the JTA TransactionManager
* interface, provided that the "autodetectTransactionManager" flag is "true".
* @see #setTransactionManagerName
* @see #setAutodetectTransactionManager
*/
public static final String[] FALLBACK_TRANSACTION_MANAGER_NAMES =
new String[] {"java:comp/TransactionManager", "java:appserver/TransactionManager",
"java:pm/TransactionManager", "java:/TransactionManager"};

...

// Check fallback JNDI locations.
for (int i = 0; i < FALLBACK_TRANSACTION_MANAGER_NAMES.length; i++) {
String jndiName = FALLBACK_TRANSACTION_MANAGER_NAMES[i];
try {
TransactionManager tm = (TransactionManager) getJndiTemplate().lookup(jndiName, TransactionManager.class);
if (logger.isDebugEnabled()) {
logger.debug("JTA TransactionManager found at fallback JNDI location [" + jndiName + "]");
}
return tm;
}
catch (NamingException ex) {
if (logger.isDebugEnabled()) {
logger.debug("No JTA TransactionManager found at fallback JNDI location [" + jndiName + "]", ex);
}
}
}

위의 소스코드를 추적해보면 알겠지만 결국 최종 목적은 UserTransaction을 생성하는 것에 있다. 그리고 TransactionManager는 UserTransaction을 생성하기 위해 필요한 것이다.
       /**
* Initialize the UserTransaction as well as the TransactionManager handle.
* @throws TransactionSystemException if initialization failed
*/
protected void initUserTransactionAndTransactionManager() throws TransactionSystemException {
// Fetch JTA UserTransaction from JNDI, if necessary.
if (this.userTransaction == null) {
if (StringUtils.hasLength(this.userTransactionName)) {
this.userTransaction = lookupUserTransaction(this.userTransactionName);
this.userTransactionObtainedFromJndi = true;
}
else {
this.userTransaction = retrieveUserTransaction();
}
}

// Fetch JTA TransactionManager from JNDI, if necessary.
if (this.transactionManager == null) {
if (StringUtils.hasLength(this.transactionManagerName)) {
this.transactionManager = lookupTransactionManager(this.transactionManagerName);
}
else {
this.transactionManager = retrieveTransactionManager();
}
}

// Autodetect UserTransaction at its default JNDI location.
if (this.userTransaction == null && this.autodetectUserTransaction) {
this.userTransaction = findUserTransaction();
}

// Autodetect UserTransaction object that implements TransactionManager,
// and check fallback JNDI locations else.
if (this.transactionManager == null && this.autodetectTransactionManager) {
this.transactionManager = findTransactionManager(this.userTransaction);
}

// If only JTA TransactionManager specified, create UserTransaction handle for it.
if (this.userTransaction == null && this.transactionManager != null) {
this.userTransaction = buildUserTransaction(this.transactionManager);
}
}

이소스코드로 부터 예외를 내지 않게 하는 방법을 찾았는데 다음과 같이 WAS가 제공하는 것을 설정해주면 정상동작했다. 그러나 굳이 DEBUG 모드에서 스프링을 실행하지 않을 것이고 로드할 당시에만 발생하므로 무시해도 될것 같다.

  JBoss에서 예외로그 없이 동작하는 JtaTransactionManager 설정.
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" >
    <property name="autodetectTransactionManager" value="false"/>
    <property name="transactionManagerName" value="java:/TransactionManager"/>
  </bean>

결론은 내가 너무 예민한 것같다. ^^;
저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License

스프링에서 Multi DataSource 사용

2010/12/14 14:31 | Posted by 포데브(엄지사랑) 엄지사랑
Spring-2.5.6 에서 여러개의 DataSource를 사용하는 방법을 소개한다.

다음과 같이 두개의 데이터 소스가 선언되어 있을 때 자동 주입(Inject)하고자 한다면 
<context:component-scan base-package="com.kyobobook.kflow" />

  <!-- ===================================================================== -->
  <!-- Transaction Configuration                                             -->
  <!-- ===================================================================== -->
  <aop:config proxy-target-class="true" />

  <bean id="riUniTestDataSource"  class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <qualifier value="riUniTestDataSource" />
    <property name="driverClassName" value="com.sybase.jdbc3.jdbc.SybDriver" />
    <property name="url" value="jdbc:sybase:Tds:192.168.0.15:3000" />
    <property name="connectionProperties" value="charset=eucksc;" />
    <property name="username" value="dev_kf" />
    <property name="password" value="dev_kf" />
  </bean>

  <bean id="riDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <qualifier value="riDataSource" />
    <property name="driverClassName" value="com.sybase.jdbc3.jdbc.SybDriver" />
    <property name="url" value="jdbc:sybase:Tds:192.168.0.6:3000/biltis" />
    <property name="connectionProperties" value="charset=eucksc;" />
    <property name="username" value="dev_kb" />
    <property name="password" value="dev_kb" />
  </bean>

첫번째 @Resource annotation을 사용할 수 있다.
    @Resource(name = "riDataSource")
    public void setDataSource(DataSource dataSource) {
        super.buildSqlMapClient(dataSource);
    }

두번째 방법은 @Autowired와 @Qualifier 를 사용하는 방법이다.
    @Autowired
    public void setDataSource(@Qualifier("riDataSource") DataSource dataSource) {
        super.buildSqlMapClient(dataSource);
    }

위 두가지 방법의 차이점은 DI(Dependency Injection)를 어떻게 할 것인가이다. 다음은 Anyframe에서 설명하는 Dependency Injection 에 대한 내용이다.(설명이 잘되어 있어서 퍼왔으나 라이센스가 문제가 된다면 링크로 변경할 것입니다.)

특정 Bean의 기능 수행을 위해 다른 Bean을 참조해야 하는 경우 사용하는 Annotation으로는 @Autowired 또는 @Resource가 있다.
  • @Autowired

  • Spring Framework에서 지원하는 Dependency 정의 용도의 Annotation으로, Spring Framework에 종속적이긴 하지만 정밀한 Dependency Injection이 필요한 경우에 유용하다.

  • @Resource

  • JSR-250 표준 Annotation으로 Spring Framework 2.5.* 부터 지원 가능한 Annotation이다. Annotation 사용으로 인해 특정 Framework에 종속적인 어플리케이션을 구성하지 않기 위해서는 @Resource를 사용할 것을 권장한다. @Resource를 사용하기 위해서는 클래스패스 내에 jsr250-api.jar 파일이 추가되어야 함에 유의해야 한다.
다음은 @Resource를 사용한 예이다.
@Service
public UserServiceImpl implements UserService {
    @Resource
    private UserDAO userDAO;
}

@Autowired와 @Resource를 사용할 수 있는 위치는 다음과 같이 약간의 차이가 있으므로 필요에 따라 적절히 사용하면 된다.
  • @Autowired : 필드, 생성자, 입력파라미터가 여러개인 메소드(@Qualifier는 메소드의 파라미터)에 적용 가능
  • @Resource : 필드, 입력 파라미터가 한 개인 빈 프로퍼티 setter 메소드에 적용가능
@Autowired나 @Resource를 필드에 직접 정의하는 경우 별도 setter 메소드는 정의하지 않아도 된다.


Type-driven Injection

@Autowired는 기본적으로 type-driven injection 이다. 타입으로 참조할 빈을 찾았을 때 같은 타입의 빈이 여러 개 검색되었을 경우, @Qualifier annotation을 사용하여 구분할 수 있도록 해준다. 

다음은 @Qualifier를 사용한 예이다.
@Service
public ProductService {
    @Autowired
    @Qualifier("electronics")
    private ProductCategory productCategory;
}
Qualifier를 정의하는 방법에는 다음과 같이 두가지가 있다.
  • XML을 사용한 정의
  • <bean class="anyframe.sample.springmvc.annotation.web.
    SimpleProductCategory">
        <qualifier value="electronics"/>
        <!-- inject any dependencies required by this bean -->
    </bean>
    
    <bean class="anyframe.sample.springmvc.annotation.web.
    SimpleProductCategory">
        <qualifier value="cosmetics"/>
        <!-- inject any dependencies required by this bean -->
    </bean>
    		
  • Annotation을 사용한 정의
  • @Component
    @Qualifier("electronics")
    public class ElectronicsProductCategory implements ProductCategory {
    	//...
    }

기본적으로 @Autowired가 적용된 프로퍼티는 필수이기 때문에 반드시 해당 빈이 존재해야 하지만, required 속성을 false로 설정하는 경우에는 해당되는 Bean을 찾지 못하더라도 에러가 발생하지 않는다.
@Service
public UserService implements UserService {
    @Autowired(required=false)
    private UserDAO userDAO;
}

Naming Auto Wired Dependencies

@Resource annotation은 다음과 같은 경우에 사용한다.
  • Bean name으로 Dependency Injection을 하고자 하는 경우
  • Type-driven injection을 할 수 없는 Collection이나 Map 타입의 빈
@Resource를 사용하는 경우 참조되는 Bean은 변수명을 Bean Name으로 하여 Spring 컨테이너에 의해 자동으로 인지되는데, 변수명을 이용하여 참조 관계에 놓인 Bean을 찾았는데 해당 Bean이 없는 경우에는 클래스 타입을 이용하게 된다. 참조 관계에 놓인 Bean의 Name을 직접 명시하고자 하는 경우에는 다음과 같이 'name' 속성을 부여할 수 있다.
@Service
public UserServiceImpl implements UserService {
    @Resource (name="uDAO")
    private UserDAO userDAO;
}






저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License
이전 1 다음