Does anyone have a good tutorial or some advice on how to implement one's own XAResource? I need Spring's MailSender to be transactional, so that the mail will only be sent once the transaction commits, and it seems there isn't any existing transactional wrapper.
If you just need to wait for the commit, as you say in a comment, you can investigate using TransactionSynchronizationManager.registerSynchronization() to trigger email sending on commit.
You can use a TransactionSynchronizationManager.registerSynchronization (like gpeche mentioned) with a TransactionSynchronizationAdapter which has a variety of methods that are called at various stages of the current transaction. I think the most suitable method for the question is the afterCommit.
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
#Override
public void afterCommit() {
super.afterCommit();
sendEmail();
}
});
I doubt that it's possible to implement true XAResource for SMTP. There should be transaction support on the resource manager (SMTP server in this case) and I don't believe there are any. I would say your best bet is 'Last resource commit' pattern - which allows one non XA resource participate in XA transaction. Search Google, there are plenty of info. Most Java EE servers supports this.
One other option next to the one mentioned by gpeche, is sending a transactional JMS message from within the transaction. Then let the message listener (like e.g. a MDB bean) send the email.
Another trick in EJB is scheduling a timer from within a transaction. The timer is also transactional and will only be started when the transaction commits. Simply use a timer with timeout = 0, so it will start immediately after the transaction commits.
Related
I'm trying to create a commitListener using the Java SDK to listen for commit events after submitting a transaction, although the listener is not responding.
I'm using the fabcar example.
// create a gateway connection
try (Gateway gateway = builder.connect()) {
// get the network and contract
Network network = gateway.getNetwork("mychannel");
Contract contract = network.getContract("fabcar");
FabcarCommitListener listener = new FabcarCommitListener();
network.addCommitListener(listener, network.getChannel().getPeers(), "createCar");
}
The FabcarCommitListener:
public class FabcarCommitListener implements CommitListener {
#Override
public void acceptCommit(BlockEvent.TransactionEvent transactionEvent) {
System.out.println("TX COMMITTED");
}
#Override
public void acceptDisconnect(PeerDisconnectEvent peerDisconnectEvent) {
System.out.println("peerDisconnected");
}
}
Any ideas how a commitListener works using the Java SDK?
A commit listener receives events only for a specific transaction invocation, not for all invocations of a given transaction name. Every transaction invocation has its own unique transaction ID, which you can obtain from the Transaction object prior to submitting:
https://hyperledger.github.io/fabric-gateway-java/release-2.2/org/hyperledger/fabric/gateway/Transaction.html#getTransactionId--
By default, a transaction submit will also listen for the transaction to be committed by peers so there is no need for your code to listen for transaction commits. There are several built-in strategies for determining when a transaction has been successfully committed, which you can select either:
When connecting the Gateway: https://hyperledger.github.io/fabric-gateway-java/release-2.2/org/hyperledger/fabric/gateway/Gateway.Builder.html#commitHandler-org.hyperledger.fabric.gateway.spi.CommitHandlerFactory-
For a specific transaction invocation: https://hyperledger.github.io/fabric-gateway-java/release-2.2/org/hyperledger/fabric/gateway/Transaction.html#setCommitHandler-org.hyperledger.fabric.gateway.spi.CommitHandlerFactory-
If you want to implement your own custom logic for identifying whether a transaction has committed successfully, you can write your own custom commit handler implementation, and this implementation can use a commit listener to identify the commit and connection status of all the peers you care about. Here is a sample commit handler and factory implementation that make use of commit listeners:
https://github.com/hyperledger/fabric-gateway-java/blob/release-2.2/src/test/java/org/hyperledger/fabric/gateway/sample/SampleCommitHandlerFactory.java
https://github.com/hyperledger/fabric-gateway-java/blob/release-2.2/src/test/java/org/hyperledger/fabric/gateway/sample/SampleCommitHandler.java
If you want to look at all the transactions committed to the blockchain, even if only to pick out certain ones you care about, then use a block listener:
https://hyperledger.github.io/fabric-gateway-java/release-2.2/org/hyperledger/fabric/gateway/Network.html#addBlockListener-java.util.function.Consumer-
From the block event you can navigate down through all the transactions included in the block.
Having said all this, both block listeners and commit listeners really deal with the mechanics of Fabric blockchains. So inspecting the transactions that have operated on the ledger and checking whether they were successfully committed. If you want to orchestrate business processes around transactional events then probably you should actually be using a contract event listener.
If you want to trigger some business process when a new car is created, implement your createCar transaction function so that it emits an event when it is committed:
https://hyperledger.github.io/fabric-chaincode-java/release-2.2/api/org/hyperledger/fabric/shim/ChaincodeStub.html#setEvent-java.lang.String-byte:A-
In your client application, simply listen for this event using a contract event listener:
https://hyperledger.github.io/fabric-gateway-java/release-2.2/org/hyperledger/fabric/gateway/Contract.html#addContractListener-java.util.function.Consumer-java.lang.String-
You can use checkpointing to allow your client to resume listening for events at the last processed block position after a client application restart:
https://hyperledger.github.io/fabric-gateway-java/release-2.2/org/hyperledger/fabric/gateway/Contract.html#addContractListener-org.hyperledger.fabric.gateway.spi.Checkpointer-java.util.function.Consumer-java.lang.String-
in my project I want to have multiple clients connecting to a service. I am using the java Rsocket implementation.
The service should maintain a state for each client. Now at this point I either can manage the clients by some identifier. This option I have already implemented. But I do not want to manage the session manually using strings.
So another idea is to identify the clients by the Rsocket connection. Is there a way to use Rsocket channel for identification of a specific client?
Imagine an example service and a couple of clients. Each client has the Rsocket channel with the service up and running. Is there a way to identify these clients on the server side using the Rsocket channel? Would be amazing if you could show a programmatic example of such behavior.
Thank you!
EDIT (describing the case more detailed)
Here is my example.
We currently have three CORBA objects that are used as demonstrated in the diagram:
LoginObject (to which a reference is retrieved via NamingService). Clients can call a login() method to obtain a session
The Session object has various methods for query details about the current serivce context and most importatly to obtain a Transaction object
The Transaction object can be used to execute various commands via a generic method that take a commandName and a list of key-value pairs as parameters.
After the client executed n commands he can commit or rollback the transaction (also via methods on the Transaction object).
so here we use the session object to execute transactions on our service.
Now we decided to move away from CORBA to Rsocket. Thus we need Rsocket microservice to be able to store the session's state, otherwise we can't know what's going to be commited or rolled back. Can this be done with just individual Publisher for each client?
Here's an example I made the other day that will create a stateful RSocket using Netifi's broker:
https://github.com/netifi/netifi-stateful-socket
Unfortunately you'd need to build our develop branch locally to try it out (https://github.com/netifi/netifi-java) - there should be a release with the code by the end of the week if you don't want to build it locally.
I'm working on a pure RSocket example too, but if you want to see how it would take a look at the StatefulSocket found in the example. It should give you a clue how to deal with the session with pure RSocket.
Regarding your other questions about a transaction manager - you would need to tie your transaction to the Reactive Streams signals that are being emitted - if you received an cancel, an onError you'd roll back, and if received a onComplete you would commit the transaction. There are side effect methods from Flux/Mono that should make this easy to deal with. Depending on what you are doing you could also use the BaseSubscriber as it has hooks to deal with the different Reactive Streams signals.
Thanks,
Robert
An example of resuming connections i.e. maintaining the state on the server, has landed in the rsocket-java repo
https://github.com/rsocket/rsocket-java/commit/d47629147dd1a4d41c7c8d5af3d80838e01d3ba5
The resumes a whole connection, including whatever state is associated with each individual channel etc.
There is an rsocket-cli project that lets you try this out. Start and stop the socat process and observe the client and server progress.
$ socat -d TCP-LISTEN:5001,fork,reuseaddr TCP:localhost:5000
$ ./rsocket-cli --debug --resume --server -i cli:time tcp://localhost:5000
$ ./rsocket-cli -i client --stream --resume tcp://localhost:5001
From your description it looks like channel will work best, I haven't used channel before so I can't really guarantee (sorry). But what I'd recommend you to try something like this:
A transcation contoller:
public class TransactionController implements Publisher<Payload> {
List<Transaction> transcations = new ArrayList<>();
#Override
public void subscribe(Subscriber<? super Payload> subscriber) {
}
public void processPayload(Payload payload) {
// handle transcations...
}
}
And in your RSocket implementation override the requestChannel:
#Override
public Flux<Payload> requestChannel(Publisher<Payload> payloads) {
// Create new controller for each channel
TranscationController cntrl = new TranscationController();
Flux.from(payloads)
.subscribe(cntrl::processPayload);
return Flux.from(cntrl);
}
I struggeling with JTA, two-phase-commit, JMS- and JDBC-transactions. The idea is (in short) to
receive a message on a queue
perform some database operations
acknowledge the message, when the db operations have been successful
So I got the XAQueueConnectionFactory, create the XAQueueSession, create a receiver from the session and set a message listener.
Inside the listener, in the onMessage method, I begin my user transaction, do the jdbc stuff and commit the transaction or do a rollback, if something went wrong. Now I expected (aka "hoped") that the message would be acknowledged, when the user transaction commits.
But that doesn't happen, the messages are still on the queue and get redelivered again and again.
What am I missing? I double-checked the session and the acknowledge mode really is "SESSION_TRANSACTED" and getTransacted returns true.
I don't have a Java EE container, no spring, no message driven beans. I use the standalone JTA bitronix.
You don't really need XA for this. Just following your algorithm: receive the message, perform the DB operations, then acknowledge the message... Literally, that's the solution. (And instead a transacted session, you probably would just choose explicit CLIENT_ACKNOWLEDGE.) If your application should fail while performing the DB operations, don't ack the JMS msg and it will be redelivered. If your app fails after the DB txn and before the ack, then the message will be redelivered -- but you can detected this (redelivered flag will be set to true on the message), and you can decide to reprocess the message or not, based on the state of the database.
When you say that inside the listener, you begin your user transaction, this seems to hint that you are using Bean Managed Transaction (BMT). Is there a good reason for doing so?
If you used Container Managed Transaction (CMT), what you want would come for free.
As far as I remember, it is not possible with BMT, since the UserTransaction will not participate and will not be able to participate in the transaction created for the message. But you might want to double check with the Java EE spec.
Edit:
Sorry, I realized too late that you are not using a Java EE container.
Are you sure that the user transaction that you start inside the listener is part of the transaction started for the message? It seems that you start an independent transaction for the db work.
If you use no container, who provides the JMS implementation, i.e. XAQueueConnectionFactory etc?
I think with XA you shoulndn't use transacted session.
I have a Java SE(!) scenario with JMS and JPA where I might need distributed transactions as well as "regular" JDBC transactions. I have to listen to a queue which sends service requests, persist log on receiving, process the request and update the log after the request has been processed. The message shall only be acknowledged if the request has been processed successfully.
The first idea was to only use JTA (provided by Bitronix). But there I face two problems:
no log will be persisted if the request can't be processed
the request won't be processed if the log can't be updated (unlikely but yet possible)
So the other idea is to create and update the log with regular JDBC transactions. Only the entitymanager(s) for the request transaction(s) would join the user transactions and the entity managers for creating and updating the log would commit directly.
Is it possible to "mix" JTA and JPA on a single persistence unit? Or do we already have patterns for those kinds of JMS and JDBC transactions?
I actually solved my problem with a slightly different approach. Instead of "mixing" JTA and JDBC transactions I used suspend and resume to work with different user transaction.
The task is still the same: I start a (JTA) user transaction that contains some JMS and JDBC transactions (receiving a message, performing some database operations). And in the middle of that workflow, I want to write a message log but that logging shall not be rolled back when the "outer" transaction fails.
So the solution is, in pseudo code:
transactionManager.begin()
doSomeJdbcStuff();
Transaction main = transactionManager.suspend();
// do the logging
transactionManager.begin() // <- now a new transaction is created and active!
doSomeLogging();
transactionManager.commit()
// continue
transactionManager.resume(main);
doSomeMoreJdbcStuff();
transactionManager.commit();
I'm entering the world of JTA, due to need of distributed transactions, and I'm uncertain about the differences between javax.jms.ConnectionFactory and javax.jms.XAConnectionFactory or more accurately how can it be that javax.jms.ConnectionFactory performed what I expected only javax.jms.XAConnectionFactory can do for me.
The details: I'm using Atomikos essentials as my transaction manager and my app is running on Apache Tomcat 6.
I'm running a small POC with a dummy app where I have my JMS provider (OpenMQ) registered as a JNDI resource.
<Resource name="jms/myConnectionFactory" auth="Container"
type="com.atomikos.jms.AtomikosConnectionFactoryBean"
factory="com.atomikos.tomcat.EnhancedTomcatAtomikosBeanFactory"
uniqueResourceName="jms/myConnectionFactory"
xaConnectionFactoryClassName="com.sun.messaging.XAConnectionFactory"
maxPoolSize="3"/>
And the strange issue is that in my code I do this:
Context ctx = new InitialContext();
ConnectionFactory queueConnectionFactory =
(ConnectionFactory)ctx.lookup("java:comp/env/jms/myQueueFactory");
javax.jms.Connection connection = queueConnectionFactory.createConnection();
Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
And later in the code I use this session in a UserTransaction and it performs flawlessly with two MessageProducers with either Commit or Rollback.
What I don't understand is how can it be that I'm using javax.jms.XAConnectionFactory.createConnection() method and I get a Session which does the job? What's javax.jms.XAConnectionFactory role?
I'll also add that I've looked at the source code of both classes (and javax.jms.BasicConnectionFactory) and I verified that the XA class does not override createConnection.
The core of the difference between ConnectionFactory and XAConnectionFactory is that the XAConnectionFactory creates XAConnections which create XASessions. XASessions represent the real difference because (to quote from the JMS JavaDocs:)
The XASession interface extends the capability of Session by adding access to a JMS provider's support for the Java Transaction API (JTA) (optional). This support takes the form of a javax.transaction.xa.XAResource object.
In other words, the XASession is what gives the XA instances their transactional awareness. However, this specific implementation is optional, even for a fully compliant JMS provider. From the same JavaDoc:
An XAResource provides some fairly sophisticated facilities for interleaving work on multiple transactions, recovering a list of transactions in progress, and so on. A JTA aware JMS provider must fully implement this functionality. This could be done by using the services of a database that supports XA, or a JMS provider may choose to implement this functionality from scratch.
A client of the application server is given what it thinks is a regular JMS Session. Behind the scenes, the application server controls the transaction management of the underlying XASession.
In other words, the provider may require that you specify an XA or non-XA JMS resource, or, as would seem to be in your case, the provider may perform all the JTA plumbing transparently with what appears to be a regular JMS Session.
Actually, none of the example code you provided would exercise XA functionality. If all that is required is that your messages are under syncpoint, then you can get by with 1-phase commit (1PC). However if you want, for example, that JMS messages and DB updates occur in a single coordinated unit of work then you would use 2-phase commit (2PC) which is XA. Coordinating across two message producers on the same transport provider does not require XA 2PC.
If you were using 2PC, then in addition to COMMIT and ROLLBACK you would be calling BEGIN somewhere in the code. The lack of that verb in your example is why I said it looks like you are not doing 2PC. The BEGIN call would communicate with the transaction manager to establish a transaction context across the participating resource managers. Then the COMMIT would cause the messages and the DB updates to finalize in one unit of work. The interesting thing here is that if you have only one participating resource manager, some transports will silently optimize you back down to 1PC. In that case it looks as though you are doing 2PC but are really getting 1PC. Since there is only one resource manager there is no loss of reliability in this optimization.
On the other hand, if you are doing 1PC you won't see any difference between the two types of connection factory. It would exhibit exactly the behavior you describe.
The last case to consider is that you use ConnectionFactory and try to call BEGIN. Since the non-XA connection factory cannot participate in a coordinated XA transaction, this call should fail.