Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SPR-10829: Spring JMS problem with Oracle AQ still exists if underlying DataSource is decorated [SPR-11791] #16412

Closed
spring-projects-issues opened this issue May 15, 2014 · 3 comments
Assignees
Labels
in: messaging status: backported type: bug
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

@spring-projects-issues spring-projects-issues commented May 15, 2014

Ravi Sanwal opened SPR-11791 and commented

This is a re-open of #15455.
Some work was done around this. We initially thought that the fix works. But seems like it doesn't.
Here is a summary of the fix:

catch (javax.jms.IllegalStateException ex) {
				if (this.connectionFactory != null) {
					try {
						Method getDataSourceMethod = this.connectionFactory.getClass().getMethod("getDataSource");
						Object ds = ReflectionUtils.invokeMethod(getDataSourceMethod, this.connectionFactory);
						if (ds != null && TransactionSynchronizationManager.hasResource(ds)) {
							// IllegalStateException from sharing the underlying JDBC Connection
							// which typically gets committed first, e.g. with Oracle AQ --> ignore
							return;
						}
					}
					catch (Throwable ex2) {
						if (logger.isDebugEnabled()) {
							logger.debug("No working getDataSource method found on ConnectionFactory: " + ex2);
						}
						// No working getDataSource method - cannot perform DataSource transaction check
					}
				}
				throw ex;
			}
		}

This is for ignoring the IllegalArgumentException
The problem with this fix is that the datasource object returned by the reflective invocation of getDataSource on the connection factory (which is oracle.jms.AQjmsQueueConnectionFactory) returns org.springframework.data.jdbc.config.oracle.AqJmsFactoryBeanFactory$TransactionAwareDataSource, which is a transactionally wrapped proxy (to use transactional datasource resource and also to suppress "close" etc on the connection returned) around the actual datasource. So TransactionSynchronizationManager.hasResource(ds) returns false.

I don't know what is an easy and clean fix to this.
But there is one way to deal with the problem.
org.springframework.data.jdbc.config.oracle.AqJmsFactoryBeanFactory$TransactionAwareDataSource is a org.springframework.jdbc.datasource.DelegatingDataSource which gives access to the actual target datasource.
So an instance check can be done and if matches, the target datasource can be retrieved.
By adding the following before checking the transaction synchronization manager for the datasource resource:

if(DelegatingDataSource.class.isInstance(ds)) {
    ds = ((DelegatingDataSource)ds).getTargetDataSource();
}

The catch block would then look like:

catch (javax.jms.IllegalStateException ex) {
                if (this.connectionFactory != null) {
                    try {
                        Method getDataSourceMethod = this.connectionFactory.getClass().getMethod("getDataSource");
                        Object ds = ReflectionUtils.invokeMethod(getDataSourceMethod, this.connectionFactory);
                        if(DelegatingDataSource.class.isInstance(ds)) {
                            ds = ((DelegatingDataSource)ds).getTargetDataSource();
                        }
                        if (ds != null && TransactionSynchronizationManager.hasResource(ds)) {
                            // IllegalStateException from sharing the underlying JDBC Connection
                            // which typically gets committed first, e.g. with Oracle AQ --> ignore
                            return;
                        }
                    }
                    catch (Throwable ex2) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("No working getDataSource method found on ConnectionFactory: " + ex2);
                        }
                        // No working getDataSource method - cannot perform DataSource transaction check
                    }
                }
                throw ex;
            }
	}

Basically make the transaction synchronization check on the target if needed.


Affects: 3.2.5, 3.2.8, 4.0.1, 4.0.4

Issue Links:

  • #15455 spring-jms 3.0.4 introduces a change that breaks using JMSTemplate (and for that matter any JMS resource) with Oracle AQ when JMS Session are used in SESSION_TRANSACTED mode.

Backported to: 3.2.9

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented May 16, 2014

Juergen Hoeller commented

JmsResourceHolder checks for nested DataSource transactions as well now.

This will be available in the upcoming 4.0.5 and 3.2.9 snapshots in about an hour. Please give it a try and let us know whether it works for you...

We're about to release 4.0.5 and 3.2.9 Monday night, so we're unfortunately in a bit of a hurry already.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented May 16, 2014

Juergen Hoeller commented

FYI, this (and a few other recent fixes) are available in the latest 4.0.5 and 3.2.9 snapshots now. Please give one of those snapshots a try if you have the chance, ideally until Monday.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented May 16, 2014

Ravi Sanwal commented

Verified to work on 3.2.9 snapshot build. The fix is good.
Thanks for the quick turn around.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: messaging status: backported type: bug
Projects
None yet
Development

No branches or pull requests

2 participants