Skip to content

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

@spring-projects-issues

Description

@spring-projects-issues

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:

Backported to: 3.2.9

Metadata

Metadata

Assignees

Labels

in: messagingIssues in messaging modules (jms, messaging)status: backportedAn issue that has been backported to maintenance branchestype: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions