FileDocCategorySizeDatePackage
RelationService.javaAPI DocJava SE 6 API130413Tue Jun 10 00:26:18 BST 2008javax.management.relation

RelationService.java

/*
 * @(#)RelationService.java	1.50 06/01/17
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package javax.management.relation;

import com.sun.jmx.trace.Trace;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.management.Attribute;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanException;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.MBeanServerDelegate;
import javax.management.MBeanServerNotification;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.ReflectionException;

/**
 * The Relation Service is in charge of creating and deleting relation types
 * and relations, of handling the consistency and of providing query
 * mechanisms.
 * <P>It implements the NotificationBroadcaster by extending
 * NotificationBroadcasterSupport to send notifications when a relation is
 * removed from it.
 * <P>It implements the NotificationListener interface to be able to receive
 * notifications concerning unregistration of MBeans referenced in relation
 * roles and of relation MBeans.
 * <P>It implements the MBeanRegistration interface to be able to retrieve
 * its ObjectName and MBean Server.
 *
 * @since 1.5
 */
public class RelationService extends NotificationBroadcasterSupport
    implements RelationServiceMBean, MBeanRegistration, NotificationListener {

    //
    // Private members
    //

    // Map associating:
    //      <relation id> -> <RelationSupport object/ObjectName>
    // depending if the relation has been created using createRelation()
    // method (so internally handled) or is an MBean added as a relation by the
    // user
    private Map<String,Object> myRelId2ObjMap = new HashMap<String,Object>();

    // Map associating:
    //      <relation id> -> <relation type name>
    private Map<String,String> myRelId2RelTypeMap = new HashMap<String,String>();

    // Map associating:
    //      <relation MBean Object Name> -> <relation id>
    private Map<ObjectName,String> myRelMBeanObjName2RelIdMap =
	new HashMap<ObjectName,String>();

    // Map associating:
    //       <relation type name> -> <RelationType object>
    private Map<String,RelationType> myRelType2ObjMap =
	new HashMap<String,RelationType>();

    // Map associating:
    //       <relation type name> -> ArrayList of <relation id>
    // to list all the relations of a given type
    private Map<String,List<String>> myRelType2RelIdsMap =
	new HashMap<String,List<String>>();

    // Map associating:
    //       <ObjectName> -> HashMap
    // the value HashMap mapping:
    //       <relation id> -> ArrayList of <role name>
    // to track where a given MBean is referenced.
    private Map<ObjectName,Map<String,List<String>>>
	myRefedMBeanObjName2RelIdsMap =
	    new HashMap<ObjectName,Map<String,List<String>>>();

    // Flag to indicate if, when a notification is received for the
    // unregistration of an MBean referenced in a relation, if an immediate
    // "purge" of the relations (look for the relations no
    // longer valid) has to be performed , or if that will be performed only
    // when the purgeRelations method will be explicitly called.
    // true is immediate purge.
    private boolean myPurgeFlag = true;

    // Internal counter to provide sequence numbers for notifications sent by:
    // - the Relation Service
    // - a relation handled by the Relation Service
    private Long myNtfSeqNbrCounter = new Long(0);

    // ObjectName used to register the Relation Service in the MBean Server
    private ObjectName myObjName = null;

    // MBean Server where the Relation Service is registered
    private MBeanServer myMBeanServer = null;

    // Filter registered in the MBean Server with the Relation Service to be
    // informed of referenced MBean unregistrations
    private MBeanServerNotificationFilter myUnregNtfFilter = null;

    // List of unregistration notifications received (storage used if purge
    // of relations when unregistering a referenced MBean is not immediate but
    // on user request)
    private List<MBeanServerNotification> myUnregNtfList =
	new ArrayList<MBeanServerNotification>();

    //
    // Constructor
    //

    /**
     * Constructor.
     *
     * @param immediatePurgeFlag  flag to indicate when a notification is
     * received for the unregistration of an MBean referenced in a relation, if
     * an immediate "purge" of the relations (look for the relations no
     * longer valid) has to be performed , or if that will be performed only
     * when the purgeRelations method will be explicitly called.
     * <P>true is immediate purge.
     */
    public RelationService(boolean immediatePurgeFlag) {

	if (isTraceOn())
	    trace("Constructor: entering", null);

	setPurgeFlag(immediatePurgeFlag);

	if (isTraceOn())
	    trace("Constructor: exiting", null);
	return;
    }

    /**
     * Checks if the Relation Service is active.
     * Current condition is that the Relation Service must be registered in the
     * MBean Server
     *
     * @exception RelationServiceNotRegisteredException  if it is not
     * registered
     */
    public void isActive()
	throws RelationServiceNotRegisteredException {
	if (myMBeanServer == null) {
	    // MBean Server not set by preRegister(): relation service not
	    // registered
	    String excMsg =
		"Relation Service not registered in the MBean Server.";
	    throw new RelationServiceNotRegisteredException(excMsg);
	}
	return;
    }

    //
    // MBeanRegistration interface
    //

    // Pre-registration: retrieves its ObjectName and MBean Server
    //
    // No exception thrown.
    public ObjectName preRegister(MBeanServer server,
				  ObjectName name)
	throws Exception {

	myMBeanServer = server;
	myObjName = name;
	return name;
    }

    // Post-registration: does nothing
    public void postRegister(Boolean registrationDone) {
	return;
    }

    // Pre-unregistration: does nothing
    public void preDeregister()
	throws Exception {
	return;
    }

    // Post-unregistration: does nothing
    public void postDeregister() {
	return;
    }

    //
    // Accessors
    //

    /**
     * Returns the flag to indicate if when a notification is received for the
     * unregistration of an MBean referenced in a relation, if an immediate
     * "purge" of the relations (look for the relations no longer valid)
     * has to be performed , or if that will be performed only when the
     * purgeRelations method will be explicitly called.
     * <P>true is immediate purge.
     *
     * @return true if purges are automatic.
     *
     * @see #setPurgeFlag
     */
    public boolean getPurgeFlag() {
	return myPurgeFlag;
    }

    /**
     * Sets the flag to indicate if when a notification is received for the
     * unregistration of an MBean referenced in a relation, if an immediate
     * "purge" of the relations (look for the relations no longer valid)
     * has to be performed , or if that will be performed only when the
     * purgeRelations method will be explicitly called.
     * <P>true is immediate purge.
     *
     * @param purgeFlag  flag
     *
     * @see #getPurgeFlag
     */
    public void setPurgeFlag(boolean purgeFlag) {

	myPurgeFlag = purgeFlag;
	return;
    }

    // Returns internal counter to be used for Sequence Numbers of
    // notifications to be raised by:
    // - a relation handled by this Relation Service (when updated)
    // - the Relation Service
    private Long getNotificationSequenceNumber() {
	Long result = null;
	synchronized(myNtfSeqNbrCounter) {
	    result = new Long(myNtfSeqNbrCounter.longValue() + 1);
	    myNtfSeqNbrCounter = new Long(result.longValue());
	}
	return result;
    }

    //
    // Relation type handling
    //

    /**
     * Creates a relation type (a RelationTypeSupport object) with given
     * role infos (provided by the RoleInfo objects), and adds it in the
     * Relation Service.
     *
     * @param relationTypeName  name of the relation type
     * @param roleInfoArray  array of role infos
     *
     * @exception IllegalArgumentException  if null parameter
     * @exception InvalidRelationTypeException  If:
     * <P>- there is already a relation type with that name
     * <P>- the same name has been used for two different role infos
     * <P>- no role info provided
     * <P>- one null role info provided
     */
    public void createRelationType(String relationTypeName,
				   RoleInfo[] roleInfoArray)
	throws IllegalArgumentException,
	       InvalidRelationTypeException {

	if (relationTypeName == null || roleInfoArray == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn())
	    trace("createRelationType: entering", relationTypeName);

	// Can throw an InvalidRelationTypeException
	RelationType relType =
	    new RelationTypeSupport(relationTypeName, roleInfoArray);

	addRelationTypeInt(relType);

	if (isTraceOn())
	    trace("createRelationType: exiting", null);
	return;
    }

    /**
     * Adds given object as a relation type. The object is expected to
     * implement the RelationType interface.
     *
     * @param relationTypeObj  relation type object (implementing the
     * RelationType interface)
     *
     * @exception IllegalArgumentException  if null parameter or if
     * {@link RelationType#getRelationTypeName
     * relationTypeObj.getRelationTypeName()} returns null.
     * @exception InvalidRelationTypeException  if:
     * <P>- the same name has been used for two different roles
     * <P>- no role info provided
     * <P>- one null role info provided
     * <P>- there is already a relation type with that name
     */
    public void addRelationType(RelationType relationTypeObj)
	throws IllegalArgumentException,
	       InvalidRelationTypeException {

	if (relationTypeObj == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn())
	    trace("addRelationType: entering", null);

	// Checks the role infos
	List<RoleInfo> roleInfoList = relationTypeObj.getRoleInfos();
	if (roleInfoList == null) {
	    String excMsg = "No role info provided.";
	    throw new InvalidRelationTypeException(excMsg);
	}

	RoleInfo[] roleInfoArray = new RoleInfo[roleInfoList.size()];
	int i = 0;
	for (RoleInfo currRoleInfo : roleInfoList) {
	    roleInfoArray[i] = currRoleInfo;
	    i++;
	}
	// Can throw InvalidRelationTypeException
	RelationTypeSupport.checkRoleInfos(roleInfoArray);

	addRelationTypeInt(relationTypeObj);

	if (isTraceOn())
	    trace("addRelationType: exiting", null);
	return;
     }

    /**
     * Retrieves names of all known relation types.
     *
     * @return ArrayList of relation type names (Strings)
     */
    public List<String> getAllRelationTypeNames() {
	ArrayList<String> result = null;
	synchronized(myRelType2ObjMap) {
	    result = new ArrayList<String>(myRelType2ObjMap.keySet());
	}
	return result;
    }

    /**
     * Retrieves list of role infos (RoleInfo objects) of a given relation
     * type.
     *
     * @param relationTypeName  name of relation type
     *
     * @return ArrayList of RoleInfo.
     *
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationTypeNotFoundException  if there is no relation type
     * with that name.
     */
    public List<RoleInfo> getRoleInfos(String relationTypeName)
	throws IllegalArgumentException,
	       RelationTypeNotFoundException {

	if (relationTypeName == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn())
	    trace("getRoleInfos: entering", relationTypeName);

	// Can throw a RelationTypeNotFoundException
	RelationType relType = getRelationType(relationTypeName);

	if (isTraceOn())
	    trace("getRoleInfos: exiting", null);
	return relType.getRoleInfos();
    }

    /**
     * Retrieves role info for given role name of a given relation type.
     *
     * @param relationTypeName  name of relation type
     * @param roleInfoName  name of role
     *
     * @return RoleInfo object.
     *
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationTypeNotFoundException  if the relation type is not
     * known in the Relation Service
     * @exception RoleInfoNotFoundException  if the role is not part of the
     * relation type.
     */
    public RoleInfo getRoleInfo(String relationTypeName,
				String roleInfoName)
	throws IllegalArgumentException,
	       RelationTypeNotFoundException,
               RoleInfoNotFoundException {

	if (relationTypeName == null || roleInfoName == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn()) {
	    String str = "relationTypeName " + relationTypeName
				    + ", roleInfoName " + roleInfoName;
	    trace("getRoleInfo: entering", str);
	}

	// Can throw a RelationTypeNotFoundException
	RelationType relType = getRelationType(relationTypeName);

	// Can throw a RoleInfoNotFoundException
	RoleInfo roleInfo = relType.getRoleInfo(roleInfoName);

	if (isTraceOn())
	    trace("getRoleInfo: exiting", null);
	return roleInfo;
    }

    /**
     * Removes given relation type from Relation Service.
     * <P>The relation objects of that type will be removed from the
     * Relation Service.
     *
     * @param relationTypeName  name of the relation type to be removed
     *
     * @exception RelationServiceNotRegisteredException  if the Relation
     * Service is not registered in the MBean Server
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationTypeNotFoundException  If there is no relation type
     * with that name
     */
    public void removeRelationType(String relationTypeName)
	throws RelationServiceNotRegisteredException,
	       IllegalArgumentException,
	       RelationTypeNotFoundException {

	// Can throw RelationServiceNotRegisteredException
	isActive();

	if (relationTypeName == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn())
	    trace("removeRelationType: entering", relationTypeName);

	// Checks if the relation type to be removed exists
	// Can throw a RelationTypeNotFoundException
	RelationType relType = getRelationType(relationTypeName);

	// Retrieves the relation ids for relations of that type
	List<String> relIdList = null;
	synchronized(myRelType2RelIdsMap) {
	    // Note: take a copy of the list as it is a part of a map that
	    //       will be updated by removeRelation() below.
	    List<String> relIdList1 =
		myRelType2RelIdsMap.get(relationTypeName);
	    if (relIdList1 != null) {
		relIdList = new ArrayList<String>(relIdList1);
	    }
	}

	// Removes the relation type from all maps
	synchronized(myRelType2ObjMap) {
	    myRelType2ObjMap.remove(relationTypeName);
	}
	synchronized(myRelType2RelIdsMap) {
	    myRelType2RelIdsMap.remove(relationTypeName);
	}

	// Removes all relations of that type
	if (relIdList != null) {
	    for (String currRelId : relIdList) {
		// Note: will remove it from myRelId2RelTypeMap :)
		//
		// Can throw RelationServiceNotRegisteredException (detected
		// above)
		// Shall not throw a RelationNotFoundException
		try {
		    removeRelation(currRelId);
		} catch (RelationNotFoundException exc1) {
		    throw new RuntimeException(exc1.getMessage());
		}
	    }
	}

	if (isTraceOn())
	    trace("removeRelationType: exiting", null);
	return;
    }

    //
    // Relation handling
    //

    /**
     * Creates a simple relation (represented by a RelationSupport object) of
     * given relation type, and adds it in the Relation Service.
     * <P>Roles are initialized according to the role list provided in
     * parameter. The ones not initialized in this way are set to an empty
     * ArrayList of ObjectNames.
     * <P>A RelationNotification, with type RELATION_BASIC_CREATION, is sent.
     *
     * @param relationId  relation identifier, to identify uniquely the relation
     * inside the Relation Service
     * @param relationTypeName  name of the relation type (has to be created
     * in the Relation Service)
     * @param roleList  role list to initialize roles of the relation (can
     * be null).
     *
     * @exception RelationServiceNotRegisteredException  if the Relation
     * Service is not registered in the MBean Server
     * @exception IllegalArgumentException  if null parameter, except the role
     * list which can be null if no role initialization
     * @exception RoleNotFoundException  if a value is provided for a role
     * that does not exist in the relation type
     * @exception InvalidRelationIdException  if relation id already used
     * @exception RelationTypeNotFoundException  if relation type not known in
     * Relation Service
     * @exception InvalidRoleValueException if:
     * <P>- the same role name is used for two different roles
     * <P>- the number of referenced MBeans in given value is less than
     * expected minimum degree
     * <P>- the number of referenced MBeans in provided value exceeds expected
     * maximum degree
     * <P>- one referenced MBean in the value is not an Object of the MBean
     * class expected for that role
     * <P>- an MBean provided for that role does not exist
     */
    public void createRelation(String relationId,
			       String relationTypeName,
			       RoleList roleList)
	throws RelationServiceNotRegisteredException,
	       IllegalArgumentException,
               RoleNotFoundException,
               InvalidRelationIdException,
               RelationTypeNotFoundException,
               InvalidRoleValueException {

	// Can throw RelationServiceNotRegisteredException
	isActive();

	if (relationId == null ||
	    relationTypeName == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn()) {
	    StringBuffer strB =
		new StringBuffer("relationId " + relationId
				 + ", relationTypeName " + relationTypeName);
	    if (roleList != null) {
		strB.append(", roleList " + roleList.toString());
	    }
	    trace("createRelation: entering", strB.toString());
	}

	// Creates RelationSupport object
	// Can throw InvalidRoleValueException
	RelationSupport relObj = new RelationSupport(relationId,
					       myObjName,
					       relationTypeName,
					       roleList);

	// Adds relation object as a relation into the Relation Service
	// Can throw RoleNotFoundException, InvalidRelationId,
	// RelationTypeNotFoundException, InvalidRoleValueException
	//
	// Cannot throw MBeanException
	addRelationInt(true,
		       relObj,
		       null,
		       relationId,
		       relationTypeName,
		       roleList);

	if (isTraceOn())
	    trace("createRelation: exiting", null);
	return;
    }

    /**
     * Adds an MBean created by the user (and registered by him in the MBean
     * Server) as a relation in the Relation Service.
     * <P>To be added as a relation, the MBean must conform to the
     * following:
     * <P>- implement the Relation interface
     * <P>- have for RelationService ObjectName the ObjectName of current
     * Relation Service
     * <P>- have a relation id unique and unused in current Relation Service
     * <P>- have for relation type a relation type created in the Relation
     * Service
     * <P>- have roles conforming to the role info provided in the relation
     * type.
     *
     * @param relationObjectName  ObjectName of the relation MBean to be added.
     *
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationServiceNotRegisteredException  if the Relation
     * Service is not registered in the MBean Server
     * @exception NoSuchMethodException  If the MBean does not implement the
     * Relation interface
     * @exception InvalidRelationIdException  if:
     * <P>- no relation identifier in MBean
     * <P>- the relation identifier is already used in the Relation Service
     * @exception InstanceNotFoundException  if the MBean for given ObjectName
     * has not been registered
     * @exception InvalidRelationServiceException  if:
     * <P>- no Relation Service name in MBean
     * <P>- the Relation Service name in the MBean is not the one of the
     * current Relation Service
     * @exception RelationTypeNotFoundException  if:
     * <P>- no relation type name in MBean
     * <P>- the relation type name in MBean does not correspond to a relation
     * type created in the Relation Service
     * @exception InvalidRoleValueException  if:
     * <P>- the number of referenced MBeans in a role is less than
     * expected minimum degree
     * <P>- the number of referenced MBeans in a role exceeds expected
     * maximum degree
     * <P>- one referenced MBean in the value is not an Object of the MBean
     * class expected for that role
     * <P>- an MBean provided for a role does not exist
     * @exception RoleNotFoundException  if a value is provided for a role
     * that does not exist in the relation type
     */
    public void addRelation(ObjectName relationObjectName)
	throws IllegalArgumentException,
	       RelationServiceNotRegisteredException,
	       NoSuchMethodException,
	       InvalidRelationIdException,
	       InstanceNotFoundException,
	       InvalidRelationServiceException,
               RelationTypeNotFoundException,
               RoleNotFoundException,
	       InvalidRoleValueException {

	if (relationObjectName == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn())
	    trace("addRelation: entering", relationObjectName.toString());

	// Can throw RelationServiceNotRegisteredException
	isActive();

	// Checks that the relation MBean implements the Relation interface.
	// It will also check that the provided ObjectName corresponds to a
	// registered MBean (else will throw an InstanceNotFoundException)
	if ((!(myMBeanServer.isInstanceOf(relationObjectName, "javax.management.relation.Relation")))) {
	    String excMsg = "This MBean does not implement the Relation interface.";
	    throw new NoSuchMethodException(excMsg);
	}
	// Checks there is a relation id in the relation MBean (its uniqueness
	// is checked in addRelationInt())
	// Can throw InstanceNotFoundException (but detected above)
	// No MBeanException as no exception raised by this method, and no
	// ReflectionException
	String relId = null;
	try {
	    relId = (String)(myMBeanServer.getAttribute(relationObjectName,
                                                        "RelationId"));

	} catch (MBeanException exc1) {
	    throw new RuntimeException(
				     (exc1.getTargetException()).getMessage());
	} catch (ReflectionException exc2) {
	    throw new RuntimeException(exc2.getMessage());
	} catch (AttributeNotFoundException exc3) {
	    throw new RuntimeException(exc3.getMessage());
	}

	if (relId == null) {
	    String excMsg = "This MBean does not provide a relation id.";
	    throw new InvalidRelationIdException(excMsg);
	}
	// Checks that the Relation Service where the relation MBean is
	// expected to be added is the current one
	// Can throw InstanceNotFoundException (but detected above)
	// No MBeanException as no exception raised by this method, no
	// ReflectionException
	ObjectName relServObjName = null;
	try {
	    relServObjName = (ObjectName)
		(myMBeanServer.getAttribute(relationObjectName,
                                            "RelationServiceName"));

	} catch (MBeanException exc1) {
	    throw new RuntimeException(
				     (exc1.getTargetException()).getMessage());
	} catch (ReflectionException exc2) {
	    throw new RuntimeException(exc2.getMessage());
	} catch (AttributeNotFoundException exc3) {
	    throw new RuntimeException(exc3.getMessage());
	}

	boolean badRelServFlag = false;
	if (relServObjName == null) {
	    badRelServFlag = true;

	} else if (!(relServObjName.equals(myObjName))) {
	    badRelServFlag = true;
	}
	if (badRelServFlag) {
	    String excMsg = "The Relation Service referenced in the MBean is not the current one.";
	    throw new InvalidRelationServiceException(excMsg);
	}
	// Checks that a relation type has been specified for the relation
	// Can throw InstanceNotFoundException (but detected above)
	// No MBeanException as no exception raised by this method, no
	// ReflectionException
	String relTypeName = null;
	try {
	    relTypeName = (String)(myMBeanServer.getAttribute(relationObjectName,
                                                              "RelationTypeName"));

	} catch (MBeanException exc1) {
	    throw new RuntimeException(
				     (exc1.getTargetException()).getMessage());
	}catch (ReflectionException exc2) {
	    throw new RuntimeException(exc2.getMessage());
	} catch (AttributeNotFoundException exc3) {
	    throw new RuntimeException(exc3.getMessage());
	}
	if (relTypeName == null) {
	    String excMsg = "No relation type provided.";
	    throw new RelationTypeNotFoundException(excMsg);
	}
	// Retrieves all roles without considering read mode
	// Can throw InstanceNotFoundException (but detected above)
	// No MBeanException as no exception raised by this method, no
	// ReflectionException
	RoleList roleList = null;
	try {
	    roleList = (RoleList)(myMBeanServer.invoke(relationObjectName,
						       "retrieveAllRoles",
						       null,
						       null));
	} catch (MBeanException exc1) {
	    throw new RuntimeException(
				     (exc1.getTargetException()).getMessage());
	} catch (ReflectionException exc2) {
	    throw new RuntimeException(exc2.getMessage());
	}

	// Can throw RoleNotFoundException, InvalidRelationIdException,
	// RelationTypeNotFoundException, InvalidRoleValueException
	addRelationInt(false,
		       null,
		       relationObjectName,
		       relId,
		       relTypeName,
		       roleList);
	// Adds relation MBean ObjectName in map
	synchronized(myRelMBeanObjName2RelIdMap) {
	    myRelMBeanObjName2RelIdMap.put(relationObjectName, relId);
	}

	// Updates flag to specify that the relation is managed by the Relation
	// Service
	// This flag and setter are inherited from RelationSupport and not parts
	// of the Relation interface, so may be not supported.
	try {
	    myMBeanServer.setAttribute(relationObjectName,
                                       new Attribute(
                                         "RelationServiceManagementFlag",
                                         Boolean.TRUE));
	} catch (Exception exc) {
	    // OK : The flag is not supported.
	}

	// Updates listener information to received notification for
	// unregistration of this MBean
	List<ObjectName> newRefList = new ArrayList<ObjectName>();
	newRefList.add(relationObjectName);
	updateUnregistrationListener(newRefList, null);

	if (isTraceOn())
	    trace("addRelation: exiting", null);
	return;
    }

    /**
     * If the relation is represented by an MBean (created by the user and
     * added as a relation in the Relation Service), returns the ObjectName of
     * the MBean.
     *
     * @param relationId  relation id identifying the relation
     *
     * @return ObjectName of the corresponding relation MBean, or null if
     * the relation is not an MBean.
     *
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationNotFoundException there is no relation associated
     * to that id
     */
    public ObjectName isRelationMBean(String relationId)
	throws IllegalArgumentException,
	       RelationNotFoundException{

	if (relationId == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn())
	    trace("isRelationMBean", relationId);

	// Can throw RelationNotFoundException
	Object result = getRelation(relationId);
	if (result instanceof ObjectName) {
	    return ((ObjectName)result);
	} else {
	    return null;
	}
    }

    /**
     * Returns the relation id associated to the given ObjectName if the
     * MBean has been added as a relation in the Relation Service.
     *
     * @param objectName  ObjectName of supposed relation
     *
     * @return relation id (String) or null (if the ObjectName is not a
     * relation handled by the Relation Service)
     *
     * @exception IllegalArgumentException  if null parameter
     */
    public String isRelation(ObjectName objectName)
	throws IllegalArgumentException {

	if (objectName == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn())
	    trace("isRelation", objectName.toString());

	String result = null;
	synchronized(myRelMBeanObjName2RelIdMap) {
	    String relId = myRelMBeanObjName2RelIdMap.get(objectName);
	    if (relId != null) {
		result = relId;
	    }
	}
	return result;
    }

    /**
     * Checks if there is a relation identified in Relation Service with given
     * relation id.
     *
     * @param relationId  relation id identifying the relation
     *
     * @return boolean: true if there is a relation, false else
     *
     * @exception IllegalArgumentException  if null parameter
     */
    public Boolean hasRelation(String relationId)
	throws IllegalArgumentException {

	if (relationId == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn())
	    trace("hasRelation", relationId);

	try {
	    // Can throw RelationNotFoundException
	    Object result = getRelation(relationId);
	    return true;
	} catch (RelationNotFoundException exc) {
	    return false;
	}
    }

    /**
     * Returns all the relation ids for all the relations handled by the
     * Relation Service.
     *
     * @return ArrayList of String
     */
    public List<String> getAllRelationIds() {
	List<String> result = null;
	synchronized(myRelId2ObjMap) {
	    result = new ArrayList<String>(myRelId2ObjMap.keySet());
	}
	return result;
    }

    /**
     * Checks if given Role can be read in a relation of the given type.
     *
     * @param roleName  name of role to be checked
     * @param relationTypeName  name of the relation type
     *
     * @return an Integer wrapping an integer corresponding to possible
     * problems represented as constants in RoleUnresolved:
     * <P>- 0 if role can be read
     * <P>- integer corresponding to RoleStatus.NO_ROLE_WITH_NAME
     * <P>- integer corresponding to RoleStatus.ROLE_NOT_READABLE
     *
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationTypeNotFoundException  if the relation type is not
     * known in the Relation Service
     */
    public Integer checkRoleReading(String roleName,
				    String relationTypeName)
	throws IllegalArgumentException,
               RelationTypeNotFoundException {

	if (roleName == null || relationTypeName == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn()) {
	    String str = "roleName " + roleName
				    + ", relationTypeName " + relationTypeName;
	    trace("checkRoleReading: entering", str);
	}

	Integer result = null;

	// Can throw a RelationTypeNotFoundException
	RelationType relType = getRelationType(relationTypeName);

	try {
	    // Can throw a RoleInfoNotFoundException to be transformed into
	    // returned value RoleStatus.NO_ROLE_WITH_NAME
	    RoleInfo roleInfo = relType.getRoleInfo(roleName);

	    result =  checkRoleInt(1,
				   roleName,
				   null,
				   roleInfo,
				   false);

	} catch (RoleInfoNotFoundException exc) {
	    result = new Integer(RoleStatus.NO_ROLE_WITH_NAME);
	}

	if (isTraceOn())
	    trace("checkRoleReading: exiting", null);
	return result;
    }

    /**
     * Checks if given Role can be set in a relation of given type.
     *
     * @param role  role to be checked
     * @param relationTypeName  name of relation type
     * @param initFlag  flag to specify that the checking is done for the
     * initialization of a role, write access shall not be verified.
     *
     * @return an Integer wrapping an integer corresponding to possible
     * problems represented as constants in RoleUnresolved:
     * <P>- 0 if role can be set
     * <P>- integer corresponding to RoleStatus.NO_ROLE_WITH_NAME
     * <P>- integer for RoleStatus.ROLE_NOT_WRITABLE
     * <P>- integer for RoleStatus.LESS_THAN_MIN_ROLE_DEGREE
     * <P>- integer for RoleStatus.MORE_THAN_MAX_ROLE_DEGREE
     * <P>- integer for RoleStatus.REF_MBEAN_OF_INCORRECT_CLASS
     * <P>- integer for RoleStatus.REF_MBEAN_NOT_REGISTERED
     *
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationTypeNotFoundException  if unknown relation type
     */
    public Integer checkRoleWriting(Role role,
				    String relationTypeName,
				    Boolean initFlag)
	throws IllegalArgumentException,
	       RelationTypeNotFoundException {

	if (role == null ||
	    relationTypeName == null ||
	    initFlag == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn()) {
	    String str = new String("role " + role.toString()
				    + ", relationTypeName " + relationTypeName
				    + ", initFlag " + initFlag);
	    trace("checkRoleWriting: entering", str);
	}

	// Can throw a RelationTypeNotFoundException
	RelationType relType = getRelationType(relationTypeName);

	String roleName = role.getRoleName();
	List<ObjectName> roleValue = role.getRoleValue();
	boolean writeChkFlag = true;
	if (initFlag.booleanValue()) {
	    writeChkFlag = false;
	}

	RoleInfo roleInfo = null;
	try {
	    roleInfo = relType.getRoleInfo(roleName);
	} catch (RoleInfoNotFoundException exc) {
	    if (isTraceOn())
	    trace("checkRoleWriting: exiting", null);
	    return new Integer(RoleStatus.NO_ROLE_WITH_NAME);
	}

	Integer result = checkRoleInt(2,
				      roleName,
				      roleValue,
				      roleInfo,
				      writeChkFlag);

	if (isTraceOn())
	    trace("checkRoleWriting: exiting", null);
	return result;
    }

    /**
     * Sends a notification (RelationNotification) for a relation creation.
     * The notification type is:
     * <P>- RelationNotification.RELATION_BASIC_CREATION if the relation is an
     * object internal to the Relation Service
     * <P>- RelationNotification.RELATION_MBEAN_CREATION if the relation is a
     * MBean added as a relation.
     * <P>The source object is the Relation Service itself.
     * <P>It is called in Relation Service createRelation() and
     * addRelation() methods.
     *
     * @param relationId  relation identifier of the updated relation
     *
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationNotFoundException  if there is no relation for given
     * relation id
     */
    public void sendRelationCreationNotification(String relationId)
	throws IllegalArgumentException,
	       RelationNotFoundException {

	if (relationId == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn())
	    trace("sendRelationCreationNotification: entering", relationId);

	// Message
	StringBuffer ntfMsg = new StringBuffer("Creation of relation ");
	ntfMsg.append(relationId);

	// Can throw RelationNotFoundException
	sendNotificationInt(1,
			    ntfMsg.toString(),
			    relationId,
			    null,
			    null,
			    null,
			    null);

	if (isTraceOn())
	    trace("sendRelationCreationNotification: exiting", null);
	return;
    }

    /**
     * Sends a notification (RelationNotification) for a role update in the
     * given relation. The notification type is:
     * <P>- RelationNotification.RELATION_BASIC_UPDATE if the relation is an
     * object internal to the Relation Service
     * <P>- RelationNotification.RELATION_MBEAN_UPDATE if the relation is a
     * MBean added as a relation.
     * <P>The source object is the Relation Service itself.
     * <P>It is called in relation MBean setRole() (for given role) and
     * setRoles() (for each role) methods (implementation provided in
     * RelationSupport class).
     * <P>It is also called in Relation Service setRole() (for given role) and
     * setRoles() (for each role) methods.
     *
     * @param relationId  relation identifier of the updated relation
     * @param newRole  new role (name and new value)
     * @param oldValue  old role value (List of ObjectName objects)
     *
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationNotFoundException  if there is no relation for given
     * relation id
     */
    public void sendRoleUpdateNotification(String relationId,
					   Role newRole,
					   List<ObjectName> oldValue)
	throws IllegalArgumentException,
	       RelationNotFoundException {

	if (relationId == null ||
	    newRole == null ||
	    oldValue == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (!(oldValue instanceof ArrayList))
	    oldValue = new ArrayList<ObjectName>(oldValue);

	if (isTraceOn()) {
	    String str = new String("relationId " + relationId
				    + ", newRole " + newRole.toString()
				    + ", oldValue "
				    + oldValue.toString());
	    trace("sendRoleUpdateNotification: entering", str);
	}

	String roleName = newRole.getRoleName();
	List<ObjectName> newRoleVal = newRole.getRoleValue();

	// Message
	String newRoleValString = Role.roleValueToString(newRoleVal);
	String oldRoleValString = Role.roleValueToString(oldValue);
	StringBuffer ntfMsg = new StringBuffer("Value of role ");
	ntfMsg.append(roleName);
	ntfMsg.append(" has changed\nOld value:\n");
	ntfMsg.append(oldRoleValString);
	ntfMsg.append("\nNew value:\n");
	ntfMsg.append(newRoleValString);

	// Can throw a RelationNotFoundException
	sendNotificationInt(2,
			    ntfMsg.toString(),
			    relationId,
			    null,
			    roleName,
			    newRoleVal,
			    oldValue);

	if (isTraceOn())
	    trace("sendRoleUpdateNotification: exiting", null);
	return;
    }

    /**
     * Sends a notification (RelationNotification) for a relation removal.
     * The notification type is:
     * <P>- RelationNotification.RELATION_BASIC_REMOVAL if the relation is an
     * object internal to the Relation Service
     * <P>- RelationNotification.RELATION_MBEAN_REMOVAL if the relation is a
     * MBean added as a relation.
     * <P>The source object is the Relation Service itself.
     * <P>It is called in Relation Service removeRelation() method.
     *
     * @param relationId  relation identifier of the updated relation
     * @param unregMBeanList  List of ObjectNames of MBeans expected
     * to be unregistered due to relation removal (can be null)
     *
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationNotFoundException  if there is no relation for given
     * relation id
     */
    public void sendRelationRemovalNotification(String relationId,
						List<ObjectName> unregMBeanList)
	throws IllegalArgumentException,
	       RelationNotFoundException {

	if (relationId == null) {
	    String excMsg = "Invalid parameter";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn()) {
	    StringBuffer strB = new StringBuffer("relationId " + relationId);
	    if (unregMBeanList != null) {
		strB.append(", unregMBeanList "
			    + unregMBeanList.toString());
	    }
	    trace("sendRelationRemovalNotification: entering",
		  strB.toString());
	}

	// Can throw RelationNotFoundException
	sendNotificationInt(3,
			    "Removal of relation " + relationId,
			    relationId,
			    unregMBeanList,
			    null,
			    null,
			    null);

	if (isTraceOn())
	    trace("sendRelationRemovalNotification: exiting", null);
	return;
    }

    /**
     * Handles update of the Relation Service role map for the update of given
     * role in given relation.
     * <P>It is called in relation MBean setRole() (for given role) and
     * setRoles() (for each role) methods (implementation provided in
     * RelationSupport class).
     * <P>It is also called in Relation Service setRole() (for given role) and
     * setRoles() (for each role) methods.
     * <P>To allow the Relation Service to maintain the consistency (in case
     * of MBean unregistration) and to be able to perform queries, this method
     * must be called when a role is updated.
     *
     * @param relationId  relation identifier of the updated relation
     * @param newRole  new role (name and new value)
     * @param oldValue  old role value (List of ObjectName objects)
     *
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationServiceNotRegisteredException  if the Relation
     * Service is not registered in the MBean Server
     * @exception RelationNotFoundException  if no relation for given id.
     */
    public void updateRoleMap(String relationId,
			      Role newRole,
			      List<ObjectName> oldValue)
	throws IllegalArgumentException,
	       RelationServiceNotRegisteredException,
               RelationNotFoundException {

	if (relationId == null ||
	    newRole == null ||
	    oldValue == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn()) {
	    String str = new String("relationId " + relationId
				    + ", newRole " + newRole.toString()
				    + ", oldValue "
				    + oldValue.toString());
	    trace("updateRoleMap: entering", str);
	}

	// Can throw RelationServiceNotRegisteredException
	isActive();

	// Verifies the relation has been added in the Relation Service
	// Can throw a RelationNotFoundException
	Object result = getRelation(relationId);

	String roleName = newRole.getRoleName();
	List<ObjectName> newRoleValue = newRole.getRoleValue();
	// Note: no need to test if oldValue not null before cloning,
	//       tested above.
	List<ObjectName> oldRoleValue =
	    new ArrayList<ObjectName>(oldValue);

	// List of ObjectNames of new referenced MBeans
	List<ObjectName> newRefList = new ArrayList<ObjectName>();

	for (ObjectName currObjName : newRoleValue) {

	    // Checks if this ObjectName was already present in old value
	    // Note: use copy (oldRoleValue) instead of original
	    //       oldValue to speed up, as oldRoleValue is decreased
	    //       by removing unchanged references :)
	    int currObjNamePos = oldRoleValue.indexOf(currObjName);

	    if (currObjNamePos == -1) {
		// New reference to an ObjectName

		// Stores this reference into map
		// Returns true if new reference, false if MBean already
		// referenced
		boolean isNewFlag = addNewMBeanReference(currObjName,
							relationId,
							roleName);

		if (isNewFlag) {
		    // Adds it into list of new reference
		    newRefList.add(currObjName);
		}

	    } else {
		// MBean was already referenced in old value

		// Removes it from old value (local list) to ignore it when
		// looking for remove MBean references
		oldRoleValue.remove(currObjNamePos);
	    }
	}

	// List of ObjectNames of MBeans no longer referenced
	List<ObjectName> obsRefList = new ArrayList<ObjectName>();

	// Each ObjectName remaining in oldRoleValue is an ObjectName no longer
	// referenced in new value
	for (ObjectName currObjName : oldRoleValue) {
	    // Removes MBean reference from map
	    // Returns true if the MBean is no longer referenced in any
	    // relation
	    boolean noLongerRefFlag = removeMBeanReference(currObjName,
							  relationId,
							  roleName,
							  false);

	    if (noLongerRefFlag) {
		// Adds it into list of references to be removed
		obsRefList.add(currObjName);
	    }
	}

	// To avoid having one listener per ObjectName of referenced MBean,
	// and to increase performances, there is only one listener recording
	// all ObjectNames of interest
	updateUnregistrationListener(newRefList, obsRefList);

	if (isTraceOn())
	    trace("updateRoleMap: exiting", null);
	return;
    }

    /**
     * Removes given relation from the Relation Service.
     * <P>A RelationNotification notification is sent, its type being:
     * <P>- RelationNotification.RELATION_BASIC_REMOVAL if the relation was
     * only internal to the Relation Service
     * <P>- RelationNotification.RELATION_MBEAN_REMOVAL if the relation is
     * registered as an MBean.
     * <P>For MBeans referenced in such relation, nothing will be done,
     *
     * @param relationId  relation id of the relation to be removed
     *
     * @exception RelationServiceNotRegisteredException  if the Relation
     * Service is not registered in the MBean Server
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationNotFoundException  if no relation corresponding to
     * given relation id
     */
    public void removeRelation(String relationId)
	throws RelationServiceNotRegisteredException,
	       IllegalArgumentException,
	       RelationNotFoundException {

	// Can throw RelationServiceNotRegisteredException
	isActive();

	if (relationId == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn())
	    trace("removeRelation: entering", relationId);

	// Checks there is a relation with this id
	// Can throw RelationNotFoundException
	Object result = getRelation(relationId);

	// Removes it from listener filter
	if (result instanceof ObjectName) {
	    List<ObjectName> obsRefList = new ArrayList<ObjectName>();
	    obsRefList.add((ObjectName)result);
	    // Can throw a RelationServiceNotRegisteredException
	    updateUnregistrationListener(null, obsRefList);
	}

	// Sends a notification
	// Note: has to be done FIRST as needs the relation to be still in the
	//       Relation Service
	// No RelationNotFoundException as checked above

	// Revisit [cebro] Handle CIM "Delete" and "IfDeleted" qualifiers:
	//   deleting the relation can mean to delete referenced MBeans. In
	//   that case, MBeans to be unregistered are put in a list sent along
	//   with the notification below

	// Can throw a RelationNotFoundException (but detected above)
	sendRelationRemovalNotification(relationId, null);

	// Removes the relation from various internal maps

	//  - MBean reference map
	// Retrieves the MBeans referenced in this relation
	// Note: here we cannot use removeMBeanReference() because it would
	//       require to know the MBeans referenced in the relation. For
	//       that it would be necessary to call 'getReferencedMBeans()'
	//       on the relation itself. Ok if it is an internal one, but if
	//       it is an MBean, it is possible it is already unregistered, so
	//       not available through the MBean Server.
	List<ObjectName> refMBeanList = new ArrayList<ObjectName>();
	// List of MBeans no longer referenced in any relation, to be
	// removed fom the map
	List<ObjectName> nonRefObjNameList = new ArrayList<ObjectName>();

	synchronized(myRefedMBeanObjName2RelIdsMap) {

	    for (ObjectName currRefObjName :
		     myRefedMBeanObjName2RelIdsMap.keySet()) {

		// Retrieves relations where the MBean is referenced
		Map<String,List<String>> relIdMap =
		    myRefedMBeanObjName2RelIdsMap.get(currRefObjName);

		if (relIdMap.containsKey(relationId)) {
		    relIdMap.remove(relationId);
		    refMBeanList.add(currRefObjName);
		}

		if (relIdMap.isEmpty()) {
		    // MBean no longer referenced
		    // Note: do not remove it here because pointed by the
		    //       iterator!
		    nonRefObjNameList.add(currRefObjName);
		}
	    }

	    // Cleans MBean reference map by removing MBeans no longer
	    // referenced
	    for (ObjectName currRefObjName : nonRefObjNameList) {
		myRefedMBeanObjName2RelIdsMap.remove(currRefObjName);
	    }
	}

	// - Relation id to object map
	synchronized(myRelId2ObjMap) {
	    myRelId2ObjMap.remove(relationId);
	}

	if (result instanceof ObjectName) {
	    // - ObjectName to relation id map
	    synchronized(myRelMBeanObjName2RelIdMap) {
		myRelMBeanObjName2RelIdMap.remove((ObjectName)result);
	    }
	}

	// Relation id to relation type name map
	// First retrieves the relation type name
	String relTypeName = null;
	synchronized(myRelId2RelTypeMap) {
	    relTypeName = myRelId2RelTypeMap.get(relationId);
	    myRelId2RelTypeMap.remove(relationId);
	}
	// - Relation type name to relation id map
	synchronized(myRelType2RelIdsMap) {
	    List<String> relIdList = myRelType2RelIdsMap.get(relTypeName);
	    if (relIdList != null) {
		// Can be null if called from removeRelationType()
		relIdList.remove(relationId);
		if (relIdList.isEmpty()) {
		    // No other relation of that type
		    myRelType2RelIdsMap.remove(relTypeName);
		}
	    }
	}

	if (isTraceOn())
	    trace("removeRelation: exiting", null);
	return;
    }

    /**
     * Purges the relations.
     *
     * <P>Depending on the purgeFlag value, this method is either called
     * automatically when a notification is received for the unregistration of
     * an MBean referenced in a relation (if the flag is set to true), or not
     * (if the flag is set to false).
     * <P>In that case it is up to the user to call it to maintain the
     * consistency of the relations. To be kept in mind that if an MBean is
     * unregistered and the purge not done immediately, if the ObjectName is
     * reused and assigned to another MBean referenced in a relation, calling
     * manually this purgeRelations() method will cause trouble, as will
     * consider the ObjectName as corresponding to the unregistered MBean, not
     * seeing the new one.
     *
     * <P>The behavior depends on the cardinality of the role where the
     * unregistered MBean is referenced:
     * <P>- if removing one MBean reference in the role makes its number of
     * references less than the minimum degree, the relation has to be removed.
     * <P>- if the remaining number of references after removing the MBean
     * reference is still in the cardinality range, keep the relation and
     * update it calling its handleMBeanUnregistration() callback.
     *
     * @exception RelationServiceNotRegisteredException  if the Relation
     * Service is not registered in the MBean Server.
     */
    public void purgeRelations()
	throws RelationServiceNotRegisteredException {

	if (isTraceOn())
	    trace("purgeRelations: entering", null);

	// Can throw RelationServiceNotRegisteredException
	isActive();

	// Revisit [cebro] Handle the CIM "Delete" and "IfDeleted" qualifier:
	//    if the unregistered MBean has the "IfDeleted" qualifier,
	//    possible that the relation itself or other referenced MBeans
	//    have to be removed (then a notification would have to be sent
	//    to inform that they should be unregistered.


	// Clones the list of notifications to be able to still receive new
	// notifications while proceeding those ones
	List<MBeanServerNotification> localUnregNtfList;
	synchronized(myUnregNtfList) {
	    localUnregNtfList =
		new ArrayList<MBeanServerNotification>(myUnregNtfList);
	    // Resets list
	    myUnregNtfList = new ArrayList<MBeanServerNotification>();
	}


	// Updates the listener filter to avoid receiving notifications for
	// those MBeans again
	// Makes also a local "myRefedMBeanObjName2RelIdsMap" map, mapping
	// ObjectName -> relId -> roles, to remove the MBean from the global
	// map
	// List of references to be removed from the listener filter
        List<ObjectName> obsRefList = new ArrayList<ObjectName>();
	// Map including ObjectNames for unregistered MBeans, with
	// referencing relation ids and roles
	Map<ObjectName,Map<String,List<String>>> localMBean2RelIdMap =
	    new HashMap<ObjectName,Map<String,List<String>>>();

	synchronized(myRefedMBeanObjName2RelIdsMap) {
	    for (MBeanServerNotification currNtf : localUnregNtfList) {

		ObjectName unregMBeanName = currNtf.getMBeanName();

		// Adds the unregsitered MBean in the list of references to
		// remove from the listener filter
		obsRefList.add(unregMBeanName);

		// Retrieves the associated map of relation ids and roles
		Map<String,List<String>> relIdMap =
		    myRefedMBeanObjName2RelIdsMap.get(unregMBeanName);
		localMBean2RelIdMap.put(unregMBeanName, relIdMap);

		myRefedMBeanObjName2RelIdsMap.remove(unregMBeanName);
	    }
	}

	// Updates the listener
	// Can throw RelationServiceNotRegisteredException
	updateUnregistrationListener(null, obsRefList);

	for (MBeanServerNotification currNtf : localUnregNtfList) {

	    ObjectName unregMBeanName = currNtf.getMBeanName();

	    // Retrieves the relations where the MBean is referenced
	    Map<String,List<String>> localRelIdMap =
		    localMBean2RelIdMap.get(unregMBeanName);

	    // List of relation ids where the unregistered MBean is
	    // referenced
	    for (String currRelId : localRelIdMap.keySet()) {

		// List of roles of the relation where the MBean is
		// referenced
		List<String> localRoleNameList = localRelIdMap.get(currRelId);

		// Checks if the relation has to be removed or not,
		// regarding expected minimum role cardinality and current
		// number of references after removal of the current one
		// If the relation is kept, calls
		// handleMBeanUnregistration() callback of the relation to
		// update it
		//
		// Can throw RelationServiceNotRegisteredException
		//
		// Shall not throw RelationNotFoundException,
		// RoleNotFoundException, MBeanException
		try {
		    handleReferenceUnregistration(currRelId,
						  unregMBeanName,
						  localRoleNameList);
		} catch (RelationNotFoundException exc1) {
		    throw new RuntimeException(exc1.getMessage());
		} catch (RoleNotFoundException exc2) {
		    throw new RuntimeException(exc2.getMessage());
		}
	    }
	}

	if (isTraceOn())
	    trace("purgeRelations: exiting", null);
	return;
    }

    /**
     * Retrieves the relations where a given MBean is referenced.
     * <P>This corresponds to the CIM "References" and "ReferenceNames"
     * operations.
     *
     * @param mbeanName  ObjectName of MBean
     * @param relationTypeName  can be null; if specified, only the relations
     * of that type will be considered in the search. Else all relation types
     * are considered.
     * @param roleName  can be null; if specified, only the relations
     * where the MBean is referenced in that role will be returned. Else all
     * roles are considered.
     *
     * @return an HashMap, where the keys are the relation ids of the relations
     * where the MBean is referenced, and the value is, for each key,
     * an ArrayList of role names (as an MBean can be referenced in several
     * roles in the same relation).
     *
     * @exception IllegalArgumentException  if null parameter
     */
    public Map<String,List<String>>
	findReferencingRelations(ObjectName mbeanName,
				 String relationTypeName,
				 String roleName)
	    throws IllegalArgumentException {

	if (mbeanName == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn()) {
	    String str = new String("mbeanName " + mbeanName.toString()
				    + ", relationTypeName " + relationTypeName
				    + ", roleName " + roleName);
	    trace("findReferencingRelations: entering", str);
	}

	Map<String,List<String>> result = new HashMap<String,List<String>>();

	synchronized(myRefedMBeanObjName2RelIdsMap) {

	    // Retrieves the relations referencing the MBean
	    Map<String,List<String>> relId2RoleNamesMap =
		myRefedMBeanObjName2RelIdsMap.get(mbeanName);

	    if (relId2RoleNamesMap != null) {

		// Relation Ids where the MBean is referenced
		Set<String> allRelIdSet = relId2RoleNamesMap.keySet();

		// List of relation ids of interest regarding the selected
		// relation type
		List<String> relIdList = null;
		if (relationTypeName == null) {
		    // Considers all relations
		    relIdList = new ArrayList<String>(allRelIdSet);

		} else {

		    relIdList = new ArrayList<String>();

		    // Considers only the relation ids for relations of given
		    // type
		    for (String currRelId : allRelIdSet) {

			// Retrieves its relation type
			String currRelTypeName = null;
			synchronized(myRelId2RelTypeMap) {
			    currRelTypeName =
				myRelId2RelTypeMap.get(currRelId);
			}

			if (currRelTypeName.equals(relationTypeName)) {

			    relIdList.add(currRelId);

			}
		    }
		}

		// Now looks at the roles where the MBean is expected to be
		// referenced

		for (String currRelId : relIdList) {
		    // Retrieves list of role names where the MBean is
		    // referenced
		    List<String> currRoleNameList =
			relId2RoleNamesMap.get(currRelId);

		    if (roleName == null) {
			// All roles to be considered
			// Note: no need to test if list not null before
			//       cloning, MUST be not null else bug :(
			result.put(currRelId,
				   new ArrayList<String>(currRoleNameList));

		    }  else if (currRoleNameList.contains(roleName)) {
			// Filters only the relations where the MBean is
			// referenced in // given role
			List<String> dummyList = new ArrayList<String>();
			dummyList.add(roleName);
			result.put(currRelId, dummyList);
		    }
		}
	    }
	}

	if (isTraceOn())
	    trace("findReferencingRelations: exiting", null);
	return result;
    }

    /**
     * Retrieves the MBeans associated to given one in a relation.
     * <P>This corresponds to CIM Associators and AssociatorNames operations.
     *
     * @param mbeanName  ObjectName of MBean
     * @param relationTypeName  can be null; if specified, only the relations
     * of that type will be considered in the search. Else all
     * relation types are considered.
     * @param roleName  can be null; if specified, only the relations
     * where the MBean is referenced in that role will be considered. Else all
     * roles are considered.
     *
     * @return an HashMap, where the keys are the ObjectNames of the MBeans
     * associated to given MBean, and the value is, for each key, an ArrayList
     * of the relation ids of the relations where the key MBean is
     * associated to given one (as they can be associated in several different
     * relations).
     *
     * @exception IllegalArgumentException  if null parameter
     */
    public Map<ObjectName,List<String>>
	findAssociatedMBeans(ObjectName mbeanName,
			     String relationTypeName,
			     String roleName)
	    throws IllegalArgumentException {

	if (mbeanName == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn()) {
	    String str = new String("mbeanName " + mbeanName.toString()
				    + ", relationTypeName " + relationTypeName
				    + ", roleName " + roleName);
	    trace("findAssociatedMBeans: entering", str);
	}

	// Retrieves the map <relation id> -> <role names> for those
	// criterias
	Map<String,List<String>> relId2RoleNamesMap =
	    findReferencingRelations(mbeanName,
				     relationTypeName,
				     roleName);

	Map<ObjectName,List<String>> result =
	    new HashMap<ObjectName,List<String>>();

	for (String currRelId : relId2RoleNamesMap.keySet()) {

	    // Retrieves ObjectNames of MBeans referenced in this relation
	    //
	    // Shall not throw a RelationNotFoundException if incorrect status
	    // of maps :(
	    Map<ObjectName,List<String>> objName2RoleNamesMap;
	    try {
		objName2RoleNamesMap = getReferencedMBeans(currRelId);
	    } catch (RelationNotFoundException exc) {
		throw new RuntimeException(exc.getMessage());
	    }

	    // For each MBean associated to given one in a relation, adds the
	    // association <ObjectName> -> <relation id> into result map
	    for (ObjectName currObjName : objName2RoleNamesMap.keySet()) {

		if (!(currObjName.equals(mbeanName))) {

		    // Sees if this MBean is already associated to the given
		    // one in another relation
		    List<String> currRelIdList = result.get(currObjName);
		    if (currRelIdList == null) {

			currRelIdList = new ArrayList<String>();
			currRelIdList.add(currRelId);
			result.put(currObjName, currRelIdList);

		    } else {
			currRelIdList.add(currRelId);
		    }
		}
	    }
	}

	if (isTraceOn())
	    trace("findReferencingRelations: exiting", null);
	return result;
    }

    /**
     * Returns the relation ids for relations of the given type.
     *
     * @param relationTypeName  relation type name
     *
     * @return an ArrayList of relation ids.
     *
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationTypeNotFoundException  if there is no relation type
     * with that name.
     */
    public List<String> findRelationsOfType(String relationTypeName)
	throws IllegalArgumentException,
               RelationTypeNotFoundException {

	if (relationTypeName == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn())
	    trace("findRelationsOfType: entering", relationTypeName);

	// Can throw RelationTypeNotFoundException
	RelationType relType = getRelationType(relationTypeName);

	List<String> result;
	synchronized(myRelType2RelIdsMap) {
	    List<String> result1 = myRelType2RelIdsMap.get(relationTypeName);
	    if (result1 == null)
		result = new ArrayList<String>();
	    else
		result = new ArrayList<String>(result1);
	}

	if (isTraceOn())
	    trace("findRelationsOfType: exiting", null);
	return result;
    }

    /**
     * Retrieves role value for given role name in given relation.
     *
     * @param relationId  relation id
     * @param roleName  name of role
     *
     * @return the ArrayList of ObjectName objects being the role value
     *
     * @exception RelationServiceNotRegisteredException  if the Relation
     * Service is not registered
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationNotFoundException  if no relation with given id
     * @exception RoleNotFoundException  if:
     * <P>- there is no role with given name
     * <P>or
     * <P>- the role is not readable.
     *
     * @see #setRole
     */
    public List<ObjectName> getRole(String relationId,
				    String roleName)
	throws RelationServiceNotRegisteredException,
	       IllegalArgumentException,
               RelationNotFoundException,
               RoleNotFoundException {

	if (relationId == null || roleName == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn()) {
	    String str = "relationId " + relationId
				    + ", roleName " + roleName;
	    trace("getRole: entering", str);
	}

	// Can throw RelationServiceNotRegisteredException
	isActive();

	// Can throw a RelationNotFoundException
	Object relObj = getRelation(relationId);

	List<ObjectName> result;

	if (relObj instanceof RelationSupport) {
	    // Internal relation
	    // Can throw RoleNotFoundException
	    result = (List<ObjectName>)
		((RelationSupport)relObj).getRoleInt(roleName,
						     true,
						     this,
						     false);

	} else {
	    // Relation MBean
	    Object[] params = new Object[1];
	    params[0] = roleName;
	    String[] signature = new String[1];
	    signature[0] = "java.lang.String";
	    // Can throw MBeanException wrapping a RoleNotFoundException:
	    // throw wrapped exception
	    //
	    // Shall not throw InstanceNotFoundException or ReflectionException
	    try {
		List<ObjectName> invokeResult = (List<ObjectName>)
		    (myMBeanServer.invoke(((ObjectName)relObj),
					  "getRole",
					  params,
					  signature));
		if (invokeResult == null || invokeResult instanceof ArrayList)
		    result = invokeResult;
		else
		    result = new ArrayList<ObjectName>(invokeResult);
	    } catch (InstanceNotFoundException exc1) {
		throw new RuntimeException(exc1.getMessage());
	    } catch (ReflectionException exc2) {
		throw new RuntimeException(exc2.getMessage());
	    } catch (MBeanException exc3) {
		Exception wrappedExc = exc3.getTargetException();
		if (wrappedExc instanceof RoleNotFoundException) {
		    throw ((RoleNotFoundException)wrappedExc);
		} else {
		    throw new RuntimeException(wrappedExc.getMessage());
		}
	    }
	}

	if (isTraceOn())
	    trace("getRole: exiting", null);
	return result;
    }

    /**
     * Retrieves values of roles with given names in given relation.
     *
     * @param relationId  relation id
     * @param roleNameArray  array of names of roles to be retrieved
     *
     * @return a RoleResult object, including a RoleList (for roles
     * successfully retrieved) and a RoleUnresolvedList (for roles not
     * retrieved).
     *
     * @exception RelationServiceNotRegisteredException  if the Relation
     * Service is not registered in the MBean Server
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationNotFoundException  if no relation with given id
     *
     * @see #setRoles
     */
    public RoleResult getRoles(String relationId,
			       String[] roleNameArray)
	throws RelationServiceNotRegisteredException,
	       IllegalArgumentException,
	       RelationNotFoundException {

	if (relationId == null || roleNameArray == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn())
	    trace("getRoles: entering", relationId);

	// Can throw RelationServiceNotRegisteredException
	isActive();

	// Can throw a RelationNotFoundException
	Object relObj = getRelation(relationId);

	RoleResult result = null;

	if (relObj instanceof RelationSupport) {
	    // Internal relation
	    result = ((RelationSupport)relObj).getRolesInt(roleNameArray,
							true,
							this);
	} else {
	    // Relation MBean
	    Object[] params = new Object[1];
	    params[0] = roleNameArray;
	    String[] signature = new String[1];
	    try {
		signature[0] = (roleNameArray.getClass()).getName();
	    } catch (Exception exc) {
		// OK : This is an array of java.lang.String
		//      so this should never happen...
	    }
	    // Shall not throw InstanceNotFoundException, ReflectionException
	    // or MBeanException
	    try {
		result = (RoleResult)
		    (myMBeanServer.invoke(((ObjectName)relObj),
					  "getRoles",
					  params,
					  signature));
	    } catch (InstanceNotFoundException exc1) {
		throw new RuntimeException(exc1.getMessage());
	    } catch (ReflectionException exc2) {
		throw new RuntimeException(exc2.getMessage());
	    } catch (MBeanException exc3) {
		throw new
		    RuntimeException((exc3.getTargetException()).getMessage());
	    }
	}

	if (isTraceOn())
	    trace("getRoles: exiting", null);
	return result;
    }

    /**
     * Returns all roles present in the relation.
     *
     * @param relationId  relation id
     *
     * @return a RoleResult object, including a RoleList (for roles
     * successfully retrieved) and a RoleUnresolvedList (for roles not
     * readable).
     *
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationNotFoundException  if no relation for given id
     * @exception RelationServiceNotRegisteredException  if the Relation
     * Service is not registered in the MBean Server
     */
    public RoleResult getAllRoles(String relationId)
        throws IllegalArgumentException,
	       RelationNotFoundException,
               RelationServiceNotRegisteredException {

	if (relationId == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn())
	    trace("getAllRoles: entering", relationId);

	// Can throw a RelationNotFoundException
	Object relObj = getRelation(relationId);

	RoleResult result = null;

	if (relObj instanceof RelationSupport) {
	    // Internal relation
	    result = ((RelationSupport)relObj).getAllRolesInt(true, this);

	} else {
	    // Relation MBean
	    // Shall not throw any Exception
	    try {
		result = (RoleResult)
		    (myMBeanServer.getAttribute(((ObjectName)relObj),
                                                "AllRoles"));
	    } catch (Exception exc) {
		throw new RuntimeException(exc.getMessage());
	    }
	}

	if (isTraceOn())
	    trace("getAllRoles: exiting", null);
	return result;
    }

    /**
     * Retrieves the number of MBeans currently referenced in the given role.
     *
     * @param relationId  relation id
     * @param roleName  name of role
     *
     * @return the number of currently referenced MBeans in that role
     *
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationNotFoundException  if no relation with given id
     * @exception RoleNotFoundException  if there is no role with given name
     */
    public Integer getRoleCardinality(String relationId,
				      String roleName)
	throws IllegalArgumentException,
               RelationNotFoundException,
               RoleNotFoundException {

	if (relationId == null || roleName == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn()) {
	    String str = "relationId " + relationId
				    + ", roleName " + roleName;
	    trace("getRoleCardinality: entering", str);
	}

	// Can throw a RelationNotFoundException
	Object relObj = getRelation(relationId);

	Integer result = null;

	if (relObj instanceof RelationSupport) {
	    // Internal relation
	    // Can throw RoleNotFoundException
	    result = (Integer)
		(((RelationSupport)relObj).getRoleCardinality(roleName));

	} else {
	    // Relation MBean
	    Object[] params = new Object[1];
	    params[0] = roleName;
	    String[] signature = new String[1];
	    signature[0] = "java.lang.String";
	    // Can throw MBeanException wrapping RoleNotFoundException:
	    // throw wrapped exception
	    //
	    // Shall not throw InstanceNotFoundException or ReflectionException
	    try {
		result = (Integer)
		    (myMBeanServer.invoke(((ObjectName)relObj),
					  "getRoleCardinality",
					  params,
					  signature));
	    } catch (InstanceNotFoundException exc1) {
		throw new RuntimeException(exc1.getMessage());
	    } catch (ReflectionException exc2) {
		throw new RuntimeException(exc2.getMessage());
	    } catch (MBeanException exc3) {
		Exception wrappedExc = exc3.getTargetException();
		if (wrappedExc instanceof RoleNotFoundException) {
		    throw ((RoleNotFoundException)wrappedExc);
		} else {
		    throw new RuntimeException(wrappedExc.getMessage());
		}
	    }
	}

	if (isTraceOn())
	    trace("getRoleCardinality: exiting", null);
	return result;
    }

    /**
     * Sets the given role in given relation.
     * <P>Will check the role according to its corresponding role definition
     * provided in relation's relation type
     * <P>The Relation Service will keep track of the change to keep the
     * consistency of relations by handling referenced MBean unregistrations.
     *
     * @param relationId  relation id
     * @param role  role to be set (name and new value)
     *
     * @exception RelationServiceNotRegisteredException  if the Relation
     * Service is not registered in the MBean Server
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationNotFoundException  if no relation with given id
     * @exception RoleNotFoundException  if the role does not exist or is not
     * writable
     * @exception InvalidRoleValueException  if value provided for role is not
     * valid:
     * <P>- the number of referenced MBeans in given value is less than
     * expected minimum degree
     * <P>or
     * <P>- the number of referenced MBeans in provided value exceeds expected
     * maximum degree
     * <P>or
     * <P>- one referenced MBean in the value is not an Object of the MBean
     * class expected for that role
     * <P>or
     * <P>- an MBean provided for that role does not exist
     *
     * @see #getRole
     */
    public void setRole(String relationId,
			Role role)
	throws RelationServiceNotRegisteredException,
	       IllegalArgumentException,
	       RelationNotFoundException,
	       RoleNotFoundException,
	       InvalidRoleValueException {

	if (relationId == null || role == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn()) {
	    String str = new String("relationId " + relationId
				    + ", role " + role.toString());
	    trace("setRole: entering", str);
	}

	// Can throw RelationServiceNotRegisteredException
	isActive();

	// Can throw a RelationNotFoundException
	Object relObj = getRelation(relationId);

	if (relObj instanceof RelationSupport) {
	    // Internal relation
	    // Can throw RoleNotFoundException,
	    // InvalidRoleValueException and
	    // RelationServiceNotRegisteredException
	    //
	    // Shall not throw RelationTypeNotFoundException
	    // (as relation exists in the RS, its relation type is known)
	    try {
		((RelationSupport)relObj).setRoleInt(role,
						  true,
						  this,
						  false);

	    } catch (RelationTypeNotFoundException exc) {
		throw new RuntimeException(exc.getMessage());
	    }

	} else {
	    // Relation MBean
	    Object[] params = new Object[1];
	    params[0] = role;
	    String[] signature = new String[1];
	    signature[0] = "javax.management.relation.Role";
	    // Can throw MBeanException wrapping RoleNotFoundException,
	    // InvalidRoleValueException
	    //
	    // Shall not MBeanException wrapping an MBeanException wrapping
	    // RelationTypeNotFoundException, or ReflectionException, or
	    // InstanceNotFoundException
	    try {
		myMBeanServer.setAttribute(((ObjectName)relObj),
                                           new Attribute("Role", role));

	    } catch (InstanceNotFoundException exc1) {
		throw new RuntimeException(exc1.getMessage());
	    } catch (ReflectionException exc3) {
		throw new RuntimeException(exc3.getMessage());
	    } catch (MBeanException exc2) {
		Exception wrappedExc = exc2.getTargetException();
		if (wrappedExc instanceof RoleNotFoundException) {
		    throw ((RoleNotFoundException)wrappedExc);
		} else if (wrappedExc instanceof InvalidRoleValueException) {
		    throw ((InvalidRoleValueException)wrappedExc);
		} else {
		    throw new RuntimeException(wrappedExc.getMessage());

		}
	    } catch (AttributeNotFoundException exc4) {
              throw new RuntimeException(exc4.getMessage());
            } catch (InvalidAttributeValueException exc5) {
              throw new RuntimeException(exc5.getMessage());
            }
	}

	if (isTraceOn())
	    trace("setRole: exiting", null);
	return;
    }

    /**
     * Sets the given roles in given relation.
     * <P>Will check the role according to its corresponding role definition
     * provided in relation's relation type
     * <P>The Relation Service keeps track of the changes to keep the
     * consistency of relations by handling referenced MBean unregistrations.
     *
     * @param relationId  relation id
     * @param roleList  list of roles to be set
     *
     * @return a RoleResult object, including a RoleList (for roles
     * successfully set) and a RoleUnresolvedList (for roles not
     * set).
     *
     * @exception RelationServiceNotRegisteredException  if the Relation
     * Service is not registered in the MBean Server
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationNotFoundException  if no relation with given id
     *
     * @see #getRoles
     */
    public RoleResult setRoles(String relationId,
			       RoleList roleList)
	throws RelationServiceNotRegisteredException,
	       IllegalArgumentException,
               RelationNotFoundException {

	if (relationId == null || roleList == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn()) {
	    String str = new String("relationId " + relationId
				    + ", roleList "
				    + roleList.toString());
	    trace("setRoles: entering", str);
	}

	// Can throw RelationServiceNotRegisteredException
	isActive();

	// Can throw a RelationNotFoundException
	Object relObj = getRelation(relationId);

	RoleResult result = null;

	if (relObj instanceof RelationSupport) {
	    // Internal relation
	    // Can throw RelationServiceNotRegisteredException
	    //
	    // Shall not throw RelationTypeNotFoundException (as relation is
	    // known, its relation type exists)
	    try {
		result = ((RelationSupport)relObj).setRolesInt(roleList,
							    true,
							    this);
	    } catch (RelationTypeNotFoundException exc) {
		throw new RuntimeException(exc.getMessage());
	    }

	} else {
	    // Relation MBean
	    Object[] params = new Object[1];
	    params[0] = roleList;
	    String[] signature = new String[1];
	    signature[0] = "javax.management.relation.RoleList";
	    // Shall not throw InstanceNotFoundException or an MBeanException
	    // or ReflectionException
	    try {
		result = (RoleResult)
		    (myMBeanServer.invoke(((ObjectName)relObj),
					  "setRoles",
					  params,
					  signature));
	    } catch (InstanceNotFoundException exc1) {
		throw new RuntimeException(exc1.getMessage());
	    } catch (ReflectionException exc3) {
		throw new RuntimeException(exc3.getMessage());
	    } catch (MBeanException exc2) {
		throw new
		    RuntimeException((exc2.getTargetException()).getMessage());
	    }
	}

	if (isTraceOn())
	    trace("setRoles: exiting", null);
	return result;
    }

    /**
     * Retrieves MBeans referenced in the various roles of the relation.
     *
     * @param relationId  relation id
     *
     * @return a HashMap mapping:
     * <P> ObjectName -> ArrayList of String (role
     * names)
     *
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationNotFoundException  if no relation for given
     * relation id
     */
    public Map<ObjectName,List<String>>
	getReferencedMBeans(String relationId)
	    throws IllegalArgumentException,
	RelationNotFoundException {

	if (relationId == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn())
	    trace("getReferencedMBeans: entering", relationId);

	// Can throw a RelationNotFoundException
	Object relObj = getRelation(relationId);

	Map<ObjectName,List<String>> result;

	if (relObj instanceof RelationSupport) {
	    // Internal relation
	    result = ((RelationSupport)relObj).getReferencedMBeans();

	} else {
	    // Relation MBean
	    // No Exception
	    try {
		result = (Map<ObjectName,List<String>>)
		    (myMBeanServer.getAttribute(((ObjectName)relObj),
                                                "ReferencedMBeans"));
	    } catch (Exception exc) {
		throw new RuntimeException(exc.getMessage());
	    }
	}

	if (isTraceOn())
	    trace("getReferencedMBeans: exiting", null);
	return result;
    }

    /**
     * Returns name of associated relation type for given relation.
     *
     * @param relationId  relation id
     *
     * @return the name of the associated relation type.
     *
     * @exception IllegalArgumentException  if null parameter
     * @exception RelationNotFoundException  if no relation for given
     * relation id
     */
    public String getRelationTypeName(String relationId)
	throws IllegalArgumentException,
	       RelationNotFoundException {

	if (relationId == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn())
	    trace("getRelationTypeName: entering", relationId);

	// Can throw a RelationNotFoundException
	Object relObj = getRelation(relationId);

	String result = null;

	if (relObj instanceof RelationSupport) {
	    // Internal relation
	    result = ((RelationSupport)relObj).getRelationTypeName();

	} else {
	    // Relation MBean
	    // No Exception
	    try {
		result = (String)
		    (myMBeanServer.getAttribute(((ObjectName)relObj),
                                                "RelationTypeName"));
	    } catch (Exception exc) {
		throw new RuntimeException(exc.getMessage());
	    }
	}

	if (isTraceOn())
	    trace("getRelationTypeName: exiting", null);
	return result;
    }

    //
    // NotificationListener Interface
    //

    /**
     * Invoked when a JMX notification occurs.
     * Currently handles notifications for unregistration of MBeans, either
     * referenced in a relation role or being a relation itself.
     *
     * @param notif  The notification.
     * @param handback  An opaque object which helps the listener to
     * associate information regarding the MBean emitter (can be null).
     */
    public void handleNotification(Notification notif,
				   Object handback) {

	if (notif == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isTraceOn())
	    trace("handleNotification: entering", notif.toString());

	if (notif instanceof MBeanServerNotification) {

	    MBeanServerNotification mbsNtf = (MBeanServerNotification) notif;
	    String ntfType = notif.getType();

	    if (ntfType.equals(
		       MBeanServerNotification.UNREGISTRATION_NOTIFICATION )) {
		ObjectName mbeanName =
		    ((MBeanServerNotification)notif).getMBeanName();

		// Note: use a flag to block access to
		// myRefedMBeanObjName2RelIdsMap only for a quick access
		boolean isRefedMBeanFlag = false;
		synchronized(myRefedMBeanObjName2RelIdsMap) {

		    if (myRefedMBeanObjName2RelIdsMap.containsKey(mbeanName)) {
			// Unregistration of a referenced MBean
			synchronized(myUnregNtfList) {
			    myUnregNtfList.add(mbsNtf);
			}
			isRefedMBeanFlag = true;
		    }
		    if (isRefedMBeanFlag && myPurgeFlag) {
			// Immediate purge
			// Can throw RelationServiceNotRegisteredException
			// but assume that will be fine :)
			try {
			    purgeRelations();
			} catch (Exception exc) {
			    throw new RuntimeException(exc.getMessage());
			}
		    }
		}

		// Note: do both tests as a relation can be an MBean and be
		//       itself referenced in another relation :)
		String relId = null;
		synchronized(myRelMBeanObjName2RelIdMap){
		    relId = (String)
			(myRelMBeanObjName2RelIdMap.get(mbeanName));
		}
		if (relId != null) {
		    // Unregistration of a relation MBean
		    // Can throw RelationTypeNotFoundException,
		    // RelationServiceNotRegisteredException
		    //
		    // Shall not throw RelationTypeNotFoundException or
		    // InstanceNotFoundException
		    try {
			removeRelation(relId);
		    } catch (Exception exc) {
			throw new RuntimeException(exc.getMessage());
		    }
		}
	    }
	}

	if (isTraceOn())
	    trace("handleNotification: exiting", null);
	return;
    }

    //
    // NotificationBroadcaster interface
    //

    /**
     * Returns a NotificationInfo object containing the name of the Java class
     * of the notification and the notification types sent.
     */
    public MBeanNotificationInfo[] getNotificationInfo() {

	if (isTraceOn())
	    trace("getNotificationInfo: entering", null);

	MBeanNotificationInfo[] ntfInfoArray =
	    new MBeanNotificationInfo[1];

	String ntfClass = "javax.management.relation.RelationNotification";

	String[] ntfTypes = new String[] {
	    RelationNotification.RELATION_BASIC_CREATION,
	    RelationNotification.RELATION_MBEAN_CREATION,
	    RelationNotification.RELATION_BASIC_UPDATE,
	    RelationNotification.RELATION_MBEAN_UPDATE,
	    RelationNotification.RELATION_BASIC_REMOVAL,
	    RelationNotification.RELATION_MBEAN_REMOVAL,
	};

	String ntfDesc = "Sent when a relation is created, updated or deleted.";

	MBeanNotificationInfo ntfInfo =
	    new MBeanNotificationInfo(ntfTypes, ntfClass, ntfDesc);

	if (isTraceOn())
	    trace("getNotificationInfo: exiting", null);
	return new MBeanNotificationInfo[] {ntfInfo};
    }

    //
    // Misc
    //

    // Adds given object as a relation type.
    //
    // -param relationTypeObj  relation type object
    //
    // -exception IllegalArgumentException  if null parameter
    // -exception InvalidRelationTypeException  if there is already a relation
    //  type with that name
    private void addRelationTypeInt(RelationType relationTypeObj)
	throws IllegalArgumentException,
	       InvalidRelationTypeException {

	if (relationTypeObj == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isDebugOn())
	    debug("addRelationTypeInt: entering", null);

	String relTypeName = relationTypeObj.getRelationTypeName();

	// Checks that there is not already a relation type with that name
	// existing in the Relation Service
	try {
	    // Can throw a RelationTypeNotFoundException (in fact should ;)
	    RelationType relType = getRelationType(relTypeName);

	    if (relType != null) {
		String excMsg = "There is already a relation type in the Relation Service with name ";
		StringBuffer excMsgStrB = new StringBuffer(excMsg);
		excMsgStrB.append(relTypeName);
		throw new InvalidRelationTypeException(excMsgStrB.toString());
	    }

	} catch (RelationTypeNotFoundException exc) {
	    // OK : The RelationType could not be found.
	}

	// Adds the relation type
	synchronized(myRelType2ObjMap) {
	    myRelType2ObjMap.put(relTypeName, relationTypeObj);
	}

	if (relationTypeObj instanceof RelationTypeSupport) {
	    ((RelationTypeSupport)relationTypeObj).setRelationServiceFlag(true);
	}

	if (isDebugOn())
	    debug("addRelationTypeInt: exiting", null);
	return;
     }

    // Retrieves relation type with given name
    //
    // -param relationTypeName  expected name of a relation type created in the
    //  Relation Service
    //
    // -return RelationType object corresponding to given name
    //
    // -exception IllegalArgumentException  if null parameter
    // -exception RelationTypeNotFoundException  if no relation type for that
    //  name created in Relation Service
    //
    RelationType getRelationType(String relationTypeName)
	throws IllegalArgumentException,
	       RelationTypeNotFoundException {

	if (relationTypeName == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isDebugOn())
	    debug("getRelationType: entering", relationTypeName);

	// No null relation type accepted, so can use get()
	RelationType relType = null;
	synchronized(myRelType2ObjMap) {
	    relType = (RelationType)(myRelType2ObjMap.get(relationTypeName));
	}

	if (relType == null) {
	    String excMsg = "No relation type created in the Relation Service with the name ";
	    StringBuffer excMsgStrB = new StringBuffer(excMsg);
	    excMsgStrB.append(relationTypeName);
	    throw new RelationTypeNotFoundException(excMsgStrB.toString());
	}

	if (isDebugOn())
	    debug("getRelationType: exiting", null);
	return relType;
    }

    // Retrieves relation corresponding to given relation id.
    // Returns either:
    // - a RelationSupport object if the relation is internal
    // or
    // - the ObjectName of the corresponding MBean
    //
    // -param relationId  expected relation id
    //
    // -return RelationSupport object or ObjectName of relation with given id
    //
    // -exception IllegalArgumentException  if null parameter
    // -exception RelationNotFoundException  if no relation for that
    //  relation id created in Relation Service
    //
    Object getRelation(String relationId)
	throws IllegalArgumentException,
	       RelationNotFoundException {

	if (relationId == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isDebugOn())
	    debug("getRelation: entering", relationId);

	// No null relation  accepted, so can use get()
	Object rel = null;
	synchronized(myRelId2ObjMap) {
	    rel = myRelId2ObjMap.get(relationId);
	}

	if (rel == null) {
	    String excMsg = "No relation associated to relation id " + relationId;
	    throw new RelationNotFoundException(excMsg);
	}

	if (isDebugOn())
	    debug("getRelation: exiting", null);
	return rel;
    }

    // Adds a new MBean reference (reference to an ObjectName) in the
    // referenced MBean map (myRefedMBeanObjName2RelIdsMap).
    //
    // -param objectName  ObjectName of new referenced MBean
    // -param relationId  relation id of the relation where the MBean is
    //  referenced
    // -param roleName  name of the role where the MBean is referenced
    //
    // -return boolean:
    //  - true  if the MBean was not referenced before, so really a new
    //    reference
    //  - false else
    //
    // -exception IllegalArgumentException  if null parameter
    private boolean addNewMBeanReference(ObjectName objectName,
					 String relationId,
					 String roleName)
	throws IllegalArgumentException {

	if (objectName == null ||
	    relationId == null ||
	    roleName == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isDebugOn()) {
	    String str = new String("objectName " + objectName.toString()
				    + ", relationId " + relationId
				    + ", roleName " + roleName);
	    debug("addNewMBeanReference: entering", str);
	}

	boolean isNewFlag = false;

	synchronized(myRefedMBeanObjName2RelIdsMap) {

	    // Checks if the MBean was already referenced
	    // No null value allowed, use get() directly
	    Map<String,List<String>> mbeanRefMap =
		myRefedMBeanObjName2RelIdsMap.get(objectName);

	    if (mbeanRefMap == null) {
		// MBean not referenced in any relation yet

		isNewFlag = true;

		// List of roles where the MBean is referenced in given
		// relation
		List<String> roleNames = new ArrayList<String>();
		roleNames.add(roleName);

		// Map of relations where the MBean is referenced
		mbeanRefMap = new HashMap<String,List<String>>();
		mbeanRefMap.put(relationId, roleNames);

		myRefedMBeanObjName2RelIdsMap.put(objectName, mbeanRefMap);

	    } else {
		// MBean already referenced in at least another relation
		// Checks if already referenced in another role in current
		// relation
		List<String> roleNames = mbeanRefMap.get(relationId);

		if (roleNames == null) {
		    // MBean not referenced in current relation

		    // List of roles where the MBean is referenced in given
		    // relation
		    roleNames = new ArrayList<String>();
		    roleNames.add(roleName);

		    // Adds new reference done in current relation
		    mbeanRefMap.put(relationId, roleNames);

		} else {
		    // MBean already referenced in current relation in another
		    // role
		    // Adds new reference done
		    roleNames.add(roleName);
		}
	    }
	}

	if (isDebugOn())
	    debug("addNewMBeanReference: exiting", null);
	return isNewFlag;
    }

    // Removes an obsolete MBean reference (reference to an ObjectName) in
    // the referenced MBean map (myRefedMBeanObjName2RelIdsMap).
    //
    // -param objectName  ObjectName of MBean no longer referenced
    // -param relationId  relation id of the relation where the MBean was
    //  referenced
    // -param roleName  name of the role where the MBean was referenced
    // -param allRolesFlag  flag, if true removes reference to MBean for all
    //  roles in the relation, not only for the one above
    //
    // -return boolean:
    //  - true  if the MBean is no longer reference in any relation
    //  - false else
    //
    // -exception IllegalArgumentException  if null parameter
    private boolean removeMBeanReference(ObjectName objectName,
					 String relationId,
					 String roleName,
					 boolean allRolesFlag)
	throws IllegalArgumentException {

	if (objectName == null ||
	    relationId == null ||
	    roleName == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isDebugOn()) {
	    String str = new String("objectName " + objectName.toString()
				    + ", relationId " + relationId
				    + ", roleName " + roleName
				    + ", allRolesFlag " + allRolesFlag);
	    debug("removeMBeanReference: entering", str);
	}

	boolean noLongerRefFlag = false;

	synchronized(myRefedMBeanObjName2RelIdsMap) {

	    // Retrieves the set of relations (designed via their relation ids)
	    // where the MBean is referenced
	    // Note that it is possible that the MBean has already been removed
	    // from the internal map: this is the case when the MBean is
	    // unregistered, the role is updated, then we arrive here.
	    HashMap mbeanRefMap = (HashMap)
		(myRefedMBeanObjName2RelIdsMap.get(objectName));

	    if (mbeanRefMap == null) {
		// The MBean is no longer referenced
		if (isDebugOn())
		    debug("removeMBeanReference: exiting", null);
		return true;
	    }

	    ArrayList roleNames = new ArrayList();
	    if (!allRolesFlag) {
		// Now retrieves the roles of current relation where the MBean
		// was referenced
		roleNames = (ArrayList)(mbeanRefMap.get(relationId));

		// Removes obsolete reference to role
		int obsRefIdx = roleNames.indexOf(roleName);
		if (obsRefIdx != -1) {
		    roleNames.remove(obsRefIdx);
		}
	    }

	    // Checks if there is still at least one role in current relation
	    // where the MBean is referenced
	    if (roleNames.isEmpty() || allRolesFlag) {
		// MBean no longer referenced in current relation: removes
		// entry
		mbeanRefMap.remove(relationId);
	    }

	    // Checks if the MBean is still referenced in at least on relation
	    if (mbeanRefMap.isEmpty()) {
		// MBean no longer referenced in any relation: removes entry
		myRefedMBeanObjName2RelIdsMap.remove(objectName);
		noLongerRefFlag = true;
	    }
	}

	if (isDebugOn())
	    debug("removeMBeanReference: exiting", null);
	return noLongerRefFlag;
    }

    // Updates the listener registered to the MBean Server to be informed of
    // referenced MBean unregistrations
    //
    // -param newRefList  ArrayList of ObjectNames for new references done
    //  to MBeans (can be null)
    // -param obsoleteRefList  ArrayList of ObjectNames for obsolete references
    //  to MBeans (can be null)
    //
    // -exception RelationServiceNotRegisteredException  if the Relation
    //  Service is not registered in the MBean Server.
    private void updateUnregistrationListener(List newRefList,
					      List obsoleteRefList)
	throws RelationServiceNotRegisteredException {

	if (newRefList != null && obsoleteRefList != null) {
	    if (newRefList.isEmpty() && obsoleteRefList.isEmpty()) {
		// Nothing to do :)
		return;
	    }
	}

	if (isDebugOn()) {
	    StringBuffer strB = new StringBuffer();
	    if (newRefList != null) {
		strB.append("newRefList " + newRefList.toString());
	    }
	    if (obsoleteRefList != null) {
		strB.append(", obsoleteRefList" + obsoleteRefList.toString());
	    }
	    debug("updateUnregistrationListener: entering", strB.toString());
	}

	// Can throw RelationServiceNotRegisteredException
	isActive();

	if (newRefList != null || obsoleteRefList != null) {

	    boolean newListenerFlag = false;
	    if (myUnregNtfFilter == null) {
		// Initialize it to be able to synchronise it :)
		myUnregNtfFilter = new MBeanServerNotificationFilter();
		newListenerFlag = true;
	    }

	    synchronized(myUnregNtfFilter) {

		// Enables ObjectNames in newRefList
		if (newRefList != null) {
		    for (Iterator newRefIter = newRefList.iterator();
			 newRefIter.hasNext();) {

			ObjectName newObjName = (ObjectName)
			    (newRefIter.next());
			myUnregNtfFilter.enableObjectName(newObjName);
		    }
		}

		if (obsoleteRefList != null) {
		    // Disables ObjectNames in obsoleteRefList
		    for (Iterator obsRefIter = obsoleteRefList.iterator();
			 obsRefIter.hasNext();) {

			ObjectName obsObjName = (ObjectName)
			    (obsRefIter.next());
			myUnregNtfFilter.disableObjectName(obsObjName);
		    }
		}

// Under test
		if (newListenerFlag) {
		    try {
			myMBeanServer.addNotificationListener(
                                MBeanServerDelegate.DELEGATE_NAME,
                                this,
                                myUnregNtfFilter,
                                null);
		    } catch (InstanceNotFoundException exc) {
			throw new
		       RelationServiceNotRegisteredException(exc.getMessage());
		    }
		}
// End test


//		if (!newListenerFlag) {
		    // The Relation Service was already registered as a
		    // listener:
		    // removes it
		    // Shall not throw InstanceNotFoundException (as the
		    // MBean Server Delegate is expected to exist) or
		    // ListenerNotFoundException (as it has been checked above
		    // that the Relation Service is registered)
//		    try {
//			myMBeanServer.removeNotificationListener(
//				MBeanServerDelegate.DELEGATE_NAME,
//				this);
//		    } catch (InstanceNotFoundException exc1) {
//			throw new RuntimeException(exc1.getMessage());
//		    } catch (ListenerNotFoundException exc2) {
//			throw new
//			    RelationServiceNotRegisteredException(exc2.getMessage());
//		    }
//		}

		// Adds Relation Service with current filter
		// Can throw InstanceNotFoundException if the Relation
		// Service is not registered, to be transformed into
		// RelationServiceNotRegisteredException
		//
		// Assume that there will not be any InstanceNotFoundException
		// for the MBean Server Delegate :)
//		try {
//		    myMBeanServer.addNotificationListener(
//				MBeanServerDelegate.DELEGATE_NAME,
//				this,
//				myUnregNtfFilter,
//				null);
//		} catch (InstanceNotFoundException exc) {
//		    throw new
//		       RelationServiceNotRegisteredException(exc.getMessage());
//		}
	    }
	}

	if (isDebugOn())
	    debug("updateUnregistrationListener: exiting", null);
	return;
    }

    // Adds a relation (being either a RelationSupport object or an MBean
    // referenced using its ObjectName) in the Relation Service.
    // Will send a notification RelationNotification with type:
    // - RelationNotification.RELATION_BASIC_CREATION for internal relation
    //   creation
    // - RelationNotification.RELATION_MBEAN_CREATION for an MBean being added
    //   as a relation.
    //
    // -param relationBaseFlag  flag true if the relation is a RelationSupport
    //  object, false if it is an MBean
    // -param relationObj  RelationSupport object (if relation is internal)
    // -param relationObjName  ObjectName of the MBean to be added as a relation
    //  (only for the relation MBean)
    // -param relationId  relation identifier, to uniquely identify the relation
    //  inside the Relation Service
    // -param relationTypeName  name of the relation type (has to be created
    //  in the Relation Service)
    // -param roleList  role list to initialize roles of the relation
    //  (can be null)
    //
    // -exception IllegalArgumentException  if null paramater
    // -exception RelationServiceNotRegisteredException  if the Relation
    //  Service is not registered in the MBean Server
    // -exception RoleNotFoundException  if a value is provided for a role
    //  that does not exist in the relation type
    // -exception InvalidRelationIdException  if relation id already used
    // -exception RelationTypeNotFoundException  if relation type not known in
    //  Relation Service
    // -exception InvalidRoleValueException if:
    //  - the same role name is used for two different roles
    //  - the number of referenced MBeans in given value is less than
    //    expected minimum degree
    //  - the number of referenced MBeans in provided value exceeds expected
    //    maximum degree
    //  - one referenced MBean in the value is not an Object of the MBean
    //    class expected for that role
    //  - an MBean provided for that role does not exist
    private void addRelationInt(boolean relationBaseFlag,
				RelationSupport relationObj,
				ObjectName relationObjName,
				String relationId,
				String relationTypeName,
				RoleList roleList)
	throws IllegalArgumentException,
	       RelationServiceNotRegisteredException,
               RoleNotFoundException,
               InvalidRelationIdException,
               RelationTypeNotFoundException,
               InvalidRoleValueException {

	if (relationId == null ||
	    relationTypeName == null ||
	    (relationBaseFlag &&
	     (relationObj == null ||
	      relationObjName != null)) ||
	    (!relationBaseFlag &&
	     (relationObjName == null ||
	      relationObj != null))) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isDebugOn()) {
	    StringBuffer strB = new StringBuffer("relationBaseFlag "
						 + relationBaseFlag
						 + ", relationId " + relationId
						 + ", relationTypeName "
						 + relationTypeName);
	    if (relationObjName != null) {
		strB.append(",  relationObjName " + relationObjName.toString());
	    }
	    if (roleList != null) {
		strB.append(", roleList " + roleList.toString());
	    }
	    debug("addRelationInt: entering", strB.toString());
	}

	// Can throw RelationServiceNotRegisteredException
	isActive();

	// Checks if there is already a relation with given id
	try {
	    // Can throw a RelationNotFoundException (in fact should :)
	    Object rel = getRelation(relationId);

	    if (rel != null) {
		// There is already a relation with that id
		String excMsg = "There is already a relation with id ";
		StringBuffer excMsgStrB = new StringBuffer(excMsg);
		excMsgStrB.append(relationId);
		throw new InvalidRelationIdException(excMsgStrB.toString());
	    }
	} catch (RelationNotFoundException exc) {
	    // OK : The Relation could not be found.
	}

	// Retrieves the relation type
	// Can throw RelationTypeNotFoundException
	RelationType relType = getRelationType(relationTypeName);

	// Checks that each provided role conforms to its role info provided in
	// the relation type
	// First retrieves a local list of the role infos of the relation type
	// to see which roles have not been initialized
	// Note: no need to test if list not null before cloning, not allowed
	//       to have an empty relation type.
	ArrayList roleInfoList = (ArrayList)
	    (((ArrayList)(relType.getRoleInfos())).clone());

	if (roleList != null) {

	    for (Iterator roleIter = roleList.iterator();
		 roleIter.hasNext();) {

		Role currRole = (Role)(roleIter.next());
		String currRoleName = currRole.getRoleName();
		ArrayList currRoleValue = (ArrayList)
		    (currRole.getRoleValue());
		// Retrieves corresponding role info
		// Can throw a RoleInfoNotFoundException to be converted into a
		// RoleNotFoundException
		RoleInfo roleInfo = null;
		try {
		    roleInfo = relType.getRoleInfo(currRoleName);
		} catch (RoleInfoNotFoundException exc) {
		    throw new RoleNotFoundException(exc.getMessage());
		}

		// Checks that role conforms to role info,
		Integer status = checkRoleInt(2,
					      currRoleName,
					      currRoleValue,
					      roleInfo,
					      false);
		int pbType = status.intValue();
		if (pbType != 0) {
		    // A problem has occurred: throws appropriate exception
		    // here InvalidRoleValueException
		    throwRoleProblemException(pbType, currRoleName);
		}

		// Removes role info for that list from list of role infos for
		// roles to be defaulted
		int roleInfoIdx = roleInfoList.indexOf(roleInfo);
		// Note: no need to check if != -1, MUST be there :)
		roleInfoList.remove(roleInfoIdx);
	    }
	}

	// Initializes roles not initialized by roleList
	// Can throw InvalidRoleValueException
	initializeMissingRoles(relationBaseFlag,
			       relationObj,
			       relationObjName,
			       relationId,
			       relationTypeName,
			       roleInfoList);

	// Creation of relation successfull!!!!

	// Updates internal maps
	// Relation id to object map
	synchronized(myRelId2ObjMap) {
	    if (relationBaseFlag) {
		// Note: do not clone relation object, created by us :)
		myRelId2ObjMap.put(relationId, relationObj);
	    } else {
		myRelId2ObjMap.put(relationId, relationObjName);
	    }
	}

	// Relation id to relation type name map
	synchronized(myRelId2RelTypeMap) {
	    myRelId2RelTypeMap.put(relationId,
				   relationTypeName);
	}

	// Relation type to relation id map
	synchronized(myRelType2RelIdsMap) {
	    List<String> relIdList =
		myRelType2RelIdsMap.get(relationTypeName);
	    boolean firstRelFlag = false;
	    if (relIdList == null) {
		firstRelFlag = true;
		relIdList = new ArrayList<String>();
	    }
	    relIdList.add(relationId);
	    if (firstRelFlag) {
		myRelType2RelIdsMap.put(relationTypeName, relIdList);
	    }
	}

	// Referenced MBean to relation id map
	// Only role list parameter used, as default initialization of roles
	// done automatically in initializeMissingRoles() sets each
	// uninitialized role to an empty value.
	for (Iterator roleIter = roleList.iterator();
	     roleIter.hasNext();) {
	    Role currRole = (Role)(roleIter.next());
	    // Creates a dummy empty ArrayList of ObjectNames to be the old
	    // role value :)
	    List<ObjectName> dummyList = new ArrayList<ObjectName>();
	    // Will not throw a RelationNotFoundException (as the RelId2Obj map
	    // has been updated above) so catch it :)
	    try {
		updateRoleMap(relationId, currRole, dummyList);

	    } catch (RelationNotFoundException exc) {
		// OK : The Relation could not be found.
	    }
	}

	// Sends a notification for relation creation
	// Will not throw RelationNotFoundException so catch it :)
	try {
	    sendRelationCreationNotification(relationId);

	} catch (RelationNotFoundException exc) {
	    // OK : The Relation could not be found.
	}

	if (isDebugOn())
	    debug("addRelationInt: exiting", null);
	return;
    }

    // Checks that given role conforms to given role info.
    //
    // -param chkType  type of check:
    //  - 1: read, just check read access
    //  - 2: write, check value and write access if writeChkFlag
    // -param roleName  role name
    // -param roleValue  role value
    // -param roleInfo  corresponding role info
    // -param writeChkFlag  boolean to specify a current write access and
    //  to check it
    //
    // -return Integer with value:
    //  - 0: ok
    //  - RoleStatus.NO_ROLE_WITH_NAME
    //  - RoleStatus.ROLE_NOT_READABLE
    //  - RoleStatus.ROLE_NOT_WRITABLE
    //  - RoleStatus.LESS_THAN_MIN_ROLE_DEGREE
    //  - RoleStatus.MORE_THAN_MAX_ROLE_DEGREE
    //  - RoleStatus.REF_MBEAN_OF_INCORRECT_CLASS
    //  - RoleStatus.REF_MBEAN_NOT_REGISTERED
    //
    // -exception IllegalArgumentException  if null parameter
    private Integer checkRoleInt(int chkType,
				 String roleName,
				 List roleValue,
				 RoleInfo roleInfo,
				 boolean writeChkFlag)
	throws IllegalArgumentException {

	if (roleName == null ||
	    roleInfo == null ||
	    (chkType == 2 && roleValue == null)) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isDebugOn()) {
	    StringBuffer strB = new StringBuffer("chkType "
						 + chkType
						 + ", roleName "
						 + roleName
						 + ", roleInfo "
						 + roleInfo.toString()
						 + ", writeChkFlag "
						 + writeChkFlag);
	    if (roleValue != null) {
		strB.append(", roleValue " + roleValue.toString());
	    }
	    debug("checkRoleInt: entering", strB.toString());
	}

	// Compares names
	String expName = roleInfo.getName();
	if (!(roleName.equals(expName))) {
	    if (isDebugOn())
		debug("checkRoleInt: exiting", null);
	    return new Integer(RoleStatus.NO_ROLE_WITH_NAME);
	}

	// Checks read access if required
	if (chkType == 1) {
	    boolean isReadable = roleInfo.isReadable();
	    if (!isReadable) {
		if (isDebugOn())
		    debug("checkRoleInt: exiting", null);
		return new Integer(RoleStatus.ROLE_NOT_READABLE);
	    } else {
		// End of check :)
		if (isDebugOn())
		    debug("checkRoleInt: exiting", null);
		return new Integer(0);
	    }
	}

	// Checks write access if required
	if (writeChkFlag) {
	    boolean isWritable = roleInfo.isWritable();
	    if (!isWritable) {
		if (isDebugOn())
		    debug("checkRoleInt: exiting", null);
		return new Integer(RoleStatus.ROLE_NOT_WRITABLE);
	    }
	}

	int refNbr = roleValue.size();

	// Checks minimum cardinality
	boolean chkMinFlag = roleInfo.checkMinDegree(refNbr);
	if (!chkMinFlag) {
	    if (isDebugOn())
		debug("checkRoleInt: exiting", null);
	    return new Integer(RoleStatus.LESS_THAN_MIN_ROLE_DEGREE);
	}

	// Checks maximum cardinality
	boolean chkMaxFlag = roleInfo.checkMaxDegree(refNbr);
	if (!chkMaxFlag) {
	    if (isDebugOn())
		debug("checkRoleInt: exiting", null);
	    return new Integer(RoleStatus.MORE_THAN_MAX_ROLE_DEGREE);
	}

	// Verifies that each referenced MBean is registered in the MBean
	// Server and that it is an instance of the class specified in the
	// role info, or of a subclass of it
	// Note that here again this is under the assumption that
	// referenced MBeans, relation MBeans and the Relation Service are
	// registered in the same MBean Server.
	String expClassName = roleInfo.getRefMBeanClassName();

	for (Iterator refMBeanIter = roleValue.iterator();
	     refMBeanIter.hasNext();) {
	    ObjectName currObjName = (ObjectName)(refMBeanIter.next());

	    // Checks it is registered
	    if (currObjName == null) {
		if (isDebugOn())
		    debug("checkRoleInt: exiting", null);
		return new Integer(RoleStatus.REF_MBEAN_NOT_REGISTERED);
	    }

	    // Checks if it is of the correct class
	    // Can throw an InstanceNotFoundException, if MBean not registered
	    try {
		boolean classSts = myMBeanServer.isInstanceOf(currObjName,
							      expClassName);
		if (!classSts) {
		    if (isDebugOn())
			debug("checkRoleInt: exiting", null);
		    return new Integer(RoleStatus.REF_MBEAN_OF_INCORRECT_CLASS);
		}

	    } catch (InstanceNotFoundException exc) {
		if (isDebugOn())
		    debug("checkRoleInt: exiting", null);
		return new Integer(RoleStatus.REF_MBEAN_NOT_REGISTERED);
	    }
	}

	if (isDebugOn())
	    debug("checkRoleInt: exiting", null);
	return new Integer(0);
    }


    // Initializes roles associated to given role infos to default value (empty
    // ArrayList of ObjectNames) in given relation.
    // It will succeed for every role except if the role info has a minimum
    // cardinality greater than 0. In that case, an InvalidRoleValueException
    // will be raised.
    //
    // -param relationBaseFlag  flag true if the relation is a RelationSupport
    //  object, false if it is an MBean
    // -param relationObj  RelationSupport object (if relation is internal)
    // -param relationObjName  ObjectName of the MBean to be added as a relation
    //  (only for the relation MBean)
    // -param relationId  relation id
    // -param relationTypeName  name of the relation type (has to be created
    //  in the Relation Service)
    // -param roleInfoList  list of role infos for roles to be defaulted
    //
    // -exception IllegalArgumentException  if null paramater
    // -exception RelationServiceNotRegisteredException  if the Relation
    //  Service is not registered in the MBean Server
    // -exception InvalidRoleValueException  if role must have a non-empty
    //  value

    // Revisit [cebro] Handle CIM qualifiers as REQUIRED to detect roles which
    //    should have been initialized by the user
    private void initializeMissingRoles(boolean relationBaseFlag,
					RelationSupport relationObj,
					ObjectName relationObjName,
					String relationId,
					String relationTypeName,
					List roleInfoList)
	throws IllegalArgumentException,
	       RelationServiceNotRegisteredException,
	       InvalidRoleValueException {

	if ((relationBaseFlag &&
	     (relationObj == null ||
	      relationObjName != null)) ||
	    (!relationBaseFlag &&
	     (relationObjName == null ||
	      relationObj != null)) ||
	    relationId == null ||
	    relationTypeName == null ||
	    roleInfoList == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isDebugOn()) {
	    StringBuffer strB =
		new StringBuffer("relationBaseFlag " + relationBaseFlag
				 + ", relationId " + relationId
				 + ", relationTypeName " + relationTypeName
				 + ", roleInfoList " + roleInfoList);
	    if (relationObjName != null) {
		strB.append(relationObjName.toString());
	    }
	    debug("initializeMissingRoles: entering", strB.toString());
	}

	// Can throw RelationServiceNotRegisteredException
	isActive();

	// For each role info (corresponding to a role not initialized by the
	// role list provided by the user), try to set in the relation a role
	// with an empty list of ObjectNames.
	// A check is performed to verify that the role can be set to an
	// empty value, according to its minimum cardinality
	for (Iterator roleInfoIter = roleInfoList.iterator();
	     roleInfoIter.hasNext();) {

	    RoleInfo currRoleInfo = (RoleInfo)(roleInfoIter.next());
	    String roleName = currRoleInfo.getName();

	    // Creates an empty value
	    List<ObjectName> emptyValue = new ArrayList<ObjectName>();
	    // Creates a role
	    Role role = new Role(roleName, emptyValue);

	    if (relationBaseFlag) {

		// Internal relation
		// Can throw InvalidRoleValueException
		//
		// Will not throw RoleNotFoundException (role to be
		// initialized), or RelationNotFoundException, or
		// RelationTypeNotFoundException
		try {
		    relationObj.setRoleInt(role, true, this, false);

		} catch (RoleNotFoundException exc1) {
		    throw new RuntimeException(exc1.getMessage());
		} catch (RelationNotFoundException exc2) {
		    throw new RuntimeException(exc2.getMessage());
		} catch (RelationTypeNotFoundException exc3) {
		    throw new RuntimeException(exc3.getMessage());
		}

	    } else {

		// Relation is an MBean
		// Use standard setRole()
		Object[] params = new Object[1];
		params[0] = role;
		String[] signature = new String[1];
		signature[0] = "javax.management.relation.Role";
		// Can throw MBeanException wrapping
		// InvalidRoleValueException. Returns the target exception to
		// be homogeneous.
		//
		// Will not throw MBeanException (wrapping
		// RoleNotFoundException or MBeanException) or
		// InstanceNotFoundException, or ReflectionException
		//
		// Again here the assumption is that the Relation Service and
		// the relation MBeans are registered in the same MBean Server.
		try {
		    myMBeanServer.setAttribute(relationObjName,
                                               new Attribute("Role", role));

		} catch (InstanceNotFoundException exc1) {
		    throw new RuntimeException(exc1.getMessage());
		} catch (ReflectionException exc3) {
		    throw new RuntimeException(exc3.getMessage());
		} catch (MBeanException exc2) {
		    Exception wrappedExc = exc2.getTargetException();
		    if (wrappedExc instanceof InvalidRoleValueException) {
			throw ((InvalidRoleValueException)wrappedExc);
		    } else {
			throw new RuntimeException(wrappedExc.getMessage());
		    }
		} catch (AttributeNotFoundException exc4) {
                  throw new RuntimeException(exc4.getMessage());
                } catch (InvalidAttributeValueException exc5) {
                  throw new RuntimeException(exc5.getMessage());
                }
	    }
	}

	if (isDebugOn())
	    debug("initializeMissingRoles: exiting", null);
	return;
    }

    // Throws an exception corresponding to a given problem type
    //
    // -param pbType  possible problem, defined in RoleUnresolved
    // -param roleName  role name
    //
    // -exception IllegalArgumentException  if null parameter
    // -exception RoleNotFoundException  for problems:
    //  - NO_ROLE_WITH_NAME
    //  - ROLE_NOT_READABLE
    //  - ROLE_NOT_WRITABLE
    // -exception InvalidRoleValueException  for problems:
    //  - LESS_THAN_MIN_ROLE_DEGREE
    //  - MORE_THAN_MAX_ROLE_DEGREE
    //  - REF_MBEAN_OF_INCORRECT_CLASS
    //  - REF_MBEAN_NOT_REGISTERED
    static void throwRoleProblemException(int pbType,
					  String roleName)
	throws IllegalArgumentException,
	       RoleNotFoundException,
	       InvalidRoleValueException {

	if (roleName == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	// Exception type: 1 = RoleNotFoundException
	//                 2 = InvalidRoleValueException
	int excType = 0;

	String excMsgPart = null;

	switch (pbType) {
	case RoleStatus.NO_ROLE_WITH_NAME:
	    excMsgPart = " does not exist in relation.";
	    excType = 1;
	    break;
	case RoleStatus.ROLE_NOT_READABLE:
	    excMsgPart = " is not readable.";
	    excType = 1;
	    break;
	case RoleStatus.ROLE_NOT_WRITABLE:
	    excMsgPart = " is not writable.";
	    excType = 1;
	    break;
	case RoleStatus.LESS_THAN_MIN_ROLE_DEGREE:
	    excMsgPart = " has a number of MBean references less than the expected minimum degree.";
	    excType = 2;
	    break;
	case RoleStatus.MORE_THAN_MAX_ROLE_DEGREE:
	    excMsgPart = " has a number of MBean references greater than the expected maximum degree.";
	    excType = 2;
	    break;
	case RoleStatus.REF_MBEAN_OF_INCORRECT_CLASS:
	    excMsgPart = " has an MBean reference to an MBean not of the expected class of references for that role.";
	    excType = 2;
	    break;
	case RoleStatus.REF_MBEAN_NOT_REGISTERED:
	    excMsgPart = " has a reference to null or to an MBean not registered.";
	    excType = 2;
	    break;
	}
	// No default as we must have been in one of those cases

	StringBuffer excMsgStrB = new StringBuffer(roleName);
	excMsgStrB.append(excMsgPart);
	String excMsg = excMsgStrB.toString();
	if (excType == 1) {
	    throw new RoleNotFoundException(excMsg);

	} else if (excType == 2) {
	    throw new InvalidRoleValueException(excMsg);
	}
    }

    // Sends a notification of given type, with given parameters
    //
    // -param intNtfType  integer to represent notification type:
    //  - 1 : create
    //  - 2 : update
    //  - 3 : delete
    // -param message  human-readable message
    // -param relationId  relation id of the created/updated/deleted relation
    // -param unregMBeanList  list of ObjectNames of referenced MBeans
    //  expected to be unregistered due to relation removal (only for removal,
    //  due to CIM qualifiers, can be null)
    // -param roleName  role name
    // -param roleNewValue  role new value (ArrayList of ObjectNames)
    // -param oldValue  old role value (ArrayList of ObjectNames)
    //
    // -exception IllegalArgument  if null parameter
    // -exception RelationNotFoundException  if no relation for given id
    private void sendNotificationInt(int intNtfType,
				     String message,
				     String relationId,
				     List<ObjectName> unregMBeanList,
				     String roleName,
				     List<ObjectName> roleNewValue,
				     List<ObjectName> oldValue)
	throws IllegalArgumentException,
	       RelationNotFoundException {

	if (message == null ||
	    relationId == null ||
	    (intNtfType != 3 && unregMBeanList != null) ||
	    (intNtfType == 2 &&
	     (roleName == null ||
	      roleNewValue == null ||
	      oldValue == null))) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isDebugOn()) {
	    StringBuffer strB =
		new StringBuffer("intNtfType " + intNtfType
				 + ", message " + message
				 + ", relationId " + relationId);
	    if (unregMBeanList != null) {
		strB.append(", unregMBeanList " +
			    unregMBeanList.toString());
	    }
	    if (roleName != null) {
		strB.append(", roleName " + roleName);
	    }
	    if (roleNewValue != null) {
		strB.append(", roleNewValue " + roleNewValue.toString());
	    }
	    if (oldValue != null) {
		strB.append(", oldValue " + oldValue.toString());
	    }
	    debug("sendNotificationInt: entering", strB.toString());
	}

	// Relation type name
	// Note: do not use getRelationTypeName() as if it is a relation MBean
        //       it is already unregistered.
	String relTypeName = null;
	synchronized(myRelId2RelTypeMap) {
	    relTypeName = (String)(myRelId2RelTypeMap.get(relationId));
	}

	// ObjectName (for a relation MBean)
	// Can also throw a RelationNotFoundException, but detected above
	ObjectName relObjName = isRelationMBean(relationId);

	String ntfType = null;
	if (relObjName != null) {
	    switch (intNtfType) {
	    case 1:
		ntfType = RelationNotification.RELATION_MBEAN_CREATION;
		break;
	    case 2:
		ntfType = RelationNotification.RELATION_MBEAN_UPDATE;
		break;
	    case 3:
		ntfType = RelationNotification.RELATION_MBEAN_REMOVAL;
		break;
	    }
	} else {
	    switch (intNtfType) {
	    case 1:
		ntfType = RelationNotification.RELATION_BASIC_CREATION;
		break;
	    case 2:
		ntfType = RelationNotification.RELATION_BASIC_UPDATE;
		break;
	    case 3:
		ntfType = RelationNotification.RELATION_BASIC_REMOVAL;
		break;
	    }
	}

	// Sequence number
	Long seqNbr = getNotificationSequenceNumber();

	// Timestamp
	Date currDate = new Date();
	long timeStamp = currDate.getTime();

	RelationNotification ntf = null;

	if (ntfType.equals(RelationNotification.RELATION_BASIC_CREATION) ||
	    ntfType.equals(RelationNotification.RELATION_MBEAN_CREATION) ||
	    ntfType.equals(RelationNotification.RELATION_BASIC_REMOVAL) ||
	    ntfType.equals(RelationNotification.RELATION_MBEAN_REMOVAL))

	    // Creation or removal
	    ntf = new RelationNotification(ntfType,
					   this,
					   seqNbr.longValue(),
					   timeStamp,
					   message,
					   relationId,
					   relTypeName,
					   relObjName,
					   unregMBeanList);

	else if (ntfType.equals(RelationNotification.RELATION_BASIC_UPDATE)
		 ||
		 ntfType.equals(RelationNotification.RELATION_MBEAN_UPDATE))
	    {
		// Update
		ntf = new RelationNotification(ntfType,
					       this,
					       seqNbr.longValue(),
					       timeStamp,
					       message,
					       relationId,
					       relTypeName,
					       relObjName,
					       roleName,
					       roleNewValue,
					       oldValue);
	    }

	sendNotification(ntf);

	if (isDebugOn())
	    debug("sendNotificationInt: exiting", null);
	return;
    }

    // Checks, for the unregistration of an MBean referenced in the roles given
    // in parameter, if the relation has to be removed or not, regarding
    // expected minimum role cardinality and current number of
    // references in each role after removal of the current one.
    // If the relation is kept, calls handleMBeanUnregistration() callback of
    // the relation to update it.
    //
    // -param relationId  relation id
    // -param objectName  ObjectName of the unregistered MBean
    // -param roleNameList  list of names of roles where the unregistered
    //  MBean is referenced.
    //
    // -exception IllegalArgumentException  if null parameter
    // -exception RelationServiceNotRegisteredException  if the Relation
    //  Service is not registered in the MBean Server
    // -exception RelationNotFoundException  if unknown relation id
    // -exception RoleNotFoundException  if one role given as parameter does
    //  not exist in the relation
    private void handleReferenceUnregistration(String relationId,
					       ObjectName objectName,
					       List roleNameList)
	throws IllegalArgumentException,
	       RelationServiceNotRegisteredException,
               RelationNotFoundException,
	       RoleNotFoundException {

	if (relationId == null ||
	    roleNameList == null ||
	    objectName == null) {
	    String excMsg = "Invalid parameter.";
	    throw new IllegalArgumentException(excMsg);
	}

	if (isDebugOn()) {
	    String str =
		new String("relationId " + relationId
			   + ", roleNameList " + roleNameList.toString()
			   + "objectName " + objectName.toString());
	    debug("handleReferenceUnregistration: entering", str);
	}

	// Can throw RelationServiceNotRegisteredException
	isActive();

	// Retrieves the relation type name of the relation
	// Can throw RelationNotFoundException
	String currRelTypeName = getRelationTypeName(relationId);

	// Retrieves the relation
	// Can throw RelationNotFoundException, but already detected above
	Object relObj = getRelation(relationId);

	// Flag to specify if the relation has to be deleted
	boolean deleteRelFlag = false;

	for (Iterator roleNameIter = roleNameList.iterator();
	     roleNameIter.hasNext();) {

	    if (deleteRelFlag) {
		break;
	    }

	    String currRoleName = (String)(roleNameIter.next());
	    // Retrieves number of MBeans currently referenced in role
	    // BEWARE! Do not use getRole() as role may be not readable
	    //
	    // Can throw RelationNotFoundException (but already checked),
	    // RoleNotFoundException
	    int currRoleRefNbr =
		(getRoleCardinality(relationId, currRoleName)).intValue();

	    // Retrieves new number of element in role
	    int currRoleNewRefNbr = currRoleRefNbr - 1;

	    // Retrieves role info for that role
	    //
	    // Shall not throw RelationTypeNotFoundException or
	    // RoleInfoNotFoundException
	    RoleInfo currRoleInfo = null;
	    try {
		currRoleInfo = getRoleInfo(currRelTypeName,
					   currRoleName);
	    } catch (RelationTypeNotFoundException exc1) {
		throw new RuntimeException(exc1.getMessage());
	    } catch (RoleInfoNotFoundException exc2) {
		throw new RuntimeException(exc2.getMessage());
	    }

	    // Checks with expected minimum number of elements
	    boolean chkMinFlag = currRoleInfo.checkMinDegree(currRoleNewRefNbr);

	    if (!chkMinFlag) {
		// The relation has to be deleted
		deleteRelFlag = true;
	    }
	}

	if (deleteRelFlag) {
	    // Removes the relation
	    removeRelation(relationId);

	} else {

	    // Updates each role in the relation using
	    // handleMBeanUnregistration() callback
            //
            // BEWARE: this roleNameList list MUST BE A COPY of a role name
            //         list for a referenced MBean in a relation, NOT a
            //         reference to an original one part of the
            //         myRefedMBeanObjName2RelIdsMap!!!! Because each role
            //         which name is in that list will be updated (potentially
            //         using setRole(). So the Relation Service will update the
            //         myRefedMBeanObjName2RelIdsMap to refelect the new role
            //         value!
	    for (Iterator roleNameIter = roleNameList.iterator();
		 roleNameIter.hasNext();) {

		String currRoleName = (String)(roleNameIter.next());

		if (relObj instanceof RelationSupport) {
		    // Internal relation
		    // Can throw RoleNotFoundException (but already checked)
		    //
		    // Shall not throw
		    // RelationTypeNotFoundException,
		    // InvalidRoleValueException (value was correct, removing
		    // one reference shall not invalidate it, else detected
		    // above)
		    try {
			((RelationSupport)relObj).handleMBeanUnregistrationInt(
						  objectName,
						  currRoleName,
						  true,
						  this);
		    } catch (RelationTypeNotFoundException exc3) {
			throw new RuntimeException(exc3.getMessage());
		    } catch (InvalidRoleValueException exc4) {
			throw new RuntimeException(exc4.getMessage());
		    }

		} else {
		    // Relation MBean
		    Object[] params = new Object[2];
		    params[0] = objectName;
		    params[1] = currRoleName;
		    String[] signature = new String[2];
		    signature[0] = "javax.management.ObjectName";
		    signature[1] = "java.lang.String";
		    // Shall not throw InstanceNotFoundException, or
		    // MBeanException (wrapping RoleNotFoundException or
		    // MBeanException or InvalidRoleValueException) or
		    // ReflectionException
		    try {
			myMBeanServer.invoke(((ObjectName)relObj),
					     "handleMBeanUnregistration",
					     params,
					     signature);
		    } catch (InstanceNotFoundException exc1) {
			throw new RuntimeException(exc1.getMessage());
		    } catch (ReflectionException exc3) {
			throw new RuntimeException(exc3.getMessage());
		    } catch (MBeanException exc2) {
			Exception wrappedExc = exc2.getTargetException();
			throw new RuntimeException(wrappedExc.getMessage());
		    }

		}
	    }
	}

	if (isDebugOn())
	    debug("handleReferenceUnregistration: exiting", null);
	return;
    }

    // stuff for Tracing

    private static String localClassName = "RelationService";

    // trace level
    private boolean isTraceOn() {
        return Trace.isSelected(Trace.LEVEL_TRACE, Trace.INFO_RELATION);
    }

//    private void trace(String className, String methodName, String info) {
//        Trace.send(Trace.LEVEL_TRACE, Trace.INFO_RELATION, className, methodName, info);
//    }

    private void trace(String methodName, String info) {
        Trace.send(Trace.LEVEL_TRACE, Trace.INFO_RELATION, localClassName, methodName, info);
	Trace.send(Trace.LEVEL_TRACE, Trace.INFO_RELATION, "", "", "\n");
    }

//    private void trace(String className, String methodName, Exception e) {
//        Trace.send(Trace.LEVEL_TRACE, Trace.INFO_RELATION, className, methodName, e);
//    }

//    private void trace(String methodName, Exception e) {
//        Trace.send(Trace.LEVEL_TRACE, Trace.INFO_RELATION, localClassName, methodName, e);
//    }

    // debug level
    private boolean isDebugOn() {
        return Trace.isSelected(Trace.LEVEL_DEBUG, Trace.INFO_RELATION);
    }

//    private void debug(String className, String methodName, String info) {
//        Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_RELATION, className, methodName, info);
//    }

    private void debug(String methodName, String info) {
        Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_RELATION, localClassName, methodName, info);
	Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_RELATION, "", "", "\n");
    }

//    private void debug(String className, String methodName, Exception e) {
//        Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_RELATION, className, methodName, e);
//    }

//    private void debug(String methodName, Exception e) {
//        Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_RELATION, localClassName, methodName, e);
//    }
}