Reputation: 183
I have a Java tomcat project, and in my integration test, we deploy the embedded tomcat to run test against it.
I use spring to initiate a data connection
<bean id="mainDataSource" class="org.springframework.jdbc.datasource.SingleConnectionDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="${database.url}"/>
<property name="username" value="dbUserName"/>
<property name="password" ref="dbPassword"/>
<property name="autoCommit" value="false"/>
<property name="suppressClose" value="true"/>
</bean>
<bean id="dbPassword" class="java.lang.String" factory-bean="passwordFactory" factory-method="unwrapPassword">
<constructor-arg value="#{systemProperties['database.password']!=null ? systemProperties['database.password'] : '${database.password}'}" />
</bean>
<bean id="dbUserName" class="java.lang.String">
<constructor-arg value="#{systemProperties['database.username']!=null ? systemProperties['database.username'] : '${database.username}'}" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="mainDataSource" />
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml" />
<property name="mapperLocations"
value="classpath*:mycompany/dao/mappers/**/*.xml" />
</bean>
And I make this configuration be able to switch the username/password using system property. Now I am trying to test the username/password get used, we have following code
String username = ((SingleConnectionDataSource)sqlSessionFactory.getConfiguration().getEnvironment().getDataSource()).getUsername();
String password = ((SingleConnectionDataSource)sqlSessionFactory.getConfiguration().getEnvironment().getDataSource()).getPassword();
LOGGER.info("USERNAME/PASSWORD: " + username/password);
But I got this weird exception:
Constructor threw exception; nested exception is java.lang.ClassCastException:
org.springframework.jdbc.datasource.SingleConnectionDataSource cannot be cast to org.springframework.jdbc.datasource.SingleConnectionDataSource
And if I put this
LOGGER.info(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource().getClass().toString());
LOGGER.info(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource() instanceof org.springframework.jdbc.datasource.SingleConnectionDataSource? "True": "False");
I got following result
"class org.springframework.jdbc.datasource.SingleConnectionDataSource"
"False"
Wondering what is wrong, why the cast fail, it seems the class/type is correct.
Upvotes: 0
Views: 75
Reputation: 86774
This is the dreaded "class A cannot be cast to class A" message.
The cause of this is that you have two copies of the Spring Framework libraries available in Tomcat, one likely in Tomcat's classpath and one in your application's classpath.
Even though the two libraries are identical, you have instances being loaded by different classloaders, and the JVM considers both the class identity AND the classloader that loaded the class when determining compatibility. In your case, Tomcat is returning a SingleConnectionDataSource
instance created through one classloader and you are trying to cast to a class created through a different classloader.
This is hard to debug, but I would start by making sure you have only one copy of Spring deployed.
Upvotes: 1