asmuts 02/01/14 22:38:11
Added: src/java/org/apache/stratum/jcs/engine/group
GroupAttrName.java GroupCache.java
GroupCacheManager.java
GroupCacheManagerFactory.java GroupId.java
GroupRWLockManager.java
Log:
grouping funcitonality built on top of the cache hub
allows for getting the list of items in a group without keeping the entire group
local
could be cleaned up
Revision Changes Path
1.1
jakarta-turbine-stratum/src/java/org/apache/stratum/jcs/engine/group/GroupAttrName.java
Index: GroupAttrName.java
===================================================================
package org.apache.stratum.jcs.engine.group;
import java.io.*;
/////////////////////////////////////////////////
public class GroupAttrName implements Serializable {
//final GroupId groupId;
public final String groupId;
final String attrName;
//////////////////////////////////////////////////////
public GroupAttrName(String groupId, String attrName) {
//this.groupId = new GroupId(groupId);
this.groupId = groupId;
this.attrName = attrName;
if (groupId == null || attrName == null)
throw new IllegalArgumentException("groupId " + groupId + " and attrName " +
attrName + ", must not be null.");
}
/////////////////////////////////////////////////////////////
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof GroupAttrName))
return false;
GroupAttrName to = (GroupAttrName)obj;
return groupId.equals(to.groupId) && attrName.equals(to.attrName);
}
////////////////////////////////////////////////
public int hashCode() {
return groupId.hashCode() ^ attrName.hashCode();
}
////////////////////////////////////////////////
public String toString() {
return "[groupId="+groupId+", attrName="+attrName+"]";
}
}
1.1
jakarta-turbine-stratum/src/java/org/apache/stratum/jcs/engine/group/GroupCache.java
Index: GroupCache.java
===================================================================
package org.apache.stratum.jcs.engine.group;
import java.io.*;
import java.util.*;
import org.apache.stratum.jcs.engine.behavior.*;
import org.apache.stratum.jcs.engine.control.*;
import org.apache.stratum.jcs.engine.*;
import org.apache.stratum.jcs.access.*;
import org.apache.stratum.jcs.access.exception.*;
import org.apache.stratum.jcs.engine.control.Cache;
import org.apache.stratum.jcs.auxiliary.disk.*;
import org.apache.stratum.jcs.auxiliary.remote.*;
import org.apache.stratum.jcs.auxiliary.remote.behavior.*;
import org.apache.stratum.jcs.utils.reuse.*;
import org.apache.stratum.jcs.utils.log.*;
/**
* Group cache is basically a composite cache with the additional
* capability of providing automatic and safe attribute name list update
* for each GroupAttrName cache item.
*
* TODO: reduce the number of methods or describe them better. The complexity
* of this points to group design problems. I need to fix the locking and the
* maintenance of the remote list. The list can be infered fromt he contents of
* the cache. Iterating through the hashtable could be problematic but easier.
*/
public class GroupCache extends Cache implements ICompositeCache {
//////////////// debug MUST BE SET TO false in production! ///////////////////
private static final boolean debug = false;//true;
private static final boolean debuggan = false;
private ReadWriteLockManager locker = GroupRWLockManager.getInstance();
private String source_id = "org.apache.stratum.jcs.engine.group.GroupCache";
// declare a group_id cache here, so ids and elements don't compete
// for first
ICompositeCache systemGroupIdCache;
///////////////////////////////////////////////////
public Serializable getSourceId() {
return this.source_id;
}
///////////////////////////////////////////////////////////////
public GroupCache(String cacheName, ICache[] auxCaches, ICompositeCacheAttributes
cattr, Attributes attr ) {
super(cacheName, auxCaches, cattr, attr);
if ( debug ) {
p( "constructed groupcache " + cacheName + " from super" );
}
//ICompositeCache systemGroupIdCache = (ICompositeCache)systemCaches.get(
"groupIdCache" );
}
///////////////////////////////////////////////////////////////
public GroupCache(String cacheName, ICache[] auxCaches, ICompositeCacheAttributes
cattr, Attributes attr, ICompositeCache systemGroupIdCache ) {
super(cacheName, auxCaches, cattr, attr);
if ( debug ) {
p( "constructed (2) groupcache " + cacheName + " from super" );
}
this.systemGroupIdCache = systemGroupIdCache;
}
/**
* Overrides to provide read lock on both GroupAttrName read-operation
* and String read-operation.
*/
public Serializable get (Serializable key) {
return get( key, false, this.LOCAL_INVOKATION );
}
public Serializable get (Serializable key, boolean container) {
return get( key, false, this.LOCAL_INVOKATION );
}
public Serializable get( Serializable key, boolean container, boolean invocation )
{
if (key instanceof GroupAttrName) {
return getGAN( (GroupAttrName)key, container );
}
if (key instanceof GroupId) {
return getGI( (GroupId)key, container );
}
if ( debug ) {
p( this.getCacheName() + " getting " + key + " from super, invocation = " +
invocation );
}
return super.get( key, container, invocation );
}
/** Places a read lock on the group id for a GroupAttrName get-operation. */
public Serializable getGAN (GroupAttrName key, boolean container) {
return getGAN( key, container, this.LOCAL_INVOKATION );
}
public Serializable getGAN (GroupAttrName key, boolean container, boolean
invocation) {
if ( debug ) {
p( "in getGAN, invocation = " + invocation );
}
Object obj = null;
// not necessary?, stops at getaux
readLock(key.groupId);
try {
obj = super.get(key, container, invocation);
//p( "got obj" );
} finally {
locker.done(key.groupId);
}
return (Serializable)obj;
}
/** Places a read lock on the key for a GroupId get-operation. */
// get list from remote if it isn't present
public Serializable getGI (GroupId gid, boolean container) {
return getGI( gid, container, this.LOCAL_INVOKATION );
}
// stay local if this is a remote operation
// removal of a group element will call this to get the list to edit
public Serializable getGI (GroupId gid, boolean container, boolean invocation) {
if ( debug ) {
p( "in getGi" );
}
Object obj = null;
readLock(gid.key);
try {
//obj = super.get(gid.key, container);
obj = systemGroupIdCache.get(gid.key, container, invocation);
if ( debug ) {
p( "got obj in getGi " + obj );
}
} catch( IOException ioeg ) {
} finally {
locker.done(gid.key);
}
return (Serializable)obj;
}
/////////////////////////////////////////////
private void readLock (String id) {
try {
locker.readLock(id);
} catch (InterruptedException ex) {
// should never happen.
ex.printStackTrace();
//////////////// debug MUST BE SET TO false in production! ///////////////////
if (debug) {
try {
System.in.read();
} catch (IOException ignore) {}
}
throw new IllegalStateException(ex.getMessage());
}
}
//////////////////////////////////////
private void writeLock (String id) {
try {
locker.writeLock(id);
} catch (InterruptedException ex) {
// should never happen.
ex.printStackTrace();
//////////////// debug MUST BE SET TO false in production! ///////////////////
if (debug) {
try {
System.in.read();
} catch (IOException ignore) {}
}
throw new IllegalStateException(ex.getMessage());
}
}
/** Overrides to special handling for GroupAttrName put-operation. */
public void put (Serializable key, Serializable val, Attributes attr) throws
IOException {
if (key instanceof GroupAttrName) {
try {
if ( debug ) {
p( "putting via putGAN((GroupAttrName)key, val, attr) method" );
}
putGAN((GroupAttrName)key, val, attr);
} catch ( IOException ioe ) {}
return;
}
if ( debug ) {
p( "updating " + key + " via super method" );
}
//super.put(key, val);
try {
updateCaches(key, val, attr);
} catch( IOException ioe ) {}
return;
}
public void put (Serializable key, Serializable val) throws IOException {
//public void put (Object key, Object val) throws IOException {
if (key instanceof GroupAttrName) {
try {
if ( debug ) {
p( "putting via putGAN((GroupAttrName)key, val) method" );
}
putGAN((GroupAttrName)key, val);
} catch ( IOException ioe ) {}
return;
}
if ( debug ) {
p( "updating " + key + " via super method" );
}
//super.put(key, val);
Attributes attrE = (Attributes)this.attr.copy();
try {
updateCaches(key, val, attrE);
} catch( IOException ioe ) {}
return;
}
///////////////////////////////////////////////////////////
public synchronized void update( ICacheElement ce ) throws IOException {
update( ce, true );
}
///////////////////////////////////////////////////////////
public synchronized void update( ICacheElement ce, boolean localInvocation )
throws IOException {
Object key = ce.getKey();
if (key instanceof GroupAttrName) {
try {
if ( debug ) {
p( "putting via ga method" );
}
putGAN((GroupAttrName)key, ce.getVal());
} catch ( IOException ioe ) {}
return;
}
if ( debug ) {
p( "updating " + key + " via super method" );
}
//super.put(key, val);
Attributes attrE = (Attributes)this.attr.copy();
try {
super.update(ce, localInvocation);
} catch( IOException ioe ) {}
return;
} // end update
/** GroupAttrName specific put-operation. */
public void putGAN (GroupAttrName key, Serializable val) throws IOException {
if ( debug ) {
p( "in putGAN(GroupAttrName key, Serializable val) method" );
}
if (key == null || val == null) {
NullPointerException ex = new NullPointerException("key=" + key + " and val="
+ val + " must not be null.");
log.error(ex);
throw ex;
}
Attributes attrE = (Attributes)this.attr.copy();
putGAN(key, val, attrE);
return;
}
/**
* Special handling for GroupAttrName put-operation.
* Provides write lock and automatic attribute name set update.
*/
// TODO: DistCacheMulticaster,etc. currently only supports key of String type
// Needs to support GroupAttrName type, or do we ?
private void putGAN(GroupAttrName key, Serializable val, Attributes attrE ) throws
IOException {
if ( debug ) {
p( "in putGAN( gan,val,attr) " );
}
putGAN( key, val, attrE, true );
}
public void putGAN(GroupAttrName key, Serializable val, Attributes attrE, boolean
updateRemote ) throws IOException {
if ( debug ) {
p( "in putGAN( gan,val,attr,boolean updateRemote) " );
}
writeLock(key.groupId);
try {
// update the attribute.
//updateCaches(key, val, attrE, INCLUDE_REMOTE_CACHE);
CacheElement ce = new CacheElement( this.getCacheName(), key, val );
ce.setAttributes( attrE );
if ( debuggan ) {
p( "updating group attribute via super, updateRemote = " + updateRemote );
}
super.update( ce, updateRemote );
//updateCaches(key, val, attrE, updateRemote);
// update the attribute name set.
// Note: use super.get to avoid read lock.
//GroupId groupId = key.groupId;//new GroupId(key.groupId);
GroupId groupId = new GroupId(this.getCacheName(),key.groupId);
HashSet attrNameSet = null;
// avoid lock of getGI, call super
//attrNameSet = (HashSet)super.get(groupId.key, false);
attrNameSet = (HashSet)systemGroupIdCache.get(groupId.key, false);
if (attrNameSet == null) {
//p( "couldn't find group attrnameSet" );
attrNameSet = new HashSet();
}
attrNameSet.add(key.attrName);
if ( debuggan ) {
p( "attrNameSet.size() = " + attrNameSet.size() );
}
// problem, this sets the group attr to the item
// maintain the core list ont he remote.
//updateCaches(groupId.key, attrNameSet, attrE, EXCLUDE_REMOTE_CACHE );
CacheElement ceID = new CacheElement( this.getCacheName(), groupId.key,
attrNameSet );
ceID.setAttributes( attrE );
//super.update( ceID, EXCLUDE_REMOTE_CACHE );
//updateCaches(groupId.key, attrNameSet, attrE, EXCLUDE_REMOTE_CACHE );
systemGroupIdCache.update( ceID, EXCLUDE_REMOTE_CACHE );
//systemGroupIdCache.update( ceID, !EXCLUDE_REMOTE_CACHE );
} finally {
locker.done(key.groupId);
}
}
/////////////////////////////////////////////////////////////////////
protected void createGroup( String group ) throws CacheException {
createGroup( group, this.attr );
}
/////////////////////////////////////////////////////////////////////
protected void createGroup( String group, Attributes attrE ) throws CacheException
{
// update the attribute name set.
GroupId groupId = new GroupId(this.getCacheName(),group);
HashSet attrNameSet = null;
//attrNameSet = (HashSet)super.get(groupId.key);
try {
attrNameSet = (HashSet)systemGroupIdCache.get(groupId.key);
} catch ( IOException ioe ) {}
if (attrNameSet == null) {
attrNameSet = new HashSet();
} else {
throw new CacheException( "group " + group + " already exists " );
}
try {
CacheElement ceID = new CacheElement( this.getCacheName(), groupId.key,
attrNameSet );
ceID.setAttributes( attrE );
//updateCaches(groupId.key, attrNameSet, attrE );
//super.update( ceID, EXCLUDE_REMOTE_CACHE );
systemGroupIdCache.update( ceID, EXCLUDE_REMOTE_CACHE );
} catch ( IOException ioe ) {}
}
/** Overrides to provide special handling for GroupAttrName remove-operation. */
public boolean remove (Serializable key) {
if ( debug ) {
p( "in basic remove" );
}
// if expired super will call remove and we can't have a lock
// need a third method
return remove( key, ICache.LOCAL_INVOKATION );
}
/** Easier to classify and send to other methods than relying on type
* overriding. removeGAN could be called remove
*/
public boolean remove(Serializable key, boolean invocation ) {
if (key instanceof GroupAttrName) {
if ( debug ) {
p( "calling removeGAN, invocation = " + invocation );
}
return removeGAN((GroupAttrName)key, invocation);
}
if (key instanceof GroupId) {
if ( debug ) {
p( "call removeGI" );
}
return removeGI((GroupId)key, invocation);
}
if ( debug ) {
p( "call super.remove, " + invocation + " for " + key );
}
return super.remove(key, invocation);
}
/**
* Special handling for GroupAttrName remove-operation.
* Provides write lock and automatic attribute name set update.
* <br><br>
* Note: there is the possibility that all the remove-cache-events for all
* group attribute names are queued and pending to be processed. Meanwhile,
* the get-opeation for the attrbute name set will return with a size > 0.
* Hence, when a groupn is invalidated, it's necessary to queue a
* remove-attribute-name-set request to clean up garbage due to this race
condition.
*/
public boolean removeGAN (GroupAttrName key, boolean invokation) {
boolean ret;
if ( debug ) {
p( "in removeGAN, invokation = " + invokation );
}
//writeLock(key.groupId);
try {
// remove the attribute.
//ret = remove(key, LOCAL_INVOKATION);
ret = super.remove(key, invokation);
updateGroupAttrNameSet( key, invokation, true );
} finally {
//locker.done(key.groupId);
}
return ret;
}
////////////////////////////////////////////////////////////
/** Handles removal, update, and insertion of items into the attrNameSet
* for a group cache region. The Group Cache listener called the add when it
gets a put
* though it may remove the item it referes to, depending ont he configuration.
* This saves on local cache space and keeps the list up to date.
*/
public void updateGroupAttrNameSet( GroupAttrName key, boolean invokation, boolean
remove ) {
// update the attribute name set.
// Note: necessary to use super.get to avoid read lock within the current
write lock.
GroupId groupId = new GroupId(this.getCacheName(),key.groupId);
HashSet attrNameSet = null;
CacheElement ce = null;
//ce = (CacheElement)get(groupId.key, true );
//attrNameSet = (HashSet)systemGroupIdCache.get(groupId.key);
try {
//ce = (CacheElement)systemGroupIdCache.get(groupId.key, true, invokation );
//p( "systemGroupIdCache = " + systemGroupIdCache );
//p( "groupId.key " + groupId.key );
//p( "this.cacheAttr.getCacheName() = " + this.cacheAttr.getCacheName() );
ce = (CacheElement)systemGroupIdCache.get(groupId.key, true, invokation );
} catch ( IOException ioe ) {}
if ( ce != null ) {
attrNameSet = (HashSet)ce.val;
if (attrNameSet != null || !remove) {
if ( attrNameSet == null ) {
attrNameSet = new HashSet();
}
if ( remove ) {
attrNameSet.remove(key.attrName);
} else {
attrNameSet.add( key.attrName );
}
if (attrNameSet.size() > 0) {
// update the changed name set.
try {
//nonLocal = true
// last arg her is whether to update the remote
// shouldn't update remote if not local
// LOCAL_INVOKATION = false
// remove gi list for now if it the element is remotely changed
// otherwsie it will lack elements, since they are not moved
// with the items. Otherwise add the keys on the
// remote listener handlePut method. ya that's better.
//boolean updateRemote = !invokation;
//updateCaches(groupId.key, attrNameSet, ce.attr, updateRemote );
CacheElement ceID = new CacheElement( this.getCacheName(),
groupId.key, attrNameSet );
ceID.setAttributes( ce.attr );
if ( debug ) {
p( "calling systemGroupIdCache.update( ceID, EXCLUDE_REMOTE_CACHE
)" );
}
systemGroupIdCache.update( ceID, EXCLUDE_REMOTE_CACHE );
} catch ( IOException ioe ) {}
}
else if ( remove ) {
// no more attribute, so remove the name set all together, skipLock =
true.
//super.remove(groupId, invokation );
//removeGI(groupId, invokation, true );
try {
if ( debug ) {
p( "calling systemGroupIdCache.remove( groupId.key,
EXCLUDE_REMOTE_CACHE )" );
}
systemGroupIdCache.remove( groupId.key, EXCLUDE_REMOTE_CACHE );
} catch ( IOException ioe ) {}
}
}
}
} // end updateGroupAttrNameSet
/**
* Special handling for GroupId remove-operation.
* Removes the attribute name set of the session.
*/
public void removeGI (GroupId groupId ) {
if ( debug ) {
p( "removeGI" );
}
removeGI( groupId, false );
}
///////////////////////////////////////
/**
* Skip the lock from the normal remove that is called from the
* super when an element expires. Keep the read lock method for
* calls from groupaccess.
*/
protected boolean removeGI (GroupId groupId, boolean nonLocal) {
return removeGI( groupId, nonLocal, false );
}
protected boolean removeGI (GroupId groupId, boolean nonLocal, boolean skipLock) {
boolean ok = false;
// loop back, shouldn't of go here
//if (!(key instanceof GroupId)) {
// ok = super.remove( (GroupId)groupId, nonLocal );
// return ok;
//}
// problem with removing expired while getting!
skipLock = false;
if ( debug ) {
p( "in removeGI" );
}
if ( !skipLock ) {
writeLock(groupId.key);
}
try {
//ok = super.remove(groupId.key, nonLocal);
ok = systemGroupIdCache.remove(groupId.key, nonLocal);
} catch( IOException ioeg ) {
} finally {
if ( !skipLock ) {
locker.done(groupId.key);
}
}
return ok;
}
////////////////////////////////////////////////
public Attributes getElementAttributes( Serializable key ) throws CacheException,
IOException {
CacheElement ce = (CacheElement)getCacheElement( key );
if ( ce == null ) {
throw new ObjectNotFoundException( "key " + key + " is not found" );
}
return ce.getAttributes();
}
////////////////////////////////////////
public static void p( String s ) {
System.out.println( "GroupCache: " + s );
}
}
1.1
jakarta-turbine-stratum/src/java/org/apache/stratum/jcs/engine/group/GroupCacheManager.java
Index: GroupCacheManager.java
===================================================================
package org.apache.stratum.jcs.engine.group;
import java.io.*;
/////////////////////////////////////
import org.apache.stratum.jcs.engine.behavior.*;
import org.apache.stratum.jcs.engine.control.*;
import org.apache.stratum.jcs.engine.*;
import org.apache.stratum.jcs.access.*;
import org.apache.stratum.jcs.engine.control.Cache;
import org.apache.stratum.jcs.auxiliary.disk.*;
import org.apache.stratum.jcs.auxiliary.remote.*;
import org.apache.stratum.jcs.auxiliary.remote.behavior.*;
//////////////////////////////////////////////////////////////////
public class GroupCacheManager extends CompositeCacheManager implements Serializable
{
private static GroupCacheManager instance;
///////////////////////////////////////////
protected GroupCacheManager() {
super();
}
///////////////////////////////////////////////
protected GroupCacheManager(String propFile) {
super(propFile);
}
/** Factory method to create the actual GroupCache instance. */
protected Cache createSystemCache (String cacheName, ICache[] auxCaches,
ICompositeCacheAttributes cattr, Attributes attr ) {
p( "called create cache in groupcachemanager" );
ICompositeCache systemGroupIdCache = (ICompositeCache)systemCaches.get(
"groupIdCache" );
return new GroupCache( cacheName, auxCaches, cattr, attr, systemGroupIdCache );
}
protected Cache createCache (String cacheName, ICache[] auxCaches,
ICompositeCacheAttributes cattr, Attributes attr ) {
p( "called create cache in groupcachemanager" );
// sort of add hoc
ICompositeCache systemGroupIdCache = (ICompositeCache)systemCaches.get(
"groupIdCache" );
return new GroupCache( cacheName, auxCaches, cattr, attr, systemGroupIdCache );
}
protected void incrementClients() {
super.incrementClients();
}
} // end class
1.1
jakarta-turbine-stratum/src/java/org/apache/stratum/jcs/engine/group/GroupCacheManagerFactory.java
Index: GroupCacheManagerFactory.java
===================================================================
package org.apache.stratum.jcs.engine.group;
import java.io.*;
import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.*;
import java.util.*;
import java.sql.*;
/////////////////////////////////////
import org.apache.stratum.jcs.engine.behavior.*;
import org.apache.stratum.jcs.engine.control.*;
import org.apache.stratum.jcs.engine.*;
import org.apache.stratum.jcs.access.*;
import org.apache.stratum.jcs.engine.control.Cache;
import org.apache.stratum.jcs.auxiliary.disk.*;
import org.apache.stratum.jcs.auxiliary.remote.*;
import org.apache.stratum.jcs.auxiliary.remote.behavior.*;
import org.apache.stratum.jcs.utils.reuse.*;
import org.apache.stratum.jcs.utils.log.*;
/////////////////////////////////////////////////////////////
public class GroupCacheManagerFactory {
private static final boolean pOut = false; //true;
private static GroupCacheManager instance;
//////////////////////////////////////////
private GroupCacheManagerFactory() {
}
///////////////////////////////////////////
public static GroupCacheManager getInstance() {
return getInstance(null);
}
//////////////////////////////////////////////
public static GroupCacheManager getInstance(String propFile) {
p ( "getting GroupCacheManger instance" );
if (instance == null) {
synchronized(GroupCacheManager.class) {
if (instance == null) {
instance = propFile == null ? new GroupCacheManager() : new
GroupCacheManager(propFile);
}
}
}
if ( instance.log.logLevel >= instance.log.DEBUG ) {
instance.log.debug( "Manager stats : " + instance.getStats() + "<br> -- in
getInstance()" );
}
instance.incrementClients();
return instance;
}
//////////////////////////////////////////////////////////
private static void p(String s) {
if ( pOut ) {
System.out.println("GroupCacheManagerFactory:"+s+"
>"+Thread.currentThread().getName());
}
}
} // end class
1.1
jakarta-turbine-stratum/src/java/org/apache/stratum/jcs/engine/group/GroupId.java
Index: GroupId.java
===================================================================
package org.apache.stratum.jcs.engine.group;
import java.io.*;
/**
* Used to avoid name conflict when group cache items are mixed with
* non-group cache items in the same cache.
*/
public class GroupId implements Serializable {
public final String key;
public GroupId(String cacheName, String key ) {
this.key = cacheName + key;
if (key == null)
throw new IllegalArgumentException("key must not be null.");
}
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof GroupId))
return false;
GroupId to = (GroupId)obj;
return key.equals(to.key);
}
public int hashCode() {
return key.hashCode();
}
public String toString() {
return "[grouId="+key+"]";
}
}
1.1
jakarta-turbine-stratum/src/java/org/apache/stratum/jcs/engine/group/GroupRWLockManager.java
Index: GroupRWLockManager.java
===================================================================
package org.apache.stratum.jcs.engine.group;
import org.apache.stratum.jcs.utils.reuse.*;
import java.util.*;
/** The ReadWriteLock Manager for distributed group list management. */
class GroupRWLockManager extends ReadWriteLockManager {
public static final boolean debug = false;
private static GroupRWLockManager instance;
private final Hashtable ht = new Hashtable();
/** Returns the lock table of all the resources managed by this manager. */
protected Hashtable getLocks() {
return ht;
}
private GroupRWLockManager() {
}
static GroupRWLockManager getInstance() {
if (instance == null) {
synchronized(GroupRWLockManager.class) {
if (instance == null) {
instance = new GroupRWLockManager();
if (debug) {
p(" >> GroupRWLockManager instanciated.");
}
}
}
}
return instance;
}
private static void p(String s) {
System.out.println("GroupRWLockManager:"+s);
}
}
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>