Added support for Jakarta EE, Spring Boot 3 and Hibernate 6.
This release was very hard to do. Not because we wanted many new features included (there are not that many, actually), but rather because it was a balancing act between stability / backwards compatibility and new "breaking change" emerging platforms (specifically: Spring Boot 3, Hibernate 6 and their dependencies on JakartaEE and very recent Java versions).
We think we did a good job: just like the previous release 5.0, this new release can run on "good old" Java 8 for most modules, except for Spring Boot 3 integration (which requires Java 17 as per Spring Boot) and Hibernate 6. So if your application worked with release 5.0 then it should still work, provided that you do the following:
In order to do this, we had to break the transitive dependency mechanism (which, incidentally, also avoids pulling in vulnerable 3rd party code libs). So: except for Spring Boot 3 apps, this means that you will now have to add the following dependency to your pom file (or you risk facing ClassNotFoundExceptions):
<dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jta</artifactId> <version>6.0.109</version> <!-- Commercial, for open source use version 6.0.0 --> </dependency> <dependency> <groupId>jakarta.jms</groupId> <artifactId>jakarta.jms-api</artifactId> <!-- NOTE: despite "jakarta" in the name, this version is still a javax jar --> <version>2.0.3</version> </dependency> <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version> </dependency>
<dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jta</artifactId> <version>6.0.109</version> <!-- Commercial, for open source use version 6.0.0 --> <classifier>jakarta</classifier> </dependency> <dependency> <groupId>jakarta.jms</groupId> <artifactId>jakarta.jms-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>jakarta.transaction</groupId> <artifactId>jakarta.transaction-api</artifactId> <version>2.0.1</version> </dependency>
AtomikosConnectionFactoryBean
:
AtomikosConnectionFactoryBean cf = new AtomikosConnectionFactoryBean(); // 1 sets behaviour like in releases 3.9-5.0, 0 sets behaviour like in releases pre-3.9 cf.setSessionCreationMode(1);
For more details, see the javadoc of the AtomikosConnectionFactoryBean class.
We've removed reap functionality from the pool, so remove any reapTimeout property in your datasource or connection factory config.
If you like the nitty gritty details, then the following sections are for you!
We added a starter for Spring Boot 3.
Spring Boot 3 has made a "big bang" upgrade to Jakarta EE and Java 17, with all complications involved. You can now use Atomikos with Spring Boot 3 by means of our new Spring Boot 3 starter module.
Spring Boot 3 requires Java 17 or higher, plus all JakartaEE libraries (and for course Atomikos releases 6.0 or higher). Note that this means you will also need JakartaEE JMS drivers, and JakartaEE persistence providers. Hurray.
You need the following dependency to use Spring Boot 3:
<dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-spring-boot3-starter</artifactId> <version>6.0.109</version> </dependency>
For the JakartaEE JMS and JTA APIs: we've added those to the Spring Boot 3 starter module already - so you don't have to bother.
The choice for this strategy was heavily driven by our desire to still support Java 8 applications that can't upgrade to Java 17 or JakartaEE right now.
The previous statement probably deserves some explanation: after some initial attempts it was painfully clear to us that upgrading from Java 8 to Java 17 is nothing short of a nightmare.
[Oh and by the way, we figure that this is the reason why extended support for Java 8 is available until 2030, the longest of all Java versions at the time of publication. Yes, that means Java 8 should be supported for longer than Java 17 - go figure that!] Our chosen approach should allow existing installations to upgrade to our 6.0 release without the need to upgrade to JakartaEE and/or Java 17 at the same time.Past experience with our customers has shown that even a "simple" upgrade from Java 6 to Java 8 can be a huge roadblock - especially if the development team is gone and only operations teams are left. So we prefer to keep upgrading a seamless and straightforward process, without requiring needless Java updates at the same time. We are sure that you will appreciate that.
The "downside" of this choice is that you can no longer count on transitive dependencies as much as you did, because many modules now have 2 variants available (and hence the transitive dependencies would not necessarily match the right variant). While this may seem a disadvantage, it actually increases security because there are no longer transitive dependencies on 3rd party jars either (so you avoid pulling in vulnerabilities).
So: you have to add some dependencies manually that were transitive before. But the effort required is significantly lower than the effort it would take to upgrade your code base towards Java 17 and JakartaEE all at once.
As explained in the upgrade section above: you need to declare some extra dependencies. Also, you have to be careful to use the right combination of Jakarta-enabled jars in your application. To this end, we have defined BOM files that list all the jars designed to work together:
You can check these BOMs for inspiration on which jars to use / combine in your application. If you are curious where to find them: check groupId=com.atomikos and artifactId=name_of_BOM_file.
You can now easily use Hibernate 6 in combination with our product!
Hibernate 6 is also in the JakartaEE camp, so of course you need JakartaEE support to be able to use it. Well, as we've explained above: we have done just that.
To use Hibernate 6, it suffices to use transactions-hibernate4 in the jakarta flavour. For a working sample: check the supplied examples project called examples-hibernate6 or see the summary POM snippet below.No real changes are needed, but you do have to add the correct POM dependencies:
<dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-hibernate4</artifactId> <version>6.0.109</version> <classifier>jakarta</classifier> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jdbc</artifactId> <version>6.0.109</version> <classifier>jakarta</classifier> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jta</artifactId> <version>6.0.109</version> <classifier>jakarta</classifier> </dependency> <dependency> <groupId>jakarta.transaction</groupId> <artifactId>jakarta.transaction-api</artifactId> <version>2.0.1</version> </dependency> <dependency> <groupId>jakarta.persistence</groupId> <artifactId>jakarta.persistence-api</artifactId> <version>3.1.0</version> </dependency>
Of course, you also need the Hibernate 6 dependencies themselves - but we'll leave that up to you…
We now have (minimal) support for JMS 2.0.
JMSContext
API. Spring Boot 3 does not seem to need it, so that should be fine for now. Attempting to call any of the createContext
methods on the AtomikosConnectionFactoryBean
will throw UnsupportedOperationException
.
Session creation behaviour has changed due to the clarification of session creation behaviour in JMS 2.0 (which is incompatible with what we had in prior releases). As you can see in the table below, the differences are in the combined interpretation of:
localTransactionMode
and
Prior to JMS 2.0, session creation had some fuzzy border cases where the interpretation was left to the party implementing the specs (like Atomikos). In JMS 2.0 this has been addressed. Needless to say, the probably of a perfect match with our interpretation was next to zero - so we had to make some changes.
To preserve compatibility your instances ofAtomikosConnectionFactoryBean
now offer an extra method setSessionCreationMode(int mode)
which changes behaviour as described below. The default is value 2 (JMS 2.0) so for compatibility with 5.0, try value 1. For compatibility with pre-3.9 installations, try value 0.
existing JTA transaction for thread | localTransactionMode | resulting session |
---|---|---|
true | ignored | XA session |
false | false | XA session |
false | true | non-XA session according to sessionTransacted/acknowledgeMode parameters |
localTransactionMode | sessionTransactedFlag | resulting session |
---|---|---|
false | ignored | XA session |
true | ignored | non-XA session according to sessionTransacted/acknowledgeMode parameters |
localTransactionMode | sessionTransactedFlag | resulting session |
---|---|---|
false | true | XA session |
other | other | non-XA session according to sessionTransacted/acknowledgeMode parameters |
AtomikosConnectionFactoryBean
.
TransactionSynchronizationRegistry
defined in the more recent JTA specifications.
com.atomikos.icatch.jta.TransactionSynchronizationRegistryImp
contained in module transactions-jta.jar.
Unless you are a vendor of persistence providers, you probably won't ever need this class so we'll keep the explanation to a minimum - since vendors of persistence providers already know all there is to know.
An extra class available in our distribution, for you to use if you want to.
com.atomikos.icatch.jta.template.TransactionTemplate
are now tread-safe.
The first implementations of our template were not intended for threaded use cases. Based on customer feedback, we have changed that - so you can now reuse the same instance in different threads - just like Spring's template.
Instead of having to create multiple template instances, you can now reuse the same instance in different places of your code base. This should simplify your configuration and code base.
We've improved the pool logic so that borrow requests can better respect the timeout.
borrowConnectionTimeout
setting would (and should) allow. That's because there is no easy way to interrupt a blocked IO request.
We now improved this as follows:
borrowConnectionTimeout
.
No real changes are needed, except that it should work better.
We have simplified and improved the shutdown logic.
com.atomikos.icatch.default_max_wait_time_on_shutdown
. Recovery itself is already bound by a different parameter: com.atomikos.icatch.default_max_timeout
. The previous logic used to mix those two and the results were not always clear.
We have simplified this to the following:
com.atomikos.icatch.default_max_wait_time_on_shutdown
in your jta.properties file. Beware that shutdown duration is bound by the value of com.atomikos.icatch.default_max_timeout
- unless you use the "forceShutdown" option.
com.atomikos.jms.extra.MessageDrivenContainer
because they were never used anyway.
com.atomikos.jms.AtomikosConnectionFactoryBean
to create connections. The latter is also configured with a user and password, and those are the values that are actually being used.
As a result, keeping two unused properties in the com.atomikos.jms.extra.MessageDrivenContainer
class was confusing - so we have removed those.
You need to remove any references to these properties from your configuration.
We've removed the reaping functionality from the pools.
Reaping was when our pools would take away connections that were being held onto for too long.
Historically, reaping functionality has caused more problems / confusion than it solved. Moreover, it was intended to fix application-level connection leaks, meaning bugs in the application (as opposed to bugs in our code base). We felt that became a source of needless complexity.
We believe that the correct way of fixing application-level connection leaks is by fixing the application, rather than abruptly reaping connections away from that application. So our 6.0 release seemed a good time to remove that feature.
reapTimeout
from your configuration.
Severity: | 4 |
---|---|
Affected version(s): | 5.0.x |
You can now count on the property values from jta.properties to be taken into account even with Spring Boot.
The Atomikos JTA properties implementation in our Spring Boot starter would define default values for many properties, meaning that their value specified jta.properties would not be taken into account. This has now been fixed.
Your properties specified in jta.properties should now work.
Severity: | 1/2/3/4 |
---|---|
Affected version(s): | 5.0.x |
AtomikosRestPort
URL is not set (for transactions across remoting calls).
AtomikosRestPort
URL was not set, the client template would report a misleading message saying that there is no transaction for the thread. Instead, the root cause is a URL that is missing - so we fixed that for you.
None.
Severity: | 4 |
---|---|
Affected version(s): | 5.0.x |
You can now use transitive readOnly remoting transactions in all cases.
This is known as a "diamond case" because the invocation diagram looks like a diamond.
This issue has been fixed in the following way: our product will now avoid the readOnly optimisation in this specific scenario. This is still correct, at a minor performance overhead in the exotic cases where this does happen.
None.
Severity: | 4 |
---|---|
Affected version(s): | 5.0.x |
Now you can close a JDBC connection and also have the Callable- and PreparedStatements closed automatically.
When a JDBC connection is closed by the application (returned to the pool) then the JDBC specification requires all pending statements to be closed as well.
We supported this, but apparently this was not done for all statement types. We fixed this now.
This issue is marked as severity 4 (development use) because we had no real bug reports from any customers. It was something we noticed during a code review.
None.
Severity: | 2 |
---|---|
Affected version(s): | 5.0.x |
You can now use @RequiresNew with imported transactions.
Our code had a side effect of suspend/resume that changed the local sibling count.
Sibling counts help detect orphaned invocations (and their transactional updates to persistent storage) that arise out of lost replies. For instance, consider this scenario with services A and B:
This risk here is the different views of A and B regarding the scope of the transaction: A thinks it commits one update to B, whereas B commits two different updates. This can be a problem for data consistency, so we avoid this by keeping sibling counts at B and A. A constructs its sibling count picture with each result it actually receives with its replies from A. Before commit, A passes on the "count" it has for invocations at B, and if B finds that there is no match then it refuses to commit.
This would avoid the problem outlined above, because in step 4 service A will miss a count, so in step 6 service A will pass a count of 1 for service B, whereas B will see 2 and refuses the commit process.
In short, sibling counts have their purpose. However due to a bug, this was affected by a suspend/resume at service B (when it has @RequiresNew logic inside).
You should now be able to configure @RequiresNew on any service that needs it.
Severity: | 4 |
---|---|
Affected version(s): | 5.0.x |
Any spring.jta properties in Spring Boot should now be taken into account.
This has proven to confuse users, so we now take such spring.jta properties into account.
Any spring.jta properties in Spring Boot should now be taken into account.
Severity: | 3 |
---|---|
Affected version(s): | 3.9.x, 4.0.x, 5.0.x |
We've refined how we deal with lost XA connections during the commit phase.
Some connection issues would lead to needless heuristic exceptions during commit, in particular when the commit was 1-phase (where failure to reach the backend results in resource-internal rollback anyway).We have now refined this so there are no needless heuristic exceptions any more in these cases. At the same time, we have also tried to minimise the number of cases where such connection issues remain.
None.
Severity: | 4 |
---|---|
Affected version(s): | 5.0.x |
You can now use our pools with TRACE logging and get less exceptions.
None.
Severity: | 4 |
---|---|
Affected version(s): | 6.0.x |
Unlike Spring Boot 3 support, we did not yet add the JMS and JTA dependencies in the Spring Boot 2 starter.
We still have to add the JMS and JTA dependencies in the Spring Boot 2 starter. This is needed because of the removal of transitive dependencies. Until then, you will have to add them yourself.
Free DownloadSeverity: | 2 |
---|---|
Affected version(s): | 5.0.x |
We've ported a fix from our commercial release that prevents the pool from exceeding its maxPoolSize.
The concurrent pool does not enforce synchronization so when it:
… a different thread may have done the same and the pool grows too much.
Solution: we've made growPool (with existing synchronized block) check maxSize again, and don't grow if already at the maximum.
None.
This is our review of an excellent article published at InfoQ by some of our colleagues at Red Hat. See the references section at the end for the original publication, but this overview aims to give you all of the insights and more in just a few lines of text. Where applicable the content has been complemented with lessons learned from our own experience…
The bad guys are trying to get into your projects. What can you do to avoid pulling in bad code?
We now offer remoting support for gRPC so your transactions can span gRPC calls.
Sometimes you may want commit or rollback to extend across one more more outgoing gRPC calls. This is now possible.
See the example module: examples-jta-grpc in the download for a working sample.New interceptors for your gRPC architecture are included.
Generously donated by Pivotal's Spring Boot team, we were able to add the Spring Boot starter module to our code base.
Instead of adding Spring's starter module, you should now add "our" starter module to your Spring Boot project's pom.
In particular, instead of specifying:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jta-atomikos</artifactId> </dependency>
You should now specify:
<dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-spring-boot-starter</artifactId> <version>5.0.9</version> <!-- or any later atomikos release that contains our starter module --> </dependency>Additional details are here...
No real API changes, only pom dependency changes.
You can now explicitly trigger recovery in your application, via our API.
import com.atomikos.icatch.RecoveryService; import com.atomikos.icatch.config.Configuration; boolean lax = true; //false to force recovery, true to allow intelligent mode RecoveryService rs = Configuration.getRecoveryService(); rs.performRecovery(lax);
In order for this to work, make sure to set (in jta.properties):
# set to Long.MAX_VALUE so background recovery is disabled com.atomikos.icatch.recovery_delay=9223372036854775807L
We have added methods on an existing API interface, which does not break existing clients.
Severity: | 2 |
---|---|
Affected version(s): | 5.0.x |
Timed out transactions (in particular heuristic hazard cases after connection issues) used to stay around in the JVM and kept on generating timeout events. This has now been fixed.
Abandoned transaction instances now also stop any pending threads that generate timeout warnings. This prevents timed out transactions from generating endless warnings that are no longer relevant.
None.
Severity: | 2 |
---|---|
Affected version(s): | 5.0.x |
We optimised connection pool efficiency for non-JTA/XA aware use cases, so connections are reused more efficiently when waiting for busy connections.
Previously, waiting connection requests were not notified immediately when a busy connection was being closed (i.e., marked for reuse) by the application. This has now been fixed.
None.
Severity: | 2 |
---|---|
Affected version(s): | 5.0 |
In some rare cases the XAResource used for committing a transaction after prepare may become null (presumably due to connection errors, unconfirmed though). Instead of logging an error (like we used to), we now trust background-level recovery to handle this - for which it was designed in the first place. This should avoid repeated the errors logged in such a case.
This "bug" would lead to millisecond-level repeated errors similar to the following:
19/11/2020 15:20:43.467 [Atomikos:3321] ERROR com.atomikos.icatch.imp.CommitMessage - Unexpected error in commit com.atomikos.icatch.HeurHazardException: XAResourceTransaction: 31302E3235342E3134362E3131302E746D313539353531353037383735393139373038:31302E3235342E3134362E3131302E746D373030333533: no XAResource to commit? at com.atomikos.datasource.xa.XAResourceTransaction.commit(XAResourceTransaction.java:529) at com.atomikos.icatch.imp.CommitMessage.send(CommitMessage.java:52) at com.atomikos.icatch.imp.CommitMessage.send(CommitMessage.java:23) at com.atomikos.icatch.imp.PropagationMessage.submit(PropagationMessage.java:67) at com.atomikos.icatch.imp.Propagator$PropagatorThread.run(Propagator.java:63) at com.atomikos.icatch.imp.Propagator.submitPropagationMessage(Propagator.java:42) at com.atomikos.icatch.imp.HeurHazardStateHandler.onTimeout(HeurHazardStateHandler.java:71) at com.atomikos.icatch.imp.CoordinatorImp.alarm(CoordinatorImp.java:650) at com.atomikos.timing.PooledAlarmTimer.notifyListeners(PooledAlarmTimer.java:95) at com.atomikos.timing.PooledAlarmTimer.run(PooledAlarmTimer.java:82) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) 19/11/2020 15:20:53.468 [Atomikos:3321] ERROR com.atomikos.datasource.xa.XAResourceTransaction - XAResourceTransaction: 31302E3235342E3134362E3131302E746D313539353531353037383735393139373038:31302E3235342E3134362E3131302E746D373030333533: no XAResource to commit?
Now we no longer log these as errors, since we designed recovery to take care of exactly those kinds of exceptions.
None.
Severity: | 4 |
---|---|
Affected version(s): | 5.0.x |
Fixed a bug that would happen in certain class loading environments and prevented CallableStatements from being created.
Fixed a bug that would happen in certain class loading environments and prevented CallableStatements from being created. This would lead to errors like this:
java.lang.ClassCastException: com.sun.proxy.$Proxy364 cannot be cast to java.sql.CallableStatement at com.atomikos.jdbc.internal.AbstractJdbcConnectionProxy.prepareCall(AbstractJdbcConnectionProxy.java:73) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.atomikos.util.DynamicProxySupport.callProxiedMethod(DynamicProxySupport.java:162) at com.atomikos.util.DynamicProxySupport.invoke(DynamicProxySupport.java:116) at com.sun.proxy.$Proxy64.prepareCall(Unknown Source)
None.
Severity: | 2 |
---|---|
Affected version(s): | 5.0.x |
Remoting participants now also respect commit ordering so you can avoid issues with JMS notifications going out before the backend databases are up to date.
Consider the following scenario for your microservice:
1. receive a JMS message 2. do a remote call to a different microservice that in turn updates a DB 3. send out a JMS "ticket" message
Previously, the JMS message (3) would be committed first, when DB of the remote call in 2 still needed to commit. Recipients of (3) would sometimes not see the updates in the DB when they receive the message.
This has now been fixed, by incorporating the remote participant of step 2 in the commit order.
None.
Severity: | 4 |
---|---|
Affected version(s): | 5.0.x |
We now check for null when adding class loaders to create dynamic proxies.
At least one user has reported null issues when creating a dynamic proxy, like this:
java.lang.NullPointerException: null at java.util.ArrayDeque.addLast(ArrayDeque.java:249) ~[?:1.8.0_251] at java.util.ArrayDeque.add(ArrayDeque.java:423) ~[?:1.8.0_251] at com.atomikos.util.DynamicProxySupport.getClassLoadersToTry(DynamicProxySupport.java:194) ~[?:?] at com.atomikos.util.DynamicProxySupport.createDynamicProxy(DynamicProxySupport.java:189) ~[?:?] at com.atomikos.jdbc.internal.AtomikosXAPooledConnection.doCreateConnectionProxy(AtomikosXAPooledConnection.java:119) ~[?:?] at com.atomikos.jdbc.internal.AtomikosXAPooledConnection.doCreateConnectionProxy(AtomikosXAPooledConnection.java:31) ~[?:? at com.atomikos.datasource.pool.AbstractXPooledConnection.createConnectionProxy(AbstractXPooledConnection.java:86) ~[?:?] at com.atomikos.datasource.pool.ConnectionPoolWithConcurrentValidation.concurrentlyTryToUse(ConnectionPoolWithConcurrentValidation.java:61) ~[?:?] at com.atomikos.datasource.pool.ConnectionPoolWithConcurrentValidation.retrieveFirstAvailableConnection(ConnectionPoolWithConcurrentValidation.java:43) ~[?:?] at com.atomikos.datasource.pool.ConnectionPool.retrieveFirstAvailableConnectionAndGrowPoolIfNecessary(ConnectionPool.java:140) ~[?:?] at com.atomikos.datasource.pool.ConnectionPool.findOrWaitForAnAvailableConnection(ConnectionPool.java:128) ~[?:?] at com.atomikos.datasource.pool.ConnectionPool.borrowConnection(ConnectionPool.java:119) ~[?:?] at com.atomikos.jdbc.internal.AbstractDataSourceBean.getConnection(AbstractDataSourceBean.java:371) ~[?:?]
We now fixed this by simply checking if a class loader is not null before attempting to use it.
None.
2
5.0.x
We used to pass all remote participants as direct participants, meaning that two-phase commit would sometimes be repeated and fail. This would particularly be the case in "diamond" calls - or also in deeper call hierarchies of more than one level down.
Consider a remoting client service A calling another remoting service B.
For remoting we distinguish between direct participants (i.e., endpoints at B to be included for two-phase commit at A) and indirect participants (i.e., URIs added as metadata only for checking orphaned calls). The class DefaultImportingTransactionManager in module transactions-remoting used to pass all its remote participants as direct participants, i.e. participants to be included in the remoting's two-phase commit set. In particular for diamond-shaped calls (A calls B and C, and both B and C in turn call D) this would give repeated two-phase commit calls to D, because A would incorrectly receive D as a direct participant via the call hierarchies of both B and C.We fixed this, and for this specific case A would now only receive 2 direct participants: B and C.
This fix also resolves an issue with a simpler call stack: A → B → C, where C would also be called for two-phase commit by both A and B. The is no longer the case.
None.
3
4.0.x, 5.0.x
Server-side (Tomcat) integration created threads for individual transactions (at web app level) with the class loader of the web application. This would cause (Tomcat) warnings when the web application is stopped because those threads will hang around in the thread pool.
Solution: we now start new threads with the server-level class loader.
If you have the transaction core at the server level (in the server's classpath) then individual transactions started in the web application will use the server-level thread pool of Atomikos (named com.atomikos.thread.TaskManager).
This would typically give warnings (in Tomcat) like this when the web application is being shut down:
WARNING [http-nio-3030-exec-6] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [mywebapp] appears to have started a thread named [Atomikos:2] but has failed to stop it. This is very likely to create a memory leak.
That's because the server-side thread pool threads used to be created with the web application's class loader.
This also happened for our built-in Tomcat integration (since it is configured at the server-level).
We now create those threads with the thread pool's classloader instead. When configured at the server level, this will no longer be the application's class loader and that seems to prevent this problem.
None.
2
4.0.x, 5.0.x
You now no longer get "Log corrupted - restart JVM" exceptions after you interrupt a thread that is writing to the transaction log file, or after any other exception that make a log checkpoint fail.
com.atomikos.recovery.fs.CachedRepository
class, leaving the instance in an invalid state:
2021-03-01 16:15:56.662 ERROR 41669 --- [pool-1-thread-1] c.a.recovery.fs.FileSystemRepository : Failed to write checkpoint java.nio.channels.ClosedByInterruptException: null at java.nio.channels.spi.AbstractInterruptibleChannel.end(AbstractInterruptibleChannel.java:202) ~[na:1.8.0_192] at sun.nio.ch.FileChannelImpl.force(FileChannelImpl.java:392) ~[na:1.8.0_192] at com.atomikos.recovery.fs.FileSystemRepository.writeCheckpoint(FileSystemRepository.java:196) ~[transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.recovery.fs.CachedRepository.performCheckpoint(CachedRepository.java:84) [transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.recovery.fs.CachedRepository.put(CachedRepository.java:77) [transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.recovery.fs.OltpLogImp.write(OltpLogImp.java:46) [transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.persistence.imp.StateRecoveryManagerImp.preEnter(StateRecoveryManagerImp.java:51) [transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.finitestates.FSMImp.notifyListeners(FSMImp.java:164) [transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.finitestates.FSMImp.setState(FSMImp.java:251) [transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.icatch.imp.CoordinatorImp.setState(CoordinatorImp.java:284) [transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.icatch.imp.CoordinatorStateHandler.commitFromWithinCallback(CoordinatorStateHandler.java:346) [transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.icatch.imp.ActiveStateHandler$6.doCommit(ActiveStateHandler.java:273) [transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.icatch.imp.CoordinatorStateHandler.commitWithAfterCompletionNotification(CoordinatorStateHandler.java:587) [transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.icatch.imp.ActiveStateHandler.commit(ActiveStateHandler.java:268) [transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.icatch.imp.CoordinatorImp.commit(CoordinatorImp.java:550) [transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.icatch.imp.CoordinatorImp.terminate(CoordinatorImp.java:682) [transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.icatch.imp.CompositeTransactionImp.commit(CompositeTransactionImp.java:279) [transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.icatch.jta.TransactionImp.commit(TransactionImp.java:168) [transactions-jta-5.0.9-SNAPSHOT.jar:na] at com.atomikos.icatch.jta.TransactionManagerImp.commit(TransactionManagerImp.java:428) [transactions-jta-5.0.9-SNAPSHOT.jar:na] at com.atomikos.icatch.jta.UserTransactionManager.commit(UserTransactionManager.java:160) [transactions-jta-5.0.9-SNAPSHOT.jar:na] at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1035) [spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:743) [spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711) [spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:152) [spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE] at com.example.atomikos.AtomikosApplicationTests.lambda$4(AtomikosApplicationTests.java:78) [test-classes/:na] at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_192] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_192] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_192] at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_192]
Later requests trying to read from the transaction logs would get systematic corruption errors like this:
com.atomikos.recovery.LogReadException: Log corrupted - restart JVM at com.atomikos.recovery.fs.CachedRepository.assertNotCorrupted(CachedRepository.java:137) ~[transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.recovery.fs.CachedRepository.findAllCommittingCoordinatorLogEntries(CachedRepository.java:145) ~[transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.recovery.fs.RecoveryLogImp.getExpiredPendingCommittingTransactionRecordsAt(RecoveryLogImp.java:52) ~[transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.icatch.imp.RecoveryDomainService.performRecovery(RecoveryDomainService.java:76) ~[transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.icatch.imp.RecoveryDomainService$1.alarm(RecoveryDomainService.java:55) [transactions-5.0.9-SNAPSHOT.jar:na] at com.atomikos.timing.PooledAlarmTimer.notifyListeners(PooledAlarmTimer.java:101) [atomikos-util-5.0.9-SNAPSHOT.jar:na] at com.atomikos.timing.PooledAlarmTimer.run(PooledAlarmTimer.java:88) [atomikos-util-5.0.9-SNAPSHOT.jar:na] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_192] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_192] at java.lang.Thread.run(Thread.java:748) [na:1.8.0_192]
This has now been fixed.
None.