Cluster synchronization
By utilizing a synchronization server that accepts connections from a cluster a communication network is
established. Any node from the cluster can then propagate messages and objects to other nodes or access shared
information from the synchronization server.
public class DummyCache extends SynchronizedObject {
@Override
public String getNamespace() {
return DummyCache.class.getName();
}
@Override
public void receive(String cmd) {
refresh(cmd);
}
public void updateEntry(String id) {
send(id);
refresh(id);
}
private void refresh(String id) {
//refresh the entry id
}
}
To synchronize a cache it simply extends the superclass SynchronizedObject.
The cache will then be registered automatical and can send messages to instances of the same cache on
the connected cluster. If local changes require the cache to refresh it's content it can notify other nodes
to refresh their respective cache as well. Providing a solution to volatile content in caches.
When a cache entry is expensive to create, objects can be stored on the synchronization server.
Requiering only one node to create the entry. By implementing a synchronization server,
cluster can retrieve information from a common location. Allowing to rollout configurations automatical
to your web application by connecting it to a synchronization server. This allows to manage configurations
on a remote location whilest providing a local client.
| reserveObject |
A dummy is created on the server for a specific ID. When other client's query the object it will be noted as existing but unavailable. |
| updateObject |
Creates an object on the server for a specific ID. When other client's query the object it will be noted as existing and available. |
| getObject |
Gets the object for a specific ID from the server. The wrapper RemoteObject holds information about it's state. If the object does not exists the payload will be null. |
| waitForObj |
Gets the object for a specific ID from the server. When the object is existing but not available at the moment this call will lock till the object is ready(or has been removed). |
| removeObject |
Removes an object for a specific ID from the server. |
| setTimeout |
Sets the timeout for an object on the server to a fixed amount of time. After the time expires the object is deleted. |
| setTimeoutAfterRequest |
Sets the timeout for an object on the server to an amount of time that is refreshed whenever the server sends the object to a client. |
| setDefaultTimeout |
Like setTimeout but the amount is taken from the synchronization servers default timeout. |
| setDefaultTimeoutAfterRequest |
Like setTimeoutAfterRequest but the amount is taken from the synchronization servers default timeout. |
| executeMethod |
Executes a method on a server object specified by an id, a method name and method arguments. The return value of the method is returned to the client. |
The protocol for communicating between client and synchronization server feautures different types of
requests. Reserve and update are used to initialize objects. getObject and waitForObj retrieve objects with
the option to wait until a reserved object is updated. removeObject allows to manually discard objects, while the
timeout types allow to set up an automatic behaviour. executeMethod allows to alter the object on the
synchronization server or use the objects as service.
By utilizing remote method calls thread safe locks can be used. If a node wants to execute an exclusive
action, it aquires a lock for the action and releases it afterwards. During the execution no other node can
obtain the lock. This allows a safe implementation, without having to limit access to the action to a single node.
If the lock is not available the program can wait for it to be freed or deny the action.
//singleton instance
SynchronizationObjManager manager = SynchronizationObjManager.instance();
//the lock for the flow
RemoteObject ro = manager.getObject(flowName);
//getting the current lockHolder
ro = manager.executeMethod(flowName, new RemoteMethod("getLockHolder",null));
if ( ro.unpack() == null ) {
//lock is not held by anyone
Object[] args = new Object[1];
args[0] = clientId;
ro = manager.executeMethod(flowName, new RemoteMethod("lockFor",args));
if ( clientId.equals(ro.unpack()) ) {
//client aquired the lock
logger.info("Lock "+flowName+" activated.");
//working with the lock
...
//releasing the lock
ro = manager.executeMethod(flowName, new RemoteMethod("unlock",args));
if ( Boolean.TRUE.equals(ro.unpack()) ) {
//The lock can now be used by other clients
logger.info("Lock "+flowName+" deactivated");
} else {
//Could only happen if the lock wasn't aquired correctly
logger.info("Lock "+flowName+" could not be deactivated");
}
} else {
//another client took the lock between checking and acquiring
logger.info("Lock "+flowName+" could not be activated.");
}
}
The sourcecode shows the full process of checking, acquiring and releasing locks.