Helma-groups is a project mainly written in JAVA and JAVASCRIPT, it's free.
Fork of the Helma Groups extension
HelmaGroups - an extension for Helma Object Publisher =====================================================
This package enables group-communication between different Helma Servers over the network taking advantage of JGroups Library (www.jgroups.org). A new global object group
is added to the scripting environment and all changes in that object tree are immediately replicated to all members.
Helma 1.3 is needed.
Unzip the distribution to your Helma home directory. A new subdirectory helmagroups
will be created and helmagroups-0.7.jar
and jgroups-all.jar
are extracted to lib/ext
.
To activate HelmaGroups for a server add this line to the server.properties file. This makes Helma load the extension at startup and add a global object group
to every application.
extensions = helma.extensions.helmagroups.GroupExtension
To create a group we first need a configuration of the network properties: This is located in an xml-file in the /helmagroups-directory. A JGroups stack consists of a number of protocols that handle group traffic. At the bottom is a protocol that does the actual transmission (UDP or TCP), above that come protocols that handle group membership (PING, TCPPING, MERGE2, GMS - group membership service etc) and some protocols that take care of messages (FRAG - divides up larger message into smaller pieces, NAKACK - reliable mcast message transission etc). Two default stacks are included with the extension, default.xml for multicast-setups (basically only useful in a LAN environment) and tcp.xml for tcp-setups that can run over a WAN too.
The following parameters in the stack config have to be set in case of using the UDP protocol (default.xml):
<protocol-param name="mcast_addr" value="228.8.8.8"/> <protocol-param name="mcast_port" value="45566"/> <protocol-param name="bind_port" value="46000"/> <protocol-param name="port_range" value="1000"/> <protocol-param name="ip_ttl" value="32"/> <protocol-param name="bind_addr" value="192.168.10.10"/>
mcast_addr
and mcast_port
specify the multicast address this group is using. If you wan to run different groups each needs its own xml-configuration-file with either a different address or port here. ip_ttl
specifies how far multicast packets travel on the network: If set to 0 they are only seen on the localhost, 32 is for the whole network, higher values may be needed if your router is forwarding multicast traffic to another network. bind_addr
is only needed when your computer has more than one network adapter. bind_port
and port_range
define a number of ports used for group membership management (the full range needs to be open in the firewall too).
Configuration in case of a TCP stack (tcp.xml):
TCP: <protocol-param name="start_port" value="7800"/> <protocol-param name="bind_addr" value="192.168.10.10"/> TCPPING: <protocol-param name="initial_hosts" value="www.helma.org[7800],classic.helma.at[7800]"/> <protocol-param name="port_range" value="3"/>
In the TCP protocol start_port
defines which port a new member tries to use first. If it is already taken (possibly be another group instance running on the same machine the port number is increased until a free port is found. bind_addr
is again only necessary for multiple network adapters.
The TCPPING protocol is responsible for finding other group members. Hosts that can be contacted at startup are listed in inital_hosts
, port_range
defines how many port above the given are tried.
An application can run different groups: The network- and stack-configuration for each group is located in an xml-file. This configuration can either be stored locally in the the helmahome/helmagroups-directory or is fetched from an url. To mount such a group to an application add this to the app.properties
file:
group.<alias> = <filename-of-config> group.<alias>.writable = true | false group.<alias>.sendMode = all | majority | first | noneor
group.<alias> = http://<your-config-url> group.<alias>.writable = true | false group.<alias>.sendMode = all | majority | first | none
Each group is available as group.<alias>
to the scripting environment. To be able to change a group from an application the writable
property explicitly has to be set to true
. The sendMode
property defines how the group is going to handle write operations: in mode all
it waits for all other group members to acknowledge a change operation, majority
waits for 50% + 1 member, first
is obvious. none
sets the group to fire & forget mode - be careful with your group architecture as this can cause confusion if the group is fed from more than one member.
In the /helmagroups/debug.properties
different levels of debugging can be defined:
debug.helma = true
This makes the HelmaExtension log all its activities (each put/remove-operation is logged when sent and when received).
trace=true
This makes JGroups log all in- and outgoing messages/events.
trace=true default_output=DEBUG STDOUT
This makes JGroups log all activities - use this only for debugging as it creates megabytes of log data within minutes.
Basically, a new global object group
is added to the scripting environment (like app
or root
etc). Below this object a tree of objects can be built, with all add-, modify- and remove-operations being transmitted to the network and replicated to all other helma servers in that group immediately. The JGroups-library takes care of transmitting these operations lossless, ordered and obtains the correct state during startup.
The group object is visible in all applications in the group. To this object new GroupObjects can be added. A GroupObject can have strings, numbers, dateobjects and other GroupObjects as properties, so a tree of GroupObjects and data can be built.
group.test = new GroupObject(); group.test.somekey = "some value"; group.test.propkey1 = new GroupObject(); group.test.propkey1.anotherkey = "another value";
As soon as a GroupObject is assigning to the tree, it can be seen by all other members of the group. From that point on, any change of a property will be replicated immediately to all other members. Please note that a change operation will block until it has been seen by all group members. So the above code would produce three updates of the group, better would be the following code:
var obj = new GroupObject(); obj.somekey1 = "somevalue1"; obj.somekey2 = "somevalue2"; group.test = obj; < now the whole object gets replicated
Unfortunately it's currently not possible to build a tree locally and then replicate all the objects.
GroupObject.list()
returns an array containing all the properties that are GroupObjects.
var arr = group.test.list();
GroupObject.count()
Returns the number of properties that are GroupObjects.
GroupObject.waitFor(propName, x)
Waits for x millis and stops if a property propName is touched in the meantime (added/modified,removed). Can be used for example to wait until a login-server has logged a user in and returned its data to the tree.
GroupObject.unwrap(), GroupObject.wrap()
Returns a local copy of a replicated GroupObject, with just the primitive properties (but not the children) copied. This can be used to save network load if more than one property is changed in a row: Unwrap() the object, do all the changes and put it back in the tree by using the wrap() method. It is important to go this way and not just assigning it to the original position because otherwise the branch that existed below that point would get lost.
var obj = group.test.unwrap(); obj.key1 = "value1"; obj.key2 = "value2"; group.test.wrap(obj);
Please note that transactions are NOT supported, so if the script fails later in the execution changes that were made to replicated objects will remain.
group.getRemote (groupname)
It is possible to execute requests in all applications that mount to a group. The syntax is similar to Helma's XmlRpc-API. First get a Remote-Object, then call a function on it. The Remote-object represents the root of the remote application, it is possible to descend deeper.
var r = group.getRemote ("sessions"); var arr = r.testfunction (123,456); // or: r.bogusobj.testfunction() .....
Calling a function returns an array of result objects each having four properties: result
, error
, host
and app
for (var i=0; i<arr.length; i++) { res.write (arr[i].app + "@" + arr[i].host + " returned: "); res.writeln ((arr[i].result) ? arr[i].result : arr[i].error); }
For each group some additional information and methods are supplied by the top group
object:
group.getContent (groupname)
group.getFullContent (groupname)
the structure of the tree (content) or the full content of the tree (fullContent).
group.size (groupname)
group.count (groupname)
the total number of GroupObjects in the group.
group.getMembers (groupname)
a list of all members of the group
group.getConnection (groupname)
the ip and portnumber(s) this group is connected to.
group.getConfig (groupname)
the configuration of this group (the JGroups stack)
group.getFullConfig (groupname)
the full configuration of this group (the JGroups stack including all properties)
group.isConnected (groupname)
returns true/false on wether the group is connected.
group.connect (groupname)
group.reconnect (groupname)
group.disconnect (groupname)
Remove or add the server from/to the group, connect() is done at startup automatically.
group.reset (groupname)
deletes all content from the group.
group.destroy (groupname)
makes all instances of the group disconnect.
group.log (message)
logs to the same logfile as the groupextension does. this is useful to have the log statements in the correct order as the internal messages by the extension (which is difficult to achieve if different logs are used).
last modified: 2004-03-28