DRAFT - An introduction to the JBoss Modular Service Container: Part 2 - Transaction layer

May 21, 2017
• Weinan Li

Here is the diagram that shows both the transaction layer and the container layer:

From the diagram, we can see whole system can be roughly divided into two parts. The bottom left corner is the container layer, and the up right corner is the transaction layer. The connection point is ServiceContainerImpl <-> TransactionController.

Here is the diagram that shows the transaction layer only:

There are two kinds of transactions by design. One is ReadTransaction and the other is UpdateTransaction. Here is the relationship of them:

The above diagram shows that the ReadTransaction interface extends the Transaction interface, and UpdateTransaction extends from the ReadTransaction. When we want to use the transaction controller to create the service container, or adding services into the container, or removing services from the container, then we need the UpdateTransaction as a handle to request the controller to take these actions. We will see the detail usage of the update transaction later.

Nearly all the actions in the container are asynchronous. The above code creates a transaction. You can think a transaction as a handler provided by TransactionController to control the whole lifecycle of the service container. The transaction will be used to create service container, register services into container, and it is associated with an executor. We will see the usage of the UpdateTransaction later.

The above transaction creation action is executed asynchronously, so we need to create a CompletionListener in above code to wait for the transaction creation to be done by the controller, and finally we get the UpdateTransaction from the listener.

We get the service container from the service controller as shown above. Please see the usage of transaction. The update transaction is associated with every action issued by the controller, and the transaction will be prepared and committed at last to modify the service container statuses. We will see this in following code.

ServiceRegistry registry = container.newRegistry(transaction);

The service registry is created from the container, and it will be used to include services.

The above code will add a service named foo into the service registry, and it will return a service builder for the service name to be connected with a real service. The following code defines a service:

The above code defines a FooService that implements the Service interface. It’s a service just does nothing but just output some log.

serviceBuilder.setService(new FooService());

The above code used the service builder to connect the service to its service name.

serviceBuilder.install();

The above code used the service builder to install the service into registry. Until now we have finished the work to create a service container and put a service registry into the container, and we have created our service and associated it with the registry. Now we should commit the transaction and all the above actions will be persisted in container. Here is the final code:

We can see the prepare(...) and commit(...) actions are all related with the update transaction, and these actions are all executed asynchronously. We can see the container relies on the transaction handle to manage its consistency of the internal state. All the actions are associated with the update transaction, and after the update transaction is committed, the actions will alter the container state.

In addition, there is only one update transaction can be in action at one time. It does not allow multiple threads to get many update transactions to alter container internal services and their statuses at one time. If there are many threads requesting the update transactions, only one thread will succeed and others need to wait.

Here is the relative code in org.jboss.msc.txn.AbstractTransaction.registerUpdateTransaction(...) method:

From the above diagram, we can see if there are more than one update transaction creation requests, the requested update transactions will be put into pendingTxns queue, and there is only one running update transaction allowed and the listener will be called to return the transaction to the caller. The other callers will be blocked if they call their completion listeners.

Here is the complete code:

importorg.jboss.msc.service.*;importorg.jboss.msc.txn.Transaction;importorg.jboss.msc.txn.TransactionController;importorg.jboss.msc.txn.UpdateTransaction;importorg.jboss.msc.util.CompletionListener;importjava.util.concurrent.Executor;importjava.util.concurrent.Executors;importjava.util.concurrent.ThreadPoolExecutor;/**
* Created by weli on 16/05/2017.
*/publicclassPlay{publicstaticvoidmain(String[]args){TransactionControllercontroller=TransactionController.newInstance();finalCompletionListener<UpdateTransaction>updateListener=newCompletionListener<>();controller.newUpdateTransaction(Executors.newSingleThreadExecutor(),updateListener);finalUpdateTransactiontransaction=updateListener.awaitCompletionUninterruptibly();classFooServiceimplementsService<Void>{@Overridepublicvoidstart(StartContext<Void>startContext){System.out.println("Foo service started.");}@Overridepublicvoidstop(StopContextstopContext){System.out.println("Foo service stopped.");}}try{ServiceContainercontainer=controller.newServiceContainer(transaction);ServiceRegistryregistry=container.newRegistry(transaction);ServiceBuilder<Void>serviceBuilder=controller.newServiceContext(transaction).addService(registry,ServiceName.of("foo"));serviceBuilder.setService(newFooService());serviceBuilder.install();controller.downgrade(transaction,null);}finally{finalCompletionListener<Transaction>prepareListener=newCompletionListener<>();controller.prepare(transaction,prepareListener);prepareListener.awaitCompletionUninterruptibly();finalCompletionListener<Transaction>commitListener=newCompletionListener<>();controller.commit(transaction,commitListener);commitListener.awaitCompletionUninterruptibly();}}}