I learned that CachingConnectionFactory has the ability to cache JMS session. However, I don't understand how can I retrieve the cached Session programmingly.
My Spring configuration looks like this:
<bean id="jmsConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<construction-arg index="0" ref="connectionFactory"/>
<property name="sessionCacheSize" value="50" />
</bean>
where connectionFactory is the connection factory created by JNDI.
The way I create session is that:
QueueConnection connection = jmsConnectionFactory.createQueueConnection();
queueConnection.createQueueSession(false, Session.DUP_OK_ACKNOWLEDGE);
However, it looks like the session created by createQueueSession is always a new session rather than cached ones. It takes about 1.5 milliseconds to create a session, which doesn't sound retrieved a cached one.
Can someone please let me know how can I get the session cache working?
Related
I am working in a project using Spring, Spring Data JPA, Spring Security, Primefaces...
I was following this tutorial about dynamic datasource routing with spring.
In this tutorial, you can only achieve dynamic datasource switching between a pre-defined datasources.
Here is a snippet of my code :
springContext-jpa.xml
<bean id="dsCgWeb1" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName.Cargest_web}"></property>
<property name="url" value="${jdbc.url.Cargest_web}"></property>
<property name="username" value="${jdbc.username.Cargest_web}"></property>
<property name="password" value="${jdbc.password.Cargest_web}"></property>
</bean>
<bean id="dsCgWeb2" class="org.apache.commons.dbcp.BasicDataSource">
// same properties, different values ..
</bean>
<!-- Generic Datasource [Default : dsCargestWeb1] -->
<bean id="dsCgWeb" class="com.cargest.custom.CargestRoutingDataSource">
<property name="targetDataSources">
<map>
<entry key="1" value-ref="dsCgWeb1" />
<entry key="2" value-ref="dsCgWeb2" />
</map>
</property>
<property name="defaultTargetDataSource" ref="dsCgWeb1" />
</bean>
What i want to do is to make the targetDataSources map dynamic same as its elements too.
In other words, i want to fetch a certain database table, use properties stored in that table to create my datasources then put them in a map like targetDataSources.
Is there a way to do this ?
Nothing in AbstractRoutingDataSource forces you to use a static map of DataSourceS. It is up to you to contruct a bean implementing Map<Object, Object>, where key is what you use to select the DataSource, and value is a DataSource or (by default) a String referencing a JNDI defined data source. You can even modify it dynamically since, as the map is stored in memory, AbstractRoutingDataSource does no caching.
I have no full example code. But here is what I can imagine. In a web application, you have one database per client, all with same structure - ok, it would be a strange design, say it is just for the example. At login time, the application creates the datasource for the client and stores it in a map indexed by sessionId - The map is a bean in root context named dataSources
#Autowired
#Qualifier("dataSources");
Map<String, DataSource> sources;
// I assume url, user and password have been found from connected user
// I use DriverManagerDataSource for the example because it is simple to setup
DataSource dataSource = new DriverManagerDataSource(url, user, password);
sources.put(request.getSession.getId(), dataSource);
You also need a session listener to cleanup dataSources in its destroy method
#Autowired
#Qualifier("dataSources");
Map<String, DataSource> sources;
public void sessionDestroyed(HttpSessionEvent se) {
// eventually cleanup the DataSource if appropriate (nothing to do for DriverManagerDataSource ...)
sources.remove(se.getSession.getId());
}
The routing datasource could be like :
public class SessionRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
HttpServletRequest request = ((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getRequest();
return request.getSession().getId();
}
#Autowired
#Qualifier("dataSources")
public void setDataSources(Map<String, DataSource> dataSources) {
setTargetDataSources(dataSources);
}
I have not tested anything because it would be a lot of work to setting the different database, but I thing that it should be Ok. In real world there would not be a different data source per session but one per user with a count of session per user but as I said it is an over simplified example.
The datasource used by a thread might change from time to time.
Should pay attention to concurrency, applications might get concurrency issues in concurrent environment.
thread-bound AbstractRoutingDataSource sample
It can be achieved with AbstractRoutingDataSource and keeping the information in the thread-local Variable. Here is a beautiful working example you can refer to:
Multi-tenancy: Managing multiple datasources with Spring Data JPA
How can I dynamically (at run time) change the DB username and password used by a DataSource when using a Spring transaction manager?
We are using Spring and have a BasicDataSource and a TransactionManager defined as follows for handling database connections and transactions:
<bean id="myDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="url" ref="dbUrl"/>
<property name="username" ref="someUsername"/>
<property name="password" ref="somePassword"/>
<property name="driverClassName" value="org.postgresql.Driver" />
...
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="myDataSource" />
<tx:annotation-driven transaction-manager="transactionManager" />
We need to support dynamically changing the password that we are using at runtime. Note that I am not trying to change the password in the DB; that has already happened outside of the application. I am trying to tell my application to switch passwords that it is using to connect to the DB.
I tried extending BasicDataSource and calling setUsername() and setPassword(), but it appears that changing the username and password have no effect. I can see by looking into the implementation of BasicDataSource that the username and password appear to only be used when it initially constructs the connection pool.
I then found org.apache.commons.dbcp2.datasources.SharedPoolDataSource and thought that would be the answer to my problems. But it looks like DataSourceTransactionManager takes a javax.sql.DataSource, which surprisingly SharedPoolDataSource is not.
I can't find any other transaction managers that would take a SharedPoolDataSource or even a javax.sql.ConnectionPoolDataSource.
Is there a way to use a transaction manager and a connection pool and dynamically change the DB password that is used?
I have a CachingConnectionFactory with multiple addresses. When one broker goes down, it connects with the second. Now, when the broker comes up again, I need to destroy existing connections and reconnect to it. But CachingConnectionFactory doesn't have any start, stop methods, instead has only destroy, which might render the factory unusable(?).
Config:
<bean id="testConnFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<property name="addresses" value="rabbit1,rabbit2" />
<property name="cacheMode" value="CONNECTION" />
<property name="connectionCacheSize" value="${connection.cache.size}" />
</bean>
Is there any way to do this, programatically?
Calling destroy() is safe; the connection(s) will be reset and re-established the next time a component wants one.
Bear in mind, though, that this will impact any in-process operations.
We should probably add a less scary method, such as resetConnection() like we have with the Spring JMS connection factory.
I am looking for a way to put CRUD of two data source into one transaction by Spring/JTA, but have following questions:
Is it ok that the datasource is is made oforg.apache.commons.dbcp.BasicDataSource instead of JNDI?
There are two parameters to intitiate a JtaTransactionManager:
public JtaTransactionManager(UserTransaction userTransaction,
TransactionManager transactionManager)
From Spring document:
userTransaction - the JTA UserTransaction to use as direct reference
transactionManager - the JTA TransactionManager to use as direct reference
If I have a datasource DS_A and another datasource DS_B, which both requires within the same transaction manager, then which should be the UserTransaction and how to define the TransactionManager, as I am just creating the JtaTransactionManager, does it mean another JtaTransactionManager? Also it looks both parameters are not required so how JtaTransactionManager can detect them from:
<context:annotation-driven/>
<tx:jta-transaction-manager/>
If everything is well defined in application context, then which transaction manager bean should referred in #Transactional annotation, if there are two datasources?
Is it ok that the datasource is is made oforg.apache.commons.dbcp.BasicDataSource instead of JNDI?
JNDI is just a way to access the datasource. To have distributed transactions across databases you need to use transactional data sources. For e.g. microsoft sql server jdbc driver has a transactional data source - com.microsoft.sqlserver.jdbc.SQLServerXADataSource.
If I have a datasource DS_A and another datasource DS_B, which both requires within the same transaction manager, then which should be the
UserTransaction and how to define the TransactionManager, as I am just
creating the JtaTransactionManager, does it mean another
JtaTransactionManager?
JtaTransactionManager is an implementation for JTA, delegating to a backend JTA provider. So you would need a JTA provider that implements distributed transactions. Bitronix is one such JTA provider. There are various configurations needed
a. hibernate.properties
<prop key="hibernate.transaction.factory_class">
<prop key="hibernate.transaction.manager_lookup_class">
<property name="useTransactionAwareDataSource" value="true"/>
b. Defining transaction manager and usertransaction beans
<bean id="BitronixTransactionManager" factory-method="getTransactionManager"
class="bitronix.tm.TransactionManagerServices" depends-on="bitronixConfiguration"
destroy-method="shutdown">
</bean>
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="BitronixTransactionManager"/>
<property name="userTransaction" ref="BitronixTransactionManager"/>
<property name="allowCustomIsolationLevels" value="true"/>
</bean>
c. Transaction Interceptor and annotation support
<bean id="annotationTransactionSource"
class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"/>
<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref local="transactionManager"/>
</property>
<property name="transactionAttributeSource">
<ref local="annotationTransactionSource"/>
</property>
</bean>
Spring 3.1 also introduced a #EnableTransactionManagement annotation so another way to configure would be
#Configuration
#EnableTransactionManagement
public class PersistenceJPAConfig{
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(){
...
}
#Bean
public PlatformTransactionManager transactionManager(){
//configure JTA transaction manager
return transactionManager;
}
}
If everything is well defined in application context, then which transaction manager bean should referred in #Transactional annotation,
if there are two datasources?
I assume the above explanation should answer this question. There is just one transaction manager managing transactions over the two data sources. These are called distributed transaction and they use a two phase commit protocol. Go through this link for an introductory article on XA transactions.
I have a situation where I have to handle multiple clients in one app and each client has separate database. To support that I'm using Spring custom scope, quite similar to the built in request scope. A user authenticates in each request and can set context client ID based passed credentials. The scoping itself seems to be working properly.
So I used my custom scope to create a scoped-proxy for my DataSource to support a diffrent database per client. And I get connections to proper databases.
Than I created a scoped-proxy for EntityManagerFactory to use JPA. And this part also looks OK.
Than I added a scoped-proxy for PlatformTransactionManager for declarative transaction management. I use #Transactional on my service layer and it gets propagated nicely to my SpringData powered repository layer.
All is fine and works correctly as long a s I use only JPA. I can even switch context to a diffrent client within the request (I use ThreadLocals under the hood) and transactions to both databases are handled correctly.
The problems start when I try to use JDBCTempate in one of my custom repositiries. Than at first glance all looks OK too, as no exceptions are thrown. But when I check the database for the objects I thought I inserted with my custom JDBC-based repository the're not there!
I know for sure I can use JPA and JDBC together by declaring only JpaTransactionManager and passing both the DataSource and EntityManagerFactory to it - I checked it and without the scoped-proxies and it works.
So the question is how to make JDBC work together with JPA using the JpaTransactionManager when I have scoped-proxied the DataSource, EntityManagerFactory and PlatformTransactionManager beans? I remind that using only JPA works perfectly, but adding plain JDBC into the mix is not working.
UPDATE1: And one more thing: all readonly (SELECT) operations work fine with JDBC too - only writes (INSERT, UPDATE, DELETE) end up not commited or rolledback.
UPDATE2: As #Tomasz suggested I've removed scoped proxy from EntityManagerFactory and PlatformTransactionManager as those are indeed not needed and provide more confusion than anything else.
The real problem seems to be switching the scope context within a transaction. The TransactionSynchronizationManager bounds transactional resources (i.e. EMF or DS) to thread at transaction start. It has the ability to unwrap the scoped proxy, so it binds the actual instance of the resource from the scope active at the time of starting a transaction. Then when I change the context within a transaction it all gets messed up.
It seems like I need to suspend the active transaction and store aside the current transaction context to be able to clear it upon entering another scope to make Spring think it's not inside a transaction any more and to force it create one for the new scope when needed. And then when leaving the scope I'd have to restore the previously suspended transaction. Unfortunatelly I was unable to come up with a working implementation yet. Any hints appreciated.
And below is some code of mine, but it's pretty standard, except for the scoped-proxies.
The DataSource:
<!-- provides database name based on client context -->
<bean id="clientDatabaseNameProvider"
class="com.example.common.spring.scope.ClientScopedNameProviderImpl"
c:clientScopeHolder-ref="clientScopeHolder"
p:databaseName="${base.db.name}" />
<!-- an extension of org.apache.commons.dbcp.BasicDataSource that
uses proper database URL based on database name given by above provider -->
<bean id="jpaDataSource" scope="client"
class="com.example.common.spring.datasource.MysqlDbInitializingDataSource"
destroy-method="close"
p:driverClassName="${mysql.driver}"
p:url="${mysql.url}"
p:databaseNameProvider-ref="clientDatabaseNameProvider"
p:username="${mysql.username}"
p:password="${mysql.password}"
p:defaultAutoCommit="false"
p:connectionProperties="sessionVariables=storage_engine=InnoDB">
<aop:scoped-proxy proxy-target-class="false" />
</bean>
The EntityManagerFactory:
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
p:database="MYSQL"
p:generateDdl="true"
p:showSql="true" />
<util:properties id="jpaProperties">
<!-- omitted for readability -->
</util:properties>
<bean id="jpaDialect"
class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:packagesToScan="com.example.model.core"
p:jpaVendorAdapter-ref="jpaVendorAdapter"
p:dataSource-ref="jpaDataSource"
p:jpaDialect-ref="jpaDialect"
p:jpaProperties-ref="jpaProperties" />
The PlatformTracsactionManager:
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager"
p:dataSource-ref="jpaDataSource"
p:entityManagerFactory-ref="entityManagerFactory" />
<tx:annotation-driven proxy-target-class="false" mode="proxy"
transaction-manager="transactionManager" />