[Spring rollback/jotm]Erros implementação p/ rollback em 2 datasource [RESOLVIDO]

[color=red][size=18]DEFINIÇÃO DO PROBLEMA 1 :[/size][/color]

Meu alvo principal é que o spring faça o rollback dos dados do banco após o teste unitário.
Já implementei isso com sucesso usando apenas um dataSource. (AbstractTransactionalDataSourceSpringContextTests)

Mas para o spring interpretar dois datasource, preciso de definir um transactionManager e utilizar outra classe da api (AbstractTransactionalSpringContextTests), juntamente com a api do jotm ( separada ou a que vem no spring ).
A api jotm me permite definir para o spring “visualizar dois dataSource’s através de um transactionManager”.

Fiz a implementação das duas maneiras: com a api do jotm separada e com a inclusa no spring.

Ao rodar os testes unitários das 2 formas, está dando o mesmo erro.
Mas estranho que acho que o erro é algo sobre a obtenção do application context pelo spring.

Obs: Nestas classes usadas para o spring fazer o rollback (ex:AbstractTransactionalDataSourceSpringContextTests e AbstractTransactionalSpringContextTests), deve-se implementar o método getConfigLocations() da superclasse, ao inves de usar um “ApplicationContext context = new ClassPathXmlApplicationContext(“applicationContext-test.xml”);” (como seria feito em um teste unitário simples do JUnit ( extends TestCase ). Assim foi feito para meu teste com apenas um dataSource ( extends AbstractTransactionalDataSourceSpringContextTests ) e tambem para este teste que estou com problemas ( extends AbstractTransactionalSpringContextTests) .

O erro:

Meu código:

XML utilizando a api jotm separada (parte):

	<bean id="jotm" class="org.objectweb.jotm.Jotm">
    	<constructor-arg index="0" value="true"/>
    	<constructor-arg index="1" value="false"/>
    </bean>
	 
	<bean id="transactionManager" factory-bean="jotm" factory-method="getTransactionManager"/>

XML utilizando a api jotm inclusa no spring (parte):

	<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean" />

	<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
		<property name="userTransaction" ref="jotm" />
		<property name="transactionManager" ref="jotm" />
	</bean>

parte .java:

public class UserServiceImplTest extends
        AbstractTransactionalSpringContextTests {

    protected String[] getConfigLocations() {
        String applicationContext1 = "classpath:applicationContext-centralsegapp-test.xml";
        String[] configs = new String[] { applicationContext1 };
        return configs;
    }

//métodos test* ocultos porque nem chega a executá-los.

[color=red]Minha suspeita sobre algo relativo à “obtenção” do applicationContext é porque no erro está dando problema aqui:ConfigurableApplicationContext.registerShutdownHook

E ConfigurableApplicationContext é a superclasse utilizada por classes que adquirem este application context (ex: ClassPathXmlApplicationContext).

PS: Estou usando spring 2.5.4 e já conferí a assinatura do método registerShutdownHook na API do spring 2.0.x e 2.5.x e são IGUAIS.[/color]

[color=red][size=18]SOLUÇÃO DO PROBLEMA 1 :[/size][/color]

Ao verificar o classpath deste teste unitário (Open Run Dialog), notei que lá estava um spring-context da versão 1.0.9.
Aí de alguma forma específica para a implementação com jotm que fiz, ao tentar utilizar o método ConfigurableApplicationContext.registerShutdownHook dava problema.

Mas aí me perguntei: como esta biblioteca estava no classpath do teste se eu nao optei por isso ?

Fui no prompt de comando e executei o comando: “mvn -X > out.txt” para verificar todas bibliotecas que o pom “importa”.
Na árvore das libs, verifiquei que o pom do acegisecurity que utilizo no projeto, necessita desta biblioteca (spring-context).
Sendo assim, removi esta dependencia do pom do acegisecurity; e resolvi meu problema.

            <dependency>
                <groupId>org.acegisecurity</groupId>
                <artifactId>acegi-security</artifactId>
                <version>1.0.6</version>
                          <exclusions>
                                       <exclusion>
                                                <groupId>org.springframework</groupId>
                                                <artifactId>spring-remoting</artifactId>
                                        </exclusion>
					
                                        <exclusion>
                                                <groupId>org.springframework</groupId>
                                                <artifactId>spring-context</artifactId>
                                        </exclusion>
					
                                        <exclusion>
                                                <groupId>org.springframework</groupId>
                                                <artifactId>spring-support</artifactId>
                                        </exclusion>					
					
                            </exclusions>                
             </dependency>

[color=red][size=18]DEFINIÇÃO DO PROBLEMA 2 :[/size][/color]

Na resolução que obtive acima, resolveu o erro citado, mas meu transactionManager não estava sendo atribuido aos dataSource (ou o contrário).
Achei uma solução na web, mas agora preciso de saber quais modificações meus DAO devem sofrer para que eles aceitem um transaction manager do tipo JTATransactionManager, ao invés de TransactionManager. (veja mais especificamente no erro posteriormente). Ou então preciso de uma solução mais simples de atribuir o transactionManager aos meus DAO’s da maneira em que estavam anteriormente ( <bean id=“dataSourceSinPlans” class=“org.apache.commons.dbcp.BasicDataSource” …)

XML:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
    "http://www.springframework.org/dtd/spring-beans-2.0.dtd">

<beans default-autowire="byName">

	<bean id="jotm"
		class="org.springframework.transaction.jta.JotmFactoryBean" />

	<bean id="transactionManager"
		class="org.springframework.transaction.jta.JtaTransactionManager">
		<property name="userTransaction" ref="jotm" />
		<property name="transactionManager" ref="jotm" />
	</bean>

	<bean id="dataSourceSinPlans"
		class="org.enhydra.jdbc.pool.StandardXAPoolDataSource"
		destroy-method="shutdown">
		<property name="dataSource">
			<bean class="org.enhydra.jdbc.standard.StandardXADataSource"
				destroy-method="shutdown">
				<property name="transactionManager" ref="jotm" />
				<property name="driverName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
				<property name="url" value="jdbc:sqlserver://xxxxxxxxxxxxxxx" />
				<property name="user" value="xxxxxxxxxxxxxxxx" />
				<property name="password" value="xxxxxxxxxxxxx" />
			</bean>
		</property>
	</bean>

	<bean id="dataSourceCentralSeg"
		class="org.enhydra.jdbc.pool.StandardXAPoolDataSource"
		destroy-method="shutdown">
		<property name="dataSource">
			<bean class="org.enhydra.jdbc.standard.StandardXADataSource"
				destroy-method="shutdown">
				<property name="transactionManager" ref="jotm" />
				<property name="driverName" value="com.mysql.jdbc.Driver" />
				<property name="url" value="jdbc:mysql://localhost/xxxxxx" />
				<property name="user" value="xxxxxxxxxxxxxxxx" />
				<property name="password" value="xxxxxxxxxxxxx" />
			</bean>
		</property>
	</bean>
	<bean id="sinPlansDAO"
		class="br.com.ctbc.centralseg.model.persistence.dao.jdbc.JdbcSinPlansDAO">
		<property name="dataSource" ref="dataSourceSinPlans" />
	</bean>

	<bean id="serviceDAO"
		class="br.com.ctbc.centralseg.model.persistence.dao.jdbc.JdbcServiceDAO">
		<property name="dataSource" ref="dataSourceCentralSeg" />
	</bean>

	<bean id="licenseDAO"
		class="br.com.ctbc.centralseg.model.persistence.dao.jdbc.JdbcLicenseDAO">
		<property name="dataSource" ref="dataSourceCentralSeg" />
	</bean>

	<bean id="userDAO"
		class="br.com.ctbc.centralseg.model.persistence.dao.jdbc.JdbcUserDAO">
		<property name="dataSource" ref="dataSourceCentralSeg" />
	</bean>

</beans>

[color=red]As classes DAO extends org.springframework.jdbc.core.support.JdbcDaoSupport.[/color]

ERRO:

! help !

Se alguem nao entendeu algo do que eu descrevi, pergunte.
Não estou encontrando solução e estou precisando de ajuda !
8)

[color=red][size=18]SOLUÇÃO DO PROBLEMA 2 :[/size][/color]

Creio ter resolvido o problema.

O erro que descrevi acima não era referente aos DAO, e sim aos dataSource. pois na definição da classe dos dataSource (“org.enhydra.jdbc.standard.StandardXADataSource”); o transactionManager deve ser do tipo javax.transaction.TransactionManager e não estava aceitando org.springframework.transaction.jta.JtaTransactionManager.

Só não sei como teria funcionado no exemplo que ví, mas acho que o erro é este.
Estarei testando agora …

Resumo:

com o erro:

	<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean" />

	<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
		<property name="userTransaction" ref="jotm" />
		<property name="transactionManager" ref="jotm" />
	</bean>

	<bean id="dataSourceSinPlans" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
	   <property name="dataSource">
	      <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
	        <property name="transactionManager" ref="transactionManager" />

solução. Para solucionar vou pegar o transactionmanager pela api do jotm (pois é javax.transaction.TransactionManager) , e não pelo JotmFactoryBean do spring (cuja transactionmanager é org.springframework.transaction.jta.JtaTransactionManager)

	<bean id="jotm" class="org.objectweb.jotm.Jotm">
    	<constructor-arg index="0" value="true"/>
    	<constructor-arg index="1" value="false"/>
    </bean>

	<bean id="transactionManager" factory-bean="jotm" factory-method="getTransactionManager"/>

	<bean id="dataSourceSinPlans" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
	   <property name="dataSource">
	      <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
	        <property name="transactionManager" ref="transactionManager" />

[color=red][size=18]DEFINIÇÃO DO PROBLEMA 3 :[/size][/color]

Só pra variar.
Ainda estou com problemas

Descobri que para definitivamente usar o transactionManager no meu teste unitário (AbstractTransactionalSpringContextTests), é preciso que eu override o método prepareTestInstance, setando o transactionManager.

Pois assim fiz. Mas mesmo assim ao rodar o teste, parece que não está reconhecendo o que faço lá…

[code]
public class UserServiceImplTest extends
AbstractTransactionalSpringContextTests {

public UserServiceImplTest() {
    setDefaultRollback(true);
    setAutowireMode(AUTOWIRE_BY_NAME);
}

protected void prepareTestInstance() throws Exception {
    /*
     * setting dependencyCheck to false, because API says that
     * setTransactionManager just works if dependency checking is turned off
     */
    super.setDependencyCheck(false);

    Current jotmCurrent = (Current) this.applicationContext
            .getBean("transactionManager");

    /*
     * org.objectweb.transaction.jta.TransactionManager that comes from
     * jotm.getTransactionManager
     */
    TransactionManager transactionManager = jotmCurrent
            .getTransactionManager();

    /*
     * creating org.springframework.transaction.PlatformTransactionManager
     * from jotm above, because
     * AbstractTransactionalSpringContextTests.setTransactionManager doesnt
     * accept this class type
     */
    PlatformTransactionManager jtaTransactionManager = new JtaTransactionManager(
            transactionManager);

    super.setTransactionManager(jtaTransactionManager);
    super.prepareTestInstance();
}

protected String[] getConfigLocations() {
    return new String[] { "classpath:applicationContext-centralsegapp-test.xml" };
}[/code]

erro:

[color=red][size=18]O que fazer ?[/size][/color]

[color=red][size=18]SOLUÇÃO DO PROBLEMA 3 :[/size][/color]

aew ! resolvi este aí de cima tambem.
foi só comentar a linha:
setAutowireMode(AUTOWIRE_BY_NAME);

mas adivinha se executou sem erro … NAO !

[color=red][size=18]DEFINIÇÃO DO PROBLEMA 4 :[/size][/color]

Próximo erro:
java.sql.SQLException: Cannot get connection for URL jdbc:mysql://localhost/centralseg : Access denied for user ‘’@‘localhost’ (using password: NO)

Lá no dataSource desta conexão eu coloquei o password, pq que ele está me informando “using password: NO” ? Onde configuro para ele usar o password ?

nao estou conseguindo resolver.

antes os dataSource estavam funcionando , com a classe de teste extendendo TestCase (teste junit normal)

aí pra possibilitar a implementação deste transactionmanager a unica mudança que fiz foi trocar a classe dos datasource de org.apache.commons.dbcp.BasicDataSource pra org.enhydra.jdbc.standard.StandardXADataSource.

mas as propriedades “usuario”, “senha”, “url” e “driverName” estão mapeadas igual estava. porque agora estaria dando problema com password ?

ERRO (note que é no dataSource localhost mesmo):

XML-AGORA:

    <bean id="dataSourceCentralSeg" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="close" lazy-init="true">
	   <property name="dataSource">
	      <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="close" lazy-init="true">
	        <property name="transactionManager" ref="transactionManager" />
        	<property name="driverName" value="com.mysql.jdbc.Driver"/>
        	<property name="url" value="jdbc:mysql://localhost/centralseg"/>
        	<property name="user" value="root"/>
        	<property name="password" value="admin"/>
          </bean>
        </property>
	</bean>

XML-ANTES:

	<bean id="dataSourceCentralSeg" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" lazy-init="true">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost/centralseg"/>
        <property name="username" value="root"/>
        <property name="password" value="admin"/>
        <property name="testOnBorrow" value="false"/>
        <property name="testWhileIdle" value="true"/>
        <property name="timeBetweenEvictionRunsMillis" value="600000"/>
    </bean>

Onde configuro para ele usar o password ?

[color=red][size=18]SOLUÇÃO DO PROBLEMA 4 :[/size][/color]

Colocar as propriedades username e password no XAPool, e não no XADataSource. Removê-las do XADataSource

Como ficou:

     &lt;bean id="dataSourceCentralSeg" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="close" lazy-init="true"&gt;
	   &lt;property name="dataSource"&gt;
	      &lt;bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="close" lazy-init="true"&gt;
	        &lt;property name="transactionManager" ref="transactionManager" /&gt;
        	&lt;property name="driverName" value="com.mysql.jdbc.Driver"/&gt;
        	&lt;property name="url" value="jdbc:mysql://localhost/centralseg"/&gt;
          &lt;/bean&gt;
        &lt;/property&gt;
       	&lt;property name="user" value="root"/&gt;
       	&lt;property name="password" value="admin"/&gt;
	&lt;/bean&gt;

O pedaço do .java pra quem for precisar:

public class UserServiceImplTest extends
        AbstractTransactionalSpringContextTests {

    public UserServiceImplTest() {
        setDefaultRollback(true);
        // setAutowireMode(AUTOWIRE_BY_NAME);
    }

    protected void prepareTestInstance() throws Exception {

        /*
         * setting dependencyCheck to false, because API says that
         * setTransactionManager just works if dependency checking is turned off
         */
        super.setDependencyCheck(false);

        /*
         * org.objectweb.transaction.jta.TransactionManager that comes from
         * jotm.getTransactionManager
         */
        TransactionManager transactionManager = ((Current) applicationContext
                .getBean("transactionManager")).getTransactionManager();

        /*
         * creating org.springframework.transaction.PlatformTransactionManager
         * from jotm above, because
         * AbstractTransactionalSpringContextTests.setTransactionManager doesnt
         * accept this jotm generated transactionManager class type
         */
        PlatformTransactionManager jtaTransactionManager = new JtaTransactionManager(
                transactionManager);

        super.setTransactionManager(jtaTransactionManager);

        super.prepareTestInstance();

    }

    protected String[] getConfigLocations() {
        return new String[] { "classpath:applicationContext-centralsegapp-test.xml" };
    }

    Logger log = Logger.getLogger(getClass());

    IUserService userService = null;

    public IUserService getUserService() {
        return userService;
    }

    public void setUserService(IUserService userService) {
        this.userService = userService;
    }

    public void testRegisterService() {
           //testes normais a partir daki ...
    }
}