Use Tree Navigation
/*
 * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */


package javax.management.relation;

import static com.sun.jmx.defaults.JmxProperties.RELATION_LOGGER;
import static com.sun.jmx.mbeanserver.Util.cast;

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 java.util.logging.Level;

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) {

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"RelationService");

        setPurgeFlag
(immediatePurgeFlag);

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"RelationService");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"createRelationType", relationTypeName);

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

        addRelationTypeInt
(relType);

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"createRelationType");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"addRelationType");

       
// 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);

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"addRelationType");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"getRoleInfos", relationTypeName);

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

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"getRoleInfos");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"getRoleInfo", new Object[] {relationTypeName, roleInfoName});

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

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

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"getRoleInfo");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"removeRelationType", 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());
               
}
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"removeRelationType");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"createRelation",
               
new Object[] {relationId, relationTypeName, roleList});

       
// 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
);
        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"createRelation");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"addRelation", relationObjectName);

       
// 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);

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"addRelation");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"isRelation", objectName);

       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"checkRoleReading", new Object[] {roleName, relationTypeName});

       
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);
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"checkRoleReading");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"checkRoleWriting",
               
new Object[] {role, relationTypeName, initFlag});

       
// 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) {
            RELATION_LOGGER
.exiting(RelationService.class.getName(),
                   
"checkRoleWriting");
           
return new Integer(RoleStatus.NO_ROLE_WITH_NAME);
       
}

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

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"checkRoleWriting");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"sendRelationCreationNotification", relationId);

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

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

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"sendRelationCreationNotification");
       
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);

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"sendRoleUpdateNotification",
               
new Object[] {relationId, newRole, oldValue});

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

       
// Message
       
String newRoleValString = Role.roleValueToString(newRoleVal);
       
String oldRoleValString = Role.roleValueToString(oldValue);
       
StringBuilder ntfMsg = new StringBuilder("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
);

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"sendRoleUpdateNotification");
   
}

   
/**
     * 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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"sendRelationRemovalNotification",
               
new Object[] {relationId, unregMBeanList});

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


        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"sendRelationRemovalNotification");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"updateRoleMap", new Object[] {relationId, newRole, oldValue});

       
// 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);

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"updateRoleMap");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"removeRelation", 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);
               
}
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"removeRelation");
       
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 {

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"purgeRelations");

       
// 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 (Map.Entry<String,List<String>> currRel :
                        localRelIdMap
.entrySet()) {
               
final String currRelId = currRel.getKey();
               
// List of roles of the relation where the MBean is
               
// referenced
               
List<String> localRoleNameList = currRel.getValue();

               
// 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());
               
}
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"purgeRelations");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"findReferencingRelations",
               
new Object[] {mbeanName, relationTypeName, roleName});

       
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);
                   
}
               
}
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"findReferencingRelations");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"findAssociatedMBeans",
               
new Object[] {mbeanName, relationTypeName, roleName});

       
// 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);
                   
}
               
}
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"findAssociatedMBeans");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"findRelationsOfType");

       
// 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);
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"findRelationsOfType");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"getRole", new Object[] {relationId, roleName});

       
// 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
= cast(
               
((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 = cast(
                    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());
               
}
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(), "getRole");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"getRoles", 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());
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(), "getRoles");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"getRoles", 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());
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(), "getRoles");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"getRoleCardinality", new Object[] {relationId, roleName});

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

       
Integer result = null;

       
if (relObj instanceof RelationSupport) {
           
// Internal relation
           
// Can throw RoleNotFoundException
            result
= ((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());
               
}
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"getRoleCardinality");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"setRole", new Object[] {relationId, role});

       
// 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());
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(), "setRole");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"setRoles", new Object[] {relationId, roleList});

       
// 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());
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(), "setRoles");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"getReferencedMBeans", 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
= cast(
                    myMBeanServer
.getAttribute(((ObjectName)relObj),
                                               
"ReferencedMBeans"));
           
} catch (Exception exc) {
               
throw new RuntimeException(exc.getMessage());
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"getReferencedMBeans");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"getRelationTypeName", 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());
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"getRelationTypeName");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"handleNotification", notif);

       
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
= 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());
                   
}
               
}
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"handleNotification");
       
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() {

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"getNotificationInfo");

       
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);

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"getNotificationInfo");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"addRelationTypeInt");

       
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 ";
               
StringBuilder excMsgStrB = new StringBuilder(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);
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"addRelationTypeInt");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"getRelationType", relationTypeName);

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

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

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"getRelationType");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"getRelation", 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);
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"getRelation");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"addNewMBeanReference",
               
new Object[] {objectName, relationId, roleName});

       
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);
               
}
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"addNewMBeanReference");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"removeMBeanReference",
               
new Object[] {objectName, relationId, roleName, allRolesFlag});

       
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
                RELATION_LOGGER
.exiting(RelationService.class.getName(),
                       
"removeMBeanReference");
               
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;
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"removeMBeanReference");
       
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;
           
}
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"updateUnregistrationListener",
               
new Object[] {newRefList, obsoleteRefList});

       
// 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());
//              }
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"updateUnregistrationListener");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"addRelationInt", new Object[] {relationBaseFlag, relationObj,
                relationObjName
, relationId, relationTypeName, roleList});

       
// 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 ";
               
StringBuilder excMsgStrB = new StringBuilder(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.
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"addRelationInt");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"checkRoleInt", new Object[] {chkType, roleName,
                roleValue
, roleInfo, writeChkFlag});

       
// Compares names
       
String expName = roleInfo.getName();
       
if (!(roleName.equals(expName))) {
            RELATION_LOGGER
.exiting(RelationService.class.getName(),
                   
"checkRoleInt");
           
return new Integer(RoleStatus.NO_ROLE_WITH_NAME);
       
}

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

       
// Checks write access if required
       
if (writeChkFlag) {
           
boolean isWritable = roleInfo.isWritable();
           
if (!isWritable) {
                RELATION_LOGGER
.exiting(RelationService.class.getName(),
                       
"checkRoleInt");
               
return new Integer(RoleStatus.ROLE_NOT_WRITABLE);
           
}
       
}

       
int refNbr = roleValue.size();

       
// Checks minimum cardinality
       
boolean chkMinFlag = roleInfo.checkMinDegree(refNbr);
       
if (!chkMinFlag) {
            RELATION_LOGGER
.exiting(RelationService.class.getName(),
                   
"checkRoleInt");
           
return new Integer(RoleStatus.LESS_THAN_MIN_ROLE_DEGREE);
       
}

       
// Checks maximum cardinality
       
boolean chkMaxFlag = roleInfo.checkMaxDegree(refNbr);
       
if (!chkMaxFlag) {
            RELATION_LOGGER
.exiting(RelationService.class.getName(),
                   
"checkRoleInt");
           
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) {
                RELATION_LOGGER
.exiting(RelationService.class.getName(),
                       
"checkRoleInt");
               
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) {
                    RELATION_LOGGER
.exiting(RelationService.class.getName(),
                           
"checkRoleInt");
                   
return new Integer(RoleStatus.REF_MBEAN_OF_INCORRECT_CLASS);
               
}

           
} catch (InstanceNotFoundException exc) {
                RELATION_LOGGER
.exiting(RelationService.class.getName(),
                       
"checkRoleInt");
               
return new Integer(RoleStatus.REF_MBEAN_NOT_REGISTERED);
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"checkRoleInt");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"initializeMissingRoles", new Object[] {relationBaseFlag,
                relationObj
, relationObjName, relationId, relationTypeName,
                roleInfoList
});

       
// 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());
               
}
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"initializeMissingRoles");
       
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

       
StringBuilder excMsgStrB = new StringBuilder(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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"sendNotificationInt", new Object[] {intNtfType, message,
                relationId
, unregMBeanList, roleName, roleNewValue, oldValue});

       
// 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
= (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);

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"sendNotificationInt");
       
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);
       
}

        RELATION_LOGGER
.entering(RelationService.class.getName(),
               
"handleReferenceUnregistration",
               
new Object[] {relationId, objectName, roleNameList});

       
// 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());
                   
}

               
}
           
}
       
}

        RELATION_LOGGER
.exiting(RelationService.class.getName(),
               
"handleReferenceUnregistration");
       
return;
   
}
}