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 prerequistes
Helma 1.3 is needed.
Installation
Unzip the distribution to your Helma home directory. A new subdirectory
helmagroups
will be created andhelmagroups-0.7.jar
andjgroups-all.jar
are extracted tolib/ext
.Configuration
server.properties
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.GroupExtensionJGroups stack
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.
Multicast
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
andmcast_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
andport_range
define a number of ports used for group membership management (the full range needs to be open in the firewall too).TCP
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.Mounting a group
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 | noneorgroup.<alias> = http://<your-config-url> group.<alias>.writable = true | false group.<alias>.sendMode = all | majority | first | noneEach group is available as
group.<alias>
to the scripting environment. To be able to change a group from an application thewritable
property explicitly has to be set totrue
. ThesendMode
property defines how the group is going to handle write operations: in modeall
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.Debugging
In the
/helmagroups/debug.properties
different levels of debugging can be defined:debug.helma = trueThis makes the HelmaExtension log all its activities (each put/remove-operation is logged when sent and when received).
trace=trueThis makes JGroups log all in- and outgoing messages/events.
trace=true default_output=DEBUG STDOUTThis makes JGroups log all activities - use this only for debugging as it creates megabytes of log data within minutes.
Usage in the scripting environment
Basically, a new global object
group
is added to the scripting environment (likeapp
orroot
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 replicatedUnfortunately it's currently not possible to build a tree locally and then replicate all the objects.
Methods of a GroupObject
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.
RPC to the whole group
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
andapp
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); }Details of a group / Modifying a group
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 groupgroup.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