Package javax.management.relation

Source Code of javax.management.relation.RelationService

/*
* @(#)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);
//    }
}
TOP

Related Classes of javax.management.relation.RelationService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.