Skip to content

Commit a9e727c

Browse files
committed
Compatibility with Hibernate 4.3
Due to key SPI interfaces such as ConnectionProvider and JtaPlatform changing their package location in Hibernate 4.3, we have to resort to reflection in a few places. Most importantly, the ConfigurableJtaPlatform used by the setJtaTransactionManager method has now been redesigned as a reflective proxy. Issue: SPR-10839
1 parent 0b37cec commit a9e727c

File tree

5 files changed

+164
-32
lines changed

5 files changed

+164
-32
lines changed
Lines changed: 93 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,60 +16,137 @@
1616

1717
package org.springframework.orm.hibernate4;
1818

19+
import java.lang.reflect.InvocationHandler;
20+
import java.lang.reflect.Method;
21+
import java.lang.reflect.Proxy;
22+
import javax.transaction.Status;
23+
import javax.transaction.Synchronization;
24+
import javax.transaction.SystemException;
25+
import javax.transaction.Transaction;
1926
import javax.transaction.TransactionManager;
27+
import javax.transaction.TransactionSynchronizationRegistry;
2028
import javax.transaction.UserTransaction;
2129

22-
import org.hibernate.service.jta.platform.internal.AbstractJtaPlatform;
30+
import org.hibernate.TransactionException;
31+
import org.hibernate.service.Service;
2332

2433
import org.springframework.transaction.jta.UserTransactionAdapter;
2534
import org.springframework.util.Assert;
2635

2736
/**
28-
* Implementation of Hibernate 4's {@link org.hibernate.service.jta.platform.spi.JtaPlatform}
29-
* SPI, exposing passed-in {@link TransactionManager} and {@link UserTransaction} references.
37+
* Implementation of Hibernate 4's JtaPlatform SPI (which has a different package
38+
* location in Hibernate 4.0-4.2 vs 4.3), exposing passed-in {@link TransactionManager},
39+
* {@link UserTransaction} and {@link TransactionSynchronizationRegistry} references.
3040
*
3141
* @author Juergen Hoeller
3242
* @since 3.1.2
3343
*/
34-
@SuppressWarnings("serial")
35-
class ConfigurableJtaPlatform extends AbstractJtaPlatform {
44+
@SuppressWarnings({"serial", "unchecked"})
45+
class ConfigurableJtaPlatform implements InvocationHandler {
46+
47+
static final Class<? extends Service> jtaPlatformClass;
48+
49+
static {
50+
Class<?> jpClass;
51+
try {
52+
// Try Hibernate 4.0-4.2 JtaPlatform variant
53+
jpClass = SpringSessionContext.class.getClassLoader().loadClass(
54+
"org.hibernate.service.jta.platform.spi.JtaPlatform");
55+
}
56+
catch (ClassNotFoundException ex) {
57+
try {
58+
// Try Hibernate 4.3 JtaPlatform variant
59+
jpClass = SpringSessionContext.class.getClassLoader().loadClass(
60+
"org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform");
61+
}
62+
catch (ClassNotFoundException ex2) {
63+
throw new IllegalStateException("Neither Hibernate 4.0-4.2 nor 4.3 variant of JtaPlatform found");
64+
}
65+
}
66+
jtaPlatformClass = (Class<? extends Service>) jpClass;
67+
}
68+
69+
static String getJtaPlatformBasePackage() {
70+
String className = jtaPlatformClass.getName();
71+
return className.substring(0, className.length() - "spi.JtaPlatform".length());
72+
}
73+
3674

3775
private final TransactionManager transactionManager;
3876

3977
private final UserTransaction userTransaction;
4078

79+
private final TransactionSynchronizationRegistry transactionSynchronizationRegistry;
80+
4181

4282
/**
4383
* Create a new ConfigurableJtaPlatform instance with the given
4484
* JTA TransactionManager and optionally a given UserTransaction.
4585
* @param tm the JTA TransactionManager reference (required)
4686
* @param ut the JTA UserTransaction reference (optional)
87+
* @param tsr the JTA 1.1 TransactionSynchronizationRegistry (optional)
4788
*/
48-
public ConfigurableJtaPlatform(TransactionManager tm, UserTransaction ut) {
89+
public ConfigurableJtaPlatform(TransactionManager tm, UserTransaction ut, TransactionSynchronizationRegistry tsr) {
4990
Assert.notNull(tm, "TransactionManager reference must not be null");
5091
this.transactionManager = tm;
5192
this.userTransaction = (ut != null ? ut : new UserTransactionAdapter(tm));
93+
this.transactionSynchronizationRegistry = tsr;
5294
}
5395

5496

55-
@Override
56-
protected TransactionManager locateTransactionManager() {
97+
public TransactionManager retrieveTransactionManager() {
5798
return this.transactionManager;
5899
}
59100

60-
@Override
61-
protected UserTransaction locateUserTransaction() {
101+
public UserTransaction retrieveUserTransaction() {
62102
return this.userTransaction;
63103
}
64104

65-
@Override
66-
protected boolean canCacheTransactionManager() {
67-
return true;
105+
public Object getTransactionIdentifier(Transaction transaction) {
106+
return transaction;
68107
}
69108

109+
public boolean canRegisterSynchronization() {
110+
try {
111+
return (this.transactionManager.getStatus() == Status.STATUS_ACTIVE);
112+
}
113+
catch (SystemException ex) {
114+
throw new TransactionException("Could not determine JTA transaction status", ex);
115+
}
116+
}
117+
118+
public void registerSynchronization(Synchronization synchronization) {
119+
if (this.transactionSynchronizationRegistry != null) {
120+
this.transactionSynchronizationRegistry.registerInterposedSynchronization(synchronization);
121+
}
122+
else {
123+
try {
124+
this.transactionManager.getTransaction().registerSynchronization(synchronization);
125+
}
126+
catch (Exception ex) {
127+
throw new TransactionException("Could not access JTA Transaction to register synchronization", ex);
128+
}
129+
}
130+
}
131+
132+
public int getCurrentStatus() throws SystemException {
133+
return this.transactionManager.getStatus();
134+
}
135+
136+
70137
@Override
71-
protected boolean canCacheUserTransaction() {
72-
return true;
138+
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
139+
Method targetMethod = getClass().getMethod(method.getName(), method.getParameterTypes());
140+
return targetMethod.invoke(this, args);
141+
}
142+
143+
/**
144+
* Obtain a proxy that implements the current Hibernate version's JtaPlatform interface
145+
* in the right package location, delegating all invocations to the same-named methods
146+
* on this ConfigurableJtaPlatform class itself.
147+
*/
148+
public Object getJtaPlatformProxy() {
149+
return Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[] {jtaPlatformClass}, this);
73150
}
74151

75152
}

spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/LocalSessionFactoryBuilder.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import org.hibernate.cfg.Configuration;
3434
import org.hibernate.cfg.Environment;
3535
import org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory;
36-
import org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform;
3736

3837
import org.springframework.core.io.Resource;
3938
import org.springframework.core.io.ResourceLoader;
@@ -140,7 +139,7 @@ public LocalSessionFactoryBuilder(DataSource dataSource, ResourceLoader resource
140139
* instructing Hibernate to interact with externally managed transactions.
141140
* <p>A passed-in Spring {@link JtaTransactionManager} needs to contain a JTA
142141
* {@link TransactionManager} reference to be usable here, except for the WebSphere
143-
* case where we'll automatically set {@link WebSphereExtendedJtaPlatform} accordingly.
142+
* case where we'll automatically set {@code WebSphereExtendedJtaPlatform} accordingly.
144143
* <p>Note: If this is set, the Hibernate settings should not contain a JTA platform
145144
* setting to avoid meaningless double configuration.
146145
*/
@@ -149,7 +148,8 @@ public LocalSessionFactoryBuilder setJtaTransactionManager(Object jtaTransaction
149148
if (jtaTransactionManager instanceof JtaTransactionManager) {
150149
boolean webspherePresent = ClassUtils.isPresent("com.ibm.wsspi.uow.UOWManager", getClass().getClassLoader());
151150
if (webspherePresent) {
152-
getProperties().put(AvailableSettings.JTA_PLATFORM, new WebSphereExtendedJtaPlatform());
151+
getProperties().put(AvailableSettings.JTA_PLATFORM,
152+
ConfigurableJtaPlatform.getJtaPlatformBasePackage() + "internal.WebSphereExtendedJtaPlatform");
153153
}
154154
else {
155155
JtaTransactionManager jtaTm = (JtaTransactionManager) jtaTransactionManager;
@@ -158,12 +158,13 @@ public LocalSessionFactoryBuilder setJtaTransactionManager(Object jtaTransaction
158158
"Can only apply JtaTransactionManager which has a TransactionManager reference set");
159159
}
160160
getProperties().put(AvailableSettings.JTA_PLATFORM,
161-
new ConfigurableJtaPlatform(jtaTm.getTransactionManager(), jtaTm.getUserTransaction()));
161+
new ConfigurableJtaPlatform(jtaTm.getTransactionManager(), jtaTm.getUserTransaction(),
162+
jtaTm.getTransactionSynchronizationRegistry()).getJtaPlatformProxy());
162163
}
163164
}
164165
else if (jtaTransactionManager instanceof TransactionManager) {
165166
getProperties().put(AvailableSettings.JTA_PLATFORM,
166-
new ConfigurableJtaPlatform((TransactionManager) jtaTransactionManager, null));
167+
new ConfigurableJtaPlatform((TransactionManager) jtaTransactionManager, null, null).getJtaPlatformProxy());
167168
}
168169
else {
169170
throw new IllegalArgumentException(

spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/SessionFactoryUtils.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.orm.hibernate4;
1818

19+
import java.lang.reflect.Method;
1920
import javax.sql.DataSource;
2021

2122
import org.apache.commons.logging.Log;
@@ -45,7 +46,7 @@
4546
import org.hibernate.exception.JDBCConnectionException;
4647
import org.hibernate.exception.LockAcquisitionException;
4748
import org.hibernate.exception.SQLGrammarException;
48-
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
49+
import org.hibernate.service.spi.Wrapped;
4950

5051
import org.springframework.dao.CannotAcquireLockException;
5152
import org.springframework.dao.DataAccessException;
@@ -57,6 +58,8 @@
5758
import org.springframework.dao.InvalidDataAccessResourceUsageException;
5859
import org.springframework.dao.PessimisticLockingFailureException;
5960
import org.springframework.jdbc.datasource.DataSourceUtils;
61+
import org.springframework.util.ClassUtils;
62+
import org.springframework.util.ReflectionUtils;
6063

6164
/**
6265
* Helper class featuring methods for Hibernate Session handling.
@@ -83,6 +86,12 @@ public abstract class SessionFactoryUtils {
8386

8487
static final Log logger = LogFactory.getLog(SessionFactoryUtils.class);
8588

89+
/**
90+
* Bridging between the different ConnectionProvider package location in 4.0-4.2 vs 4.3.
91+
*/
92+
private static final Method getConnectionProviderMethod =
93+
ClassUtils.getMethodIfAvailable(SessionFactoryImplementor.class, "getConnectionProvider");
94+
8695

8796
/**
8897
* Determine the DataSource of the given SessionFactory.
@@ -91,8 +100,8 @@ public abstract class SessionFactoryUtils {
91100
* @see org.hibernate.engine.spi.SessionFactoryImplementor#getConnectionProvider
92101
*/
93102
public static DataSource getDataSource(SessionFactory sessionFactory) {
94-
if (sessionFactory instanceof SessionFactoryImplementor) {
95-
ConnectionProvider cp = ((SessionFactoryImplementor) sessionFactory).getConnectionProvider();
103+
if (getConnectionProviderMethod != null && sessionFactory instanceof SessionFactoryImplementor) {
104+
Wrapped cp = (Wrapped) ReflectionUtils.invokeMethod(getConnectionProviderMethod, sessionFactory);
96105
return cp.unwrap(DataSource.class);
97106
}
98107
return null;

spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/SpringSessionContext.java

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,16 +16,16 @@
1616

1717
package org.springframework.orm.hibernate4;
1818

19-
import javax.transaction.TransactionManager;
19+
import java.lang.reflect.Method;
2020

2121
import org.hibernate.FlushMode;
2222
import org.hibernate.HibernateException;
2323
import org.hibernate.Session;
2424
import org.hibernate.context.spi.CurrentSessionContext;
2525
import org.hibernate.engine.spi.SessionFactoryImplementor;
26-
import org.hibernate.service.jta.platform.spi.JtaPlatform;
2726

2827
import org.springframework.transaction.support.TransactionSynchronizationManager;
28+
import org.springframework.util.ReflectionUtils;
2929

3030
/**
3131
* Implementation of Hibernate 3.1's CurrentSessionContext interface
@@ -44,7 +44,7 @@ public class SpringSessionContext implements CurrentSessionContext {
4444

4545
private final SessionFactoryImplementor sessionFactory;
4646

47-
private final CurrentSessionContext jtaSessionContext;
47+
private CurrentSessionContext jtaSessionContext;
4848

4949

5050
/**
@@ -53,9 +53,17 @@ public class SpringSessionContext implements CurrentSessionContext {
5353
*/
5454
public SpringSessionContext(SessionFactoryImplementor sessionFactory) {
5555
this.sessionFactory = sessionFactory;
56-
JtaPlatform jtaPlatform = sessionFactory.getServiceRegistry().getService(JtaPlatform.class);
57-
TransactionManager transactionManager = jtaPlatform.retrieveTransactionManager();
58-
this.jtaSessionContext = (transactionManager != null ? new SpringJtaSessionContext(sessionFactory) : null);
56+
try {
57+
Object jtaPlatform = sessionFactory.getServiceRegistry().getService(ConfigurableJtaPlatform.jtaPlatformClass);
58+
Method rtmMethod = ConfigurableJtaPlatform.jtaPlatformClass.getMethod("retrieveTransactionManager");
59+
Object transactionManager = ReflectionUtils.invokeMethod(rtmMethod, jtaPlatform);
60+
if (transactionManager != null) {
61+
this.jtaSessionContext = new SpringJtaSessionContext(sessionFactory);
62+
}
63+
}
64+
catch (Exception ex) {
65+
throw new IllegalStateException("Could not introspect Hibernate JtaPlatform for SpringJtaSessionContext", ex);
66+
}
5967
}
6068

6169

@@ -79,7 +87,7 @@ else if (value instanceof SessionHolder) {
7987
// Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
8088
// with FlushMode.MANUAL, which needs to allow flushing within the transaction.
8189
FlushMode flushMode = session.getFlushMode();
82-
if (FlushMode.isManualFlushMode(flushMode) &&
90+
if (flushMode.equals(FlushMode.MANUAL) &&
8391
!TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
8492
session.setFlushMode(FlushMode.AUTO);
8593
sessionHolder.setPreviousFlushMode(flushMode);

spring-orm-hibernate4/src/test/java/org/springframework/orm/hibernate4/HibernateTransactionManagerTests.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
import java.util.List;
2525
import java.util.Properties;
2626
import javax.sql.DataSource;
27+
import javax.transaction.TransactionManager;
28+
import javax.transaction.TransactionSynchronizationRegistry;
29+
import javax.transaction.UserTransaction;
2730

2831
import org.hibernate.FlushMode;
2932
import org.hibernate.Interceptor;
@@ -32,8 +35,10 @@
3235
import org.hibernate.SessionBuilder;
3336
import org.hibernate.SessionFactory;
3437
import org.hibernate.Transaction;
38+
import org.hibernate.cfg.AvailableSettings;
3539
import org.hibernate.dialect.HSQLDialect;
3640
import org.hibernate.engine.spi.SessionImplementor;
41+
import org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory;
3742
import org.hibernate.exception.ConstraintViolationException;
3843
import org.junit.After;
3944
import org.junit.Test;
@@ -49,6 +54,7 @@
4954
import org.springframework.transaction.TransactionDefinition;
5055
import org.springframework.transaction.TransactionStatus;
5156
import org.springframework.transaction.UnexpectedRollbackException;
57+
import org.springframework.transaction.jta.JtaTransactionManager;
5258
import org.springframework.transaction.support.TransactionCallback;
5359
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
5460
import org.springframework.transaction.support.TransactionSynchronizationManager;
@@ -1180,6 +1186,37 @@ public void doInTransactionWithoutResult(TransactionStatus status) {
11801186
verify(session).close();
11811187
}
11821188

1189+
@Test
1190+
public void testSetJtaTransactionManager() throws Exception {
1191+
DataSource ds = mock(DataSource.class);
1192+
TransactionManager tm = mock(TransactionManager.class);
1193+
UserTransaction ut = mock(UserTransaction.class);
1194+
TransactionSynchronizationRegistry tsr = mock(TransactionSynchronizationRegistry.class);
1195+
JtaTransactionManager jtm = new JtaTransactionManager();
1196+
jtm.setTransactionManager(tm);
1197+
jtm.setUserTransaction(ut);
1198+
jtm.setTransactionSynchronizationRegistry(tsr);
1199+
LocalSessionFactoryBuilder lsfb = new LocalSessionFactoryBuilder(ds);
1200+
lsfb.setJtaTransactionManager(jtm);
1201+
Object jtaPlatform = lsfb.getProperties().get(AvailableSettings.JTA_PLATFORM);
1202+
assertNotNull(jtaPlatform);
1203+
assertSame(tm, jtaPlatform.getClass().getMethod("retrieveTransactionManager").invoke(jtaPlatform));
1204+
assertSame(ut, jtaPlatform.getClass().getMethod("retrieveUserTransaction").invoke(jtaPlatform));
1205+
assertTrue(lsfb.getProperties().get(AvailableSettings.TRANSACTION_STRATEGY) instanceof CMTTransactionFactory);
1206+
}
1207+
1208+
@Test
1209+
public void testSetTransactionManager() throws Exception {
1210+
DataSource ds = mock(DataSource.class);
1211+
TransactionManager tm = mock(TransactionManager.class);
1212+
LocalSessionFactoryBuilder lsfb = new LocalSessionFactoryBuilder(ds);
1213+
lsfb.setJtaTransactionManager(tm);
1214+
Object jtaPlatform = lsfb.getProperties().get(AvailableSettings.JTA_PLATFORM);
1215+
assertNotNull(jtaPlatform);
1216+
assertSame(tm, jtaPlatform.getClass().getMethod("retrieveTransactionManager").invoke(jtaPlatform));
1217+
assertTrue(lsfb.getProperties().get(AvailableSettings.TRANSACTION_STRATEGY) instanceof CMTTransactionFactory);
1218+
}
1219+
11831220

11841221
public interface ImplementingSession extends Session, SessionImplementor {
11851222
}

0 commit comments

Comments
 (0)