2007/01/24
24 Jan, 2007

Apache Sandesha2 Storage Framework

  • Chamikara Jayalath
  • Software Engineer - WSO2

Introduction

WS-Reliable Messaging is a very useful feature which will need to be available in any Web services framework. With Reliable Messaging users will be able to guarantee the delivery of their Web service invocations which is an inevitable feature when it comes to real business applications. Apache Sandesha2 was born to fill this gap for the latest and most promising open source Web service framework Apache Axis2. Later WSO2 Web Service Application Server (WSO2 WSAS) for Java was born combining Apache Axis2/Java, Apache Sandesha2/Java and several other open source products.

Among several interesting design points of Sandesha2, it's Storage Framework plays a vital role. Due to the Storage Framework users get the ability to easily plug-in their own storage mechanism to Sandesha2. All they have to do is implement several interfaces given by Sandesha2 in their own storage mechanism and changing the Sandesha2 policies to use this new storage framework. Sandesha2 will smoothly start working on your own storage mechanism.

When it comes to real business scenarios it is very important to clearly define the storage media that is used to store data. For example, a particular company may have an Oracle database deployed, and they may want to run all their softwares based on this database. Now if they need to run a Apache Axis2 or WSO2 WSAS they will most probably want the data of all the supporting modules of those servers also to use same database. So they will most probably expect the data of Apache Sandesha2 to also be stored in the same database or the same storage media. Due to the StorageManager based architecture of Sandesha2 you can easily do this as explained later in this article.

Architecture

This section will give you a introduction to how the Storage Framework is placed inside the overall architecture of Sandesha2. It will also introduce the internal components of the storage framework and show how these components fit together. Following diagram gives a clear view of the Sandesha2 overall architecture and shows how each component of Sandesha2 is working with the Storage Framework.

 

Storage Framework

 

 

As shown in the diagram, almost all the components in Sandesha2 interacts with the Storage Framework. Storage Framework serves all other components through its interface which could be used to manipulate RM (Reliable Messaging) data or to work with transactions.

The StorageManager Interfaces

The main interface defined by the Storage framework is the StorageManager interface which is given below.
public abstract class StorageManager {        
public abstract void initStorage (AxisModule);

public abstract RMSBeanMgr getRMSBeanMgr();
public abstract RMDBeanMgr getRMDBeanMgr();
public abstract SenderBeanMgr getSenderBeanMgr();
public abstract SequencePropertyBeanMgr getSequencePropertyBeanMgr();
public abstract InvokerBeanMgr getInvokerBeanMgr();

public abstract void storeMessageContext (String, MessageContext);
public abstract void updateMessageContext (String, MessageContext);
public abstract void removeMessageContext (String);
public abstract MessageContext retrieveMessageContext
(String, ConfigurationContext);

public abstract Transaction getTransaction();

public abstract SandeshaThread getSender();
public abstract SandeshaThread getInvoker();
}

For clarity and readability I removed some parts from the class and grouped the abstract methods to show common methods together. When you want sandesha2 to work on a particular storage mechanism you have to either create an implementation of this Storage Manager interface or find an existing implementation that suits your needs. Now I will try to describe the functionality of each of the abstract methods defined in the above interface.

initStorage method

This method should be used to initialize the StorageManager implementation. This gets called only once during the runtime of a Sandesha2 instance basically at the initiation of the module.

Getter methods for BeanManagers

Each of the next five methods return a certain type of BeanManager. Sandesha2 storage framework defines five types of storage beans and five types of BeanManagers to manage these beans. Each BeanManager defines several methods to manipulate that particular type of bean. The five types of beans and the BeanManagers that should be used to work with them are given below.

RMSBean (Managed by RMSBeanManager)

Each RMSBean represents a sequence on which the particular Sandesha2 instance works as a RM Source. RMSBean defines several properties that represents various values of that sequence. For example this include the sequenceId of that sequence and the internal sequence Id.

RMDBean (Managed by RMDBeanManager)

RMDBeans represent sequences on which the particular Sandesha2 instance works as a RM Destination. So an RMDBean will hold various values of a destinations sequence and these could be manipulated using a RMDBeanManagers. These values include the sequence Id of the sequence and the sequence Id of the highest message of the sequence that has been invoked.

SenderBean (Managed by SenderBeanManager)

Sender is an spacial thread started by an Sandesha2 instance which manages transmissions and retransmissions of messages. A SenderBean is an entry that should be managed by the Sender. So basically a SenderBean entry will represent an message that has to be transmitted or retransmitted. SenderBean defines several properties that is used by the Sender to manage the message represented by it.

InvokerBean (Managed by InvokerBeanManager)

Invoker is also another thread that will get started by a Sandesha2 instance. The invoker is used when message ordering is enabled and helps to do the ordering of messages in the order of message numbers. An InvokerBean entry represents a certain message that has to be invoked and defines several properties that will be used by the Invoker when doing its invocation.

SequencePropertyBean (Managed by SequencePropertyBeanManager)

These beans are used to hold properties of incoming or outgoing sequences. These are basically used to hold values that were not covered in RM Source (RMS) or RM Destination (RMD) beans. The SequncePropertyBean basically defines three properties- the property key of the sequence (which may be the sequenceId), the name of the property to be stored and the actual value. There are also several special purpose sequence property beans which are not directly bound to a particular sequence. One such property is used to store the Ids of all the sequences which are currently activated in the system.

Getter method for the Transaction object

The next methods should be used to get a transaction object. A transaction object defined by the Transaction interface should be used to wrap a transaction of the particular StorageManager implementation. The Transaction interface of Sandesha2 is given below.

public interface Transaction {
public void commit ();
public void rollback ();
public boolean isActive ();
}

It is assumed that a new transaction gets started at the moment the getTransaction method gets called from the StorageManager. The transaction should be committed or rolled back when the respective method gets called from the returned Transaction object. The isActive method can be used to see whether the transaction is still on an active stage. I.e. it has not been committed or rolled back.

Getter methods for the Sender and the Invoker

Sender and Invoker are two threads managed by Sandesha2, each managing its own worker thread pool. Sender is responsible for transmission and retransmission of messages while the Invoker is responsible for the ordered invocation of messages. These two methods return the Sender and Invoker object that should be used with the respective StorageManager.

Using StorageManager Implementations

There can be various implementations of this StorageManager interface which works on various types of storage mechanisms. To use a particular implementation you have to use Sandesha2 policies. Currently the StorageManager policy should be mentioned in the module level. It cannot be overridden by policies set in a service or operational level. The part of the Sandesha2 policy that is used to configure StorageManagers is given below.

<sandesha2:storagemanagers>                
<sandesha2:inmemorystoragemanager>
org.apache.sandesha2.storage.inmemory.InMemoryStorageManager
</sandesha2:inmemorystoragemanager>
<sandesha2:permanentstoragemanager>
org.wso2.sandesha2.storage.persistent.hibernate.PersistentStorageManager
</sandesha2:permanentstoragemanager>
</sandesha2:storagemanagers>

As you can see, you can specify two StorageManagers in Sandesha2 policies. The first one is assumed to be an in-memory storage manager implementation and the second one is assumed to be persistent storage based. This classification is simply for ease of use and nothing prevents you from mentioning in-memory implementations in both places.

As mentioned above there could be various StorageManager implementations based on various types of storage mechanisms. This coming sections will introduce two such implementations.

Default In-Memory StorageManager Implementation

There is a HashTable based StorageManager implementation that comes with the standard Sandesha2 distributions. In this StorageManager implementation each BeanManager simply uses a internal HashTable to store its set of storage beans.

Hibernate based StorageManager Implementation

WSO2 provides a hibernate based persistent StorageManager implementation for Sandesha2. The sources code for this can be downloaded from WSO2 Oxygen Tank svn page. An official release is not available by the day I write this article. For more information on using and configuring this StorageManager implementation please refer to the WSO2 Oxygen Tank.

Creating your own StorageManager implementation

Implementing your own StorageManager is a matter of implementing a set of interfaces given by Sandesha2 using your storage mechanism.

Your first focus should be trying to implement the five BeanManager interfaces. All these interfaces are having simple methods such as create, retrieve, update, delete and find. Implement these so that they perform these operations on the respective beans to manipulate them according to your storage mechanism.

Then you have to implement the Transaction interface. Basically what you should be doing is wrapping a Transaction instance of your storage mechanism with the implementation of Sandesha2 transaction interface.

After implementing these basic interfaces you can implement the StorageManager interface. This implementation have two simple return instances of the BeanManagers and the Transaction you implemented.

Freely use Sandesha2 module.xml to hold any properties that will be needed by your StorageManager implementation. Or else, it is also possible to use an external configuration file.

Conclusion

Sandesha2 StorageManager is one of the most important components of Sandesha2. With the StorageManager you can easily change the way Sandesha2 stores its data. There are several StorageManager implementations available or if needed you can also quite easily create your own implementation.

Resources

Author

Chamikara Jayalath, Software Engineer, WSO2 In. chamikara at wso2 dot com

 

About Author

  • Chamikara Jayalath
  • Software Engineer
  • WSO2 Inc.