diff -r 4ebc2e2fb97c -r 71c04702a3d5 src/java.management/share/classes/javax/management/relation/RelationService.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.management/share/classes/javax/management/relation/RelationService.java Tue Sep 12 19:03:39 2017 +0200 @@ -0,0 +1,3711 @@ +/* + * Copyright (c) 2000, 2017, 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.concurrent.atomic.AtomicLong; +import java.lang.System.Logger.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. + *

It implements the NotificationBroadcaster by extending + * NotificationBroadcasterSupport to send notifications when a relation is + * removed from it. + *

It implements the NotificationListener interface to be able to receive + * notifications concerning unregistration of MBeans referenced in relation + * roles and of relation MBeans. + *

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: + // -> + // 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 myRelId2ObjMap = new HashMap(); + + // Map associating: + // -> + private Map myRelId2RelTypeMap = new HashMap(); + + // Map associating: + // -> + private Map myRelMBeanObjName2RelIdMap = + new HashMap(); + + // Map associating: + // -> + private Map myRelType2ObjMap = + new HashMap(); + + // Map associating: + // -> ArrayList of + // to list all the relations of a given type + private Map> myRelType2RelIdsMap = + new HashMap>(); + + // Map associating: + // -> HashMap + // the value HashMap mapping: + // -> ArrayList of + // to track where a given MBean is referenced. + private final Map>> + myRefedMBeanObjName2RelIdsMap = + new HashMap>>(); + + // 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 final AtomicLong atomicSeqNo = new AtomicLong(); + + // 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 deregistrations + 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 myUnregNtfList = + new ArrayList(); + + // + // 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. + *

true is immediate purge. + */ + public RelationService(boolean immediatePurgeFlag) { + + RELATION_LOGGER.log(Level.TRACE, "ENTRY"); + + setPurgeFlag(immediatePurgeFlag); + + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + 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. + *

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. + *

true is immediate purge. + * + * @param purgeFlag flag + * + * @see #getPurgeFlag + */ + public void setPurgeFlag(boolean purgeFlag) { + + myPurgeFlag = purgeFlag; + return; + } + + // + // 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: + *

- there is already a relation type with that name + *

- the same name has been used for two different role infos + *

- no role info provided + *

- 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.log(Level.TRACE, "ENTRY {0}", relationTypeName); + + // Can throw an InvalidRelationTypeException + RelationType relType = + new RelationTypeSupport(relationTypeName, roleInfoArray); + + addRelationTypeInt(relType); + + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + 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: + *

- the same name has been used for two different roles + *

- no role info provided + *

- one null role info provided + *

- 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.log(Level.TRACE, "ENTRY"); + + // Checks the role infos + List 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.log(Level.TRACE, "RETURN"); + return; + } + + /** + * Retrieves names of all known relation types. + * + * @return ArrayList of relation type names (Strings) + */ + public List getAllRelationTypeNames() { + ArrayList result; + synchronized(myRelType2ObjMap) { + result = new ArrayList(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 getRoleInfos(String relationTypeName) + throws IllegalArgumentException, + RelationTypeNotFoundException { + + if (relationTypeName == null) { + String excMsg = "Invalid parameter."; + throw new IllegalArgumentException(excMsg); + } + + RELATION_LOGGER.log(Level.TRACE, "ENTRY {0}", relationTypeName); + + // Can throw a RelationTypeNotFoundException + RelationType relType = getRelationType(relationTypeName); + + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + 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.log(Level.TRACE, "ENTRY {0} {1}", + relationTypeName, roleInfoName); + + // Can throw a RelationTypeNotFoundException + RelationType relType = getRelationType(relationTypeName); + + // Can throw a RoleInfoNotFoundException + RoleInfo roleInfo = relType.getRoleInfo(roleInfoName); + + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + return roleInfo; + } + + /** + * Removes given relation type from Relation Service. + *

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.log(Level.TRACE, "ENTRY {0}", 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 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 relIdList1 = + myRelType2RelIdsMap.get(relationTypeName); + if (relIdList1 != null) { + relIdList = new ArrayList(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.log(Level.TRACE, "RETURN"); + return; + } + + // + // Relation handling + // + + /** + * Creates a simple relation (represented by a RelationSupport object) of + * given relation type, and adds it in the Relation Service. + *

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. + *

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: + *

- 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 + */ + 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.log(Level.TRACE, "ENTRY {0} {1} {2}", + 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.log(Level.TRACE, "RETURN"); + return; + } + + /** + * Adds an MBean created by the user (and registered by him in the MBean + * Server) as a relation in the Relation Service. + *

To be added as a relation, the MBean must conform to the + * following: + *

- implement the Relation interface + *

- have for RelationService ObjectName the ObjectName of current + * Relation Service + *

- have a relation id unique and unused in current Relation Service + *

- have for relation type a relation type created in the Relation + * Service + *

- 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: + *

- no relation identifier in MBean + *

- 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: + *

- no Relation Service name in MBean + *

- the Relation Service name in the MBean is not the one of the + * current Relation Service + * @exception RelationTypeNotFoundException if: + *

- no relation type name in MBean + *

- the relation type name in MBean does not correspond to a relation + * type created in the Relation Service + * @exception InvalidRoleValueException if: + *

- the number of referenced MBeans in a role is less than + * expected minimum degree + *

- the number of referenced MBeans in a role 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 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.log(Level.TRACE, "ENTRY {0}", 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; + 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; + 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; + 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; + 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 newRefList = new ArrayList(); + newRefList.add(relationObjectName); + updateUnregistrationListener(newRefList, null); + + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + 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.log(Level.TRACE, "ENTRY {0}", 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.log(Level.TRACE, "ENTRY {0}", 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.log(Level.TRACE, "ENTRY {0}", 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 getAllRelationIds() { + List result; + synchronized(myRelId2ObjMap) { + result = new ArrayList(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: + *

- 0 if role can be read + *

- integer corresponding to RoleStatus.NO_ROLE_WITH_NAME + *

- 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.log(Level.TRACE, "ENTRY {0} {1}", + roleName, relationTypeName); + + Integer result; + + // 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 = Integer.valueOf(RoleStatus.NO_ROLE_WITH_NAME); + } + + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + 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: + *

- 0 if role can be set + *

- integer corresponding to RoleStatus.NO_ROLE_WITH_NAME + *

- integer for RoleStatus.ROLE_NOT_WRITABLE + *

- integer for RoleStatus.LESS_THAN_MIN_ROLE_DEGREE + *

- integer for RoleStatus.MORE_THAN_MAX_ROLE_DEGREE + *

- integer for RoleStatus.REF_MBEAN_OF_INCORRECT_CLASS + *

- 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.log(Level.TRACE, "ENTRY {0} {1} {2}", + role, relationTypeName, initFlag); + + // Can throw a RelationTypeNotFoundException + RelationType relType = getRelationType(relationTypeName); + + String roleName = role.getRoleName(); + List roleValue = role.getRoleValue(); + boolean writeChkFlag = true; + if (initFlag.booleanValue()) { + writeChkFlag = false; + } + + RoleInfo roleInfo; + try { + roleInfo = relType.getRoleInfo(roleName); + } catch (RoleInfoNotFoundException exc) { + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + return Integer.valueOf(RoleStatus.NO_ROLE_WITH_NAME); + } + + Integer result = checkRoleInt(2, + roleName, + roleValue, + roleInfo, + writeChkFlag); + + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + return result; + } + + /** + * Sends a notification (RelationNotification) for a relation creation. + * The notification type is: + *

- RelationNotification.RELATION_BASIC_CREATION if the relation is an + * object internal to the Relation Service + *

- RelationNotification.RELATION_MBEAN_CREATION if the relation is a + * MBean added as a relation. + *

The source object is the Relation Service itself. + *

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.log(Level.TRACE, "ENTRY {0}", 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.log(Level.TRACE, "RETURN"); + return; + } + + /** + * Sends a notification (RelationNotification) for a role update in the + * given relation. The notification type is: + *

- RelationNotification.RELATION_BASIC_UPDATE if the relation is an + * object internal to the Relation Service + *

- RelationNotification.RELATION_MBEAN_UPDATE if the relation is a + * MBean added as a relation. + *

The source object is the Relation Service itself. + *

It is called in relation MBean setRole() (for given role) and + * setRoles() (for each role) methods (implementation provided in + * RelationSupport class). + *

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 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(oldValue); + + RELATION_LOGGER.log(Level.TRACE, "ENTRY {0} {1} {2}", + relationId, newRole, oldValue); + + String roleName = newRole.getRoleName(); + List 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.log(Level.TRACE, "RETURN"); + } + + /** + * Sends a notification (RelationNotification) for a relation removal. + * The notification type is: + *

- RelationNotification.RELATION_BASIC_REMOVAL if the relation is an + * object internal to the Relation Service + *

- RelationNotification.RELATION_MBEAN_REMOVAL if the relation is a + * MBean added as a relation. + *

The source object is the Relation Service itself. + *

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 unregMBeanList) + throws IllegalArgumentException, + RelationNotFoundException { + + if (relationId == null) { + String excMsg = "Invalid parameter"; + throw new IllegalArgumentException(excMsg); + } + + RELATION_LOGGER.log(Level.TRACE, "ENTRY {0} {1}", + relationId, unregMBeanList); + + // Can throw RelationNotFoundException + sendNotificationInt(3, + "Removal of relation " + relationId, + relationId, + unregMBeanList, + null, + null, + null); + + + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + return; + } + + /** + * Handles update of the Relation Service role map for the update of given + * role in given relation. + *

It is called in relation MBean setRole() (for given role) and + * setRoles() (for each role) methods (implementation provided in + * RelationSupport class). + *

It is also called in Relation Service setRole() (for given role) and + * setRoles() (for each role) methods. + *

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 oldValue) + throws IllegalArgumentException, + RelationServiceNotRegisteredException, + RelationNotFoundException { + + if (relationId == null || + newRole == null || + oldValue == null) { + String excMsg = "Invalid parameter."; + throw new IllegalArgumentException(excMsg); + } + + RELATION_LOGGER.log(Level.TRACE, "ENTRY {0} {1} {2}", + 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 newRoleValue = newRole.getRoleValue(); + // Note: no need to test if oldValue not null before cloning, + // tested above. + List oldRoleValue = + new ArrayList(oldValue); + + // List of ObjectNames of new referenced MBeans + List newRefList = new ArrayList(); + + 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 obsRefList = new ArrayList(); + + // 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.log(Level.TRACE, "RETURN"); + return; + } + + /** + * Removes given relation from the Relation Service. + *

A RelationNotification notification is sent, its type being: + *

- RelationNotification.RELATION_BASIC_REMOVAL if the relation was + * only internal to the Relation Service + *

- RelationNotification.RELATION_MBEAN_REMOVAL if the relation is + * registered as an MBean. + *

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.log(Level.TRACE, "ENTRY {0}", 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 obsRefList = new ArrayList(); + 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 refMBeanList = new ArrayList(); + // List of MBeans no longer referenced in any relation, to be + // removed fom the map + List nonRefObjNameList = new ArrayList(); + + synchronized(myRefedMBeanObjName2RelIdsMap) { + + for (ObjectName currRefObjName : + myRefedMBeanObjName2RelIdsMap.keySet()) { + + // Retrieves relations where the MBean is referenced + Map> 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; + synchronized(myRelId2RelTypeMap) { + relTypeName = myRelId2RelTypeMap.get(relationId); + myRelId2RelTypeMap.remove(relationId); + } + // - Relation type name to relation id map + synchronized(myRelType2RelIdsMap) { + List 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.log(Level.TRACE, "RETURN"); + return; + } + + /** + * Purges the relations. + * + *

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). + *

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. + * + *

The behavior depends on the cardinality of the role where the + * unregistered MBean is referenced: + *

- if removing one MBean reference in the role makes its number of + * references less than the minimum degree, the relation has to be removed. + *

- 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.log(Level.TRACE, "ENTRY"); + + // 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 localUnregNtfList; + synchronized(myRefedMBeanObjName2RelIdsMap) { + localUnregNtfList = + new ArrayList(myUnregNtfList); + // Resets list + myUnregNtfList = new ArrayList(); + } + + + // 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 obsRefList = new ArrayList(); + // Map including ObjectNames for unregistered MBeans, with + // referencing relation ids and roles + Map>> localMBean2RelIdMap = + new HashMap>>(); + + 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> 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> localRelIdMap = + localMBean2RelIdMap.get(unregMBeanName); + + // List of relation ids where the unregistered MBean is + // referenced + for (Map.Entry> currRel : + localRelIdMap.entrySet()) { + final String currRelId = currRel.getKey(); + // List of roles of the relation where the MBean is + // referenced + List 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.log(Level.TRACE, "RETURN"); + return; + } + + /** + * Retrieves the relations where a given MBean is referenced. + *

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> + findReferencingRelations(ObjectName mbeanName, + String relationTypeName, + String roleName) + throws IllegalArgumentException { + + if (mbeanName == null) { + String excMsg = "Invalid parameter."; + throw new IllegalArgumentException(excMsg); + } + + RELATION_LOGGER.log(Level.TRACE, "ENTRY {0} {1} {2}", + mbeanName, relationTypeName, roleName); + + Map> result = new HashMap>(); + + synchronized(myRefedMBeanObjName2RelIdsMap) { + + // Retrieves the relations referencing the MBean + Map> relId2RoleNamesMap = + myRefedMBeanObjName2RelIdsMap.get(mbeanName); + + if (relId2RoleNamesMap != null) { + + // Relation Ids where the MBean is referenced + Set allRelIdSet = relId2RoleNamesMap.keySet(); + + // List of relation ids of interest regarding the selected + // relation type + List relIdList; + if (relationTypeName == null) { + // Considers all relations + relIdList = new ArrayList(allRelIdSet); + + } else { + + relIdList = new ArrayList(); + + // Considers only the relation ids for relations of given + // type + for (String currRelId : allRelIdSet) { + + // Retrieves its relation type + String currRelTypeName; + 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 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(currRoleNameList)); + + } else if (currRoleNameList.contains(roleName)) { + // Filters only the relations where the MBean is + // referenced in // given role + List dummyList = new ArrayList(); + dummyList.add(roleName); + result.put(currRelId, dummyList); + } + } + } + } + + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + return result; + } + + /** + * Retrieves the MBeans associated to given one in a relation. + *

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> + findAssociatedMBeans(ObjectName mbeanName, + String relationTypeName, + String roleName) + throws IllegalArgumentException { + + if (mbeanName == null) { + String excMsg = "Invalid parameter."; + throw new IllegalArgumentException(excMsg); + } + + RELATION_LOGGER.log(Level.TRACE, "ENTRY {0} {1} {2}", + mbeanName, relationTypeName, roleName); + + // Retrieves the map -> for those + // criterias + Map> relId2RoleNamesMap = + findReferencingRelations(mbeanName, + relationTypeName, + roleName); + + Map> result = + new HashMap>(); + + for (String currRelId : relId2RoleNamesMap.keySet()) { + + // Retrieves ObjectNames of MBeans referenced in this relation + // + // Shall not throw a RelationNotFoundException if incorrect status + // of maps :( + Map> 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 -> 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 currRelIdList = result.get(currObjName); + if (currRelIdList == null) { + + currRelIdList = new ArrayList(); + currRelIdList.add(currRelId); + result.put(currObjName, currRelIdList); + + } else { + currRelIdList.add(currRelId); + } + } + } + } + + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + 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 findRelationsOfType(String relationTypeName) + throws IllegalArgumentException, + RelationTypeNotFoundException { + + if (relationTypeName == null) { + String excMsg = "Invalid parameter."; + throw new IllegalArgumentException(excMsg); + } + + RELATION_LOGGER.log(Level.TRACE, "ENTRY"); + + // Can throw RelationTypeNotFoundException + RelationType relType = getRelationType(relationTypeName); + + List result; + synchronized(myRelType2RelIdsMap) { + List result1 = myRelType2RelIdsMap.get(relationTypeName); + if (result1 == null) + result = new ArrayList(); + else + result = new ArrayList(result1); + } + + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + 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: + *

- there is no role with given name + *

or + *

- the role is not readable. + * + * @see #setRole + */ + public List 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.log(Level.TRACE, "ENTRY {0} {1}", + relationId, roleName); + + // Can throw RelationServiceNotRegisteredException + isActive(); + + // Can throw a RelationNotFoundException + Object relObj = getRelation(relationId); + + List 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 invokeResult = cast( + myMBeanServer.invoke(((ObjectName)relObj), + "getRole", + params, + signature)); + if (invokeResult == null || invokeResult instanceof ArrayList) + result = invokeResult; + else + result = new ArrayList(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.log(Level.TRACE, "RETURN"); + 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.log(Level.TRACE, "ENTRY {0}", relationId); + + // Can throw RelationServiceNotRegisteredException + isActive(); + + // Can throw a RelationNotFoundException + Object relObj = getRelation(relationId); + + RoleResult result; + + 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.log(Level.TRACE, "RETURN"); + 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.log(Level.TRACE, "ENTRY {0}", relationId); + + // Can throw a RelationNotFoundException + Object relObj = getRelation(relationId); + + RoleResult result; + + 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.log(Level.TRACE, "RETURN"); + 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.log(Level.TRACE, "ENTRY {0} {1}", + relationId, roleName); + + // Can throw a RelationNotFoundException + Object relObj = getRelation(relationId); + + Integer result; + + 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.log(Level.TRACE, "RETURN"); + return result; + } + + /** + * Sets the given role in given relation. + *

Will check the role according to its corresponding role definition + * provided in relation's relation type + *

The Relation Service will keep track of the change to keep the + * consistency of relations by handling referenced MBean deregistrations. + * + * @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: + *

- the number of referenced MBeans in given value is less than + * expected minimum degree + *

or + *

- the number of referenced MBeans in provided value exceeds expected + * maximum degree + *

or + *

- one referenced MBean in the value is not an Object of the MBean + * class expected for that role + *

or + *

- 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.log(Level.TRACE, "ENTRY {0} {1}", + 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.log(Level.TRACE, "RETURN"); + return; + } + + /** + * Sets the given roles in given relation. + *

Will check the role according to its corresponding role definition + * provided in relation's relation type + *

The Relation Service keeps track of the changes to keep the + * consistency of relations by handling referenced MBean deregistrations. + * + * @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.log(Level.TRACE, "ENTRY {0} {1}", + relationId, roleList); + + // Can throw RelationServiceNotRegisteredException + isActive(); + + // Can throw a RelationNotFoundException + Object relObj = getRelation(relationId); + + RoleResult result; + + 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.log(Level.TRACE, "RETURN"); + return result; + } + + /** + * Retrieves MBeans referenced in the various roles of the relation. + * + * @param relationId relation id + * + * @return a HashMap mapping: + *

ObjectName {@literal ->} ArrayList of String (role names) + * + * @exception IllegalArgumentException if null parameter + * @exception RelationNotFoundException if no relation for given + * relation id + */ + public Map> + getReferencedMBeans(String relationId) + throws IllegalArgumentException, + RelationNotFoundException { + + if (relationId == null) { + String excMsg = "Invalid parameter."; + throw new IllegalArgumentException(excMsg); + } + + RELATION_LOGGER.log(Level.TRACE, "ENTRY {0}", + relationId); + + // Can throw a RelationNotFoundException + Object relObj = getRelation(relationId); + + Map> 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.log(Level.TRACE, "RETURN"); + 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.log(Level.TRACE, "ENTRY {0}", relationId); + + // Can throw a RelationNotFoundException + Object relObj = getRelation(relationId); + + String result; + + 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.log(Level.TRACE, "RETURN"); + 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.log(Level.TRACE, "ENTRY {0}", 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; + 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.log(Level.TRACE, "RETURN"); + 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.log(Level.TRACE, "ENTRY"); + + 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.log(Level.TRACE, "RETURN"); + 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.log(Level.TRACE, "ENTRY"); + + 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.log(Level.TRACE, "RETURN"); + 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.log(Level.TRACE, "ENTRY {0}", relationTypeName); + + // No null relation type accepted, so can use get() + RelationType relType; + 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.log(Level.TRACE, "RETURN"); + 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.log(Level.TRACE, "ENTRY {0}", relationId); + + // No null relation accepted, so can use get() + Object rel; + synchronized(myRelId2ObjMap) { + rel = myRelId2ObjMap.get(relationId); + } + + if (rel == null) { + String excMsg = "No relation associated to relation id " + relationId; + throw new RelationNotFoundException(excMsg); + } + + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + 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.log(Level.TRACE, "ENTRY {0} {1} {2}", + objectName, relationId, roleName); + + boolean isNewFlag = false; + + synchronized(myRefedMBeanObjName2RelIdsMap) { + + // Checks if the MBean was already referenced + // No null value allowed, use get() directly + Map> 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 roleNames = new ArrayList(); + roleNames.add(roleName); + + // Map of relations where the MBean is referenced + mbeanRefMap = new HashMap>(); + 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 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(); + 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.log(Level.TRACE, "RETURN"); + 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.log(Level.TRACE, "ENTRY {0} {1} {2} {3}", + 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. + Map> mbeanRefMap = + (myRefedMBeanObjName2RelIdsMap.get(objectName)); + + if (mbeanRefMap == null) { + // The MBean is no longer referenced + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + return true; + } + + List roleNames = null; + if (!allRolesFlag) { + // Now retrieves the roles of current relation where the MBean + // was referenced + roleNames = 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.log(Level.TRACE, "RETURN"); + return noLongerRefFlag; + } + + // Updates the listener registered to the MBean Server to be informed of + // referenced MBean deregistrations + // + // -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.log(Level.TRACE, "ENTRY {0} {1}", + 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 (ObjectName newObjName : newRefList) + myUnregNtfFilter.enableObjectName(newObjName); + } + + if (obsoleteRefList != null) { + // Disables ObjectNames in obsoleteRefList + for (ObjectName obsObjName : obsoleteRefList) + 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.log(Level.TRACE, "RETURN"); + 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.log(Level.TRACE, + "ENTRY {0} {1} {2} {3} {4} {5}", + 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. + List roleInfoList = new ArrayList(relType.getRoleInfos()); + + if (roleList != null) { + + for (Role currRole : roleList.asList()) { + String currRoleName = currRole.getRoleName(); + List currRoleValue = currRole.getRoleValue(); + // Retrieves corresponding role info + // Can throw a RoleInfoNotFoundException to be converted into a + // RoleNotFoundException + RoleInfo roleInfo; + 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 relIdList = + myRelType2RelIdsMap.get(relationTypeName); + boolean firstRelFlag = false; + if (relIdList == null) { + firstRelFlag = true; + relIdList = new ArrayList(); + } + 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 (Role currRole : roleList.asList()) { + // Creates a dummy empty ArrayList of ObjectNames to be the old + // role value :) + List dummyList = new ArrayList(); + // 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.log(Level.TRACE, "RETURN"); + 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.log(Level.TRACE, "ENTRY {0} {1} {2} {3} {4}", + chkType, roleName, roleValue, roleInfo, writeChkFlag); + + // Compares names + String expName = roleInfo.getName(); + if (!(roleName.equals(expName))) { + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + return Integer.valueOf(RoleStatus.NO_ROLE_WITH_NAME); + } + + // Checks read access if required + if (chkType == 1) { + boolean isReadable = roleInfo.isReadable(); + if (!isReadable) { + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + return Integer.valueOf(RoleStatus.ROLE_NOT_READABLE); + } else { + // End of check :) + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + return 0; + } + } + + // Checks write access if required + if (writeChkFlag) { + boolean isWritable = roleInfo.isWritable(); + if (!isWritable) { + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + return RoleStatus.ROLE_NOT_WRITABLE; + } + } + + int refNbr = roleValue.size(); + + // Checks minimum cardinality + boolean chkMinFlag = roleInfo.checkMinDegree(refNbr); + if (!chkMinFlag) { + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + return RoleStatus.LESS_THAN_MIN_ROLE_DEGREE; + } + + // Checks maximum cardinality + boolean chkMaxFlag = roleInfo.checkMaxDegree(refNbr); + if (!chkMaxFlag) { + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + return 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 (ObjectName currObjName : roleValue) { + + // Checks it is registered + if (currObjName == null) { + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + return 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.log(Level.TRACE, "RETURN"); + return RoleStatus.REF_MBEAN_OF_INCORRECT_CLASS; + } + + } catch (InstanceNotFoundException exc) { + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + return RoleStatus.REF_MBEAN_NOT_REGISTERED; + } + } + + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + return 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.log(Level.TRACE, "ENTRY {0} {1} {2} {3} {4} {5}", + 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 (RoleInfo currRoleInfo : roleInfoList) { + + String roleName = currRoleInfo.getName(); + + // Creates an empty value + List emptyValue = new ArrayList(); + // 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.log(Level.TRACE, "RETURN"); + 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 unregMBeanList, + String roleName, + List roleNewValue, + List 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.log(Level.TRACE, "ENTRY {0} {1} {2} {3} {4} {5} {6}", + 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; + 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 seqNo = atomicSeqNo.incrementAndGet(); + + // 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, + seqNo.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, + seqNo.longValue(), + timeStamp, + message, + relationId, + relTypeName, + relObjName, + roleName, + roleNewValue, + oldValue); + } + + sendNotification(ntf); + + RELATION_LOGGER.log(Level.TRACE, "RETURN"); + 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.log(Level.TRACE, "ENTRY {0} {1} {2}", + 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 (String currRoleName : roleNameList) { + + if (deleteRelFlag) { + break; + } + + // 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; + 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 (String currRoleName : roleNameList) { + + 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.log(Level.TRACE, "RETURN"); + return; + } +}