001: /* JFox, the OpenSource J2EE Application Server
002: *
003: * Copyright (C) 2002 huihoo.org
004: * Distributable under GNU LGPL license
005: * See the GNU Lesser General Public License for more details.
006: */
007:
008: package javax.management.relation;
009:
010: import java.util.HashMap;
011: import java.util.ArrayList;
012: import java.util.Iterator;
013: import java.util.List;
014: import java.util.Map;
015: import javax.management.MBeanRegistration;
016: import javax.management.ObjectName;
017: import javax.management.MBeanServer;
018: import javax.management.MBeanException;
019: import javax.management.ReflectionException;
020: import javax.management.InstanceNotFoundException;
021: import javax.management.JMRuntimeException;
022:
023: /**
024: *
025: * @author <a href="mailto:young_yy@hotmail.org">Young Yang</a>
026: */
027:
028: /**
029: * A RelationSupport object is used internally by the Relation Service to
030: * represent simple relations (only roles, no properties or methods), with an
031: * unlimited number of roles, of any relation type. As internal representation,
032: * it is not exposed to the user.
033: * <P>RelationSupport class conforms to the design patterns of standard MBean. So
034: * the user can decide to instantiate a RelationSupport object himself as
035: * a MBean (as it follows the MBean design patterns), to register it in the
036: * MBean Server, and then to add it in the Relation Service.
037: * <P>The user can also, when creating his own MBean relation class, have it
038: * extending RelationSupport, to retrieve the implementations of required
039: * interfaces (see below).
040: * <P>It is also possible to have in a user relation MBean class a member
041: * being a RelationSupport object, and to implement the required interfaces by
042: * delegating all to this member.
043: * <P> RelationSupport implements the Relation interface (to be handled by the
044: * Relation Service).
045: * <P>It implements also the MBeanRegistration interface to be able to retrieve
046: * the MBean Server where it is registered (if registered as a MBean) to access
047: * to its Relation Service.
048: */
049: public class RelationSupport implements RelationSupportMBean,
050: MBeanRegistration {
051:
052: // Relation identifier (expected to be unique in the Relation Service where
053: // the RelationSupport object will be added)
054: protected String id = null;
055:
056: // ObjectName of the Relation Service where the relation will be added
057: // REQUIRED if the RelationSupport is created by the user to be registered as
058: // a MBean, as it will have to access the Relation Service via the MBean
059: // Server to perform the check regarding the relation type.
060: // Is null if current object is directly created by the Relation Service,
061: // as the object will directly access it.
062: private ObjectName relationServiceObjectName = null;
063:
064: // Reference to the MBean Server where the Relation Service is
065: // registered
066: // REQUIRED if the RelationSupport is created by the user to be registered as
067: // a MBean, as it will have to access the Relation Service via the MBean
068: // Server to perform the check regarding the relation type.
069: // If the Relationbase object is created by the Relation Service (use of
070: // createRelation() method), this is null as not needed, direct access to
071: // the Relation Service.
072: // If the Relationbase object is created by the user and registered as a
073: // MBean, this is set by the preRegister() method below.
074: private MBeanServer mbeanServer = null;
075:
076: // Relation type name (must be known in the Relation Service where the
077: // relation will be added)
078: protected String relationTypeName = null;
079:
080: // Role map, mapping <role-name> -> <Role>
081: // Initialised by role list in the constructor, then updated:
082: // - if the relation is a MBean, via setRole() and setRoles() methods, or
083: // via Relation Service setRole() and setRoles() methods
084: // - if the relation is internal to the Relation Service, via
085: // setRoleInt() and setRolesInt() methods.
086: protected Map roleMap = new HashMap();
087:
088: // Flag to indicate if the object has been added in the Relation Service
089: // private Boolean isInRelationService = null;
090: private boolean isInRelationService = false;
091:
092: //
093: // Constructors
094: //
095:
096: /**
097: * Creates object.
098: * <P>This constructor has to be used when the RelationSupport object will be
099: * registered as a MBean by the user, or when creating a user relation
100: * MBean those class extends RelationSupport.
101: * <P>Nothing is done at the Relation Service level, i.e. the RelationSupport
102: * object is not added, and no check if the provided values are correct.
103: * The object is always created, EXCEPT if:
104: * <P>- one mandatory parameter is not provided
105: * <P>- the same name is used for two roles.
106: * <P>To be handled as a relation, the object has then to be added in the
107: * Relation Service using Relation Service method addRelation().
108: *
109: * @param relationId relation identifier, to identify the relation in the
110: * Relation Service.
111: * <P>Expected to be unique in the given Relation Service.
112: * @param relationServiceObjectName ObjectName of the Relation Service where
113: * the relation will be registered.
114: * <P>It is required as this is the Relation Service that is aware of the
115: * definition of the relation type of given relation, so that will be able
116: * to check update operations (set).
117: * @param relationTypeName Name of relation type.
118: * <P>Expected to have been created in given Relation Service.
119: * @param roleList list of roles (Role objects) to initialised the
120: * relation. Can be null.
121: * <P>Expected to conform to relation info in associated relation type.
122: *
123: * @exception InvalidRoleValueException if the same name is used for two
124: * roles.
125: * @exception IllegalArgumentException if a required value (Relation
126: * Service Object Name, etc.) is not provided as parameter.
127: */
128: public RelationSupport(String relationId,
129: ObjectName relationServiceObjectName,
130: String relationTypeName, RoleList roleList)
131: throws InvalidRoleValueException, IllegalArgumentException {
132: if (relationId == null || relationServiceObjectName == null
133: || relationTypeName == null) {
134: throw new IllegalArgumentException(
135: "Invalid parameter: relationId and relationServiceObjectName and relationTypeName can not be null");
136: }
137:
138: this .id = relationId;
139: this .relationServiceObjectName = relationServiceObjectName;
140: this .relationTypeName = relationTypeName;
141:
142: if (roleList == null)
143: return;
144: roleMap = _constructRoleMap(roleList);
145: }
146:
147: public RelationSupport(String relationId,
148: ObjectName relationServiceObjectName,
149: MBeanServer mbeanServer, String relationTypeName,
150: RoleList roleList) throws InvalidRoleValueException,
151: IllegalArgumentException {
152: this (relationId, relationServiceObjectName, relationTypeName,
153: roleList);
154: if (mbeanServer == null) {
155: throw new IllegalArgumentException(
156: "Invalid parameter: mbeanServe can not be null");
157: }
158: this .mbeanServer = mbeanServer;
159: }
160:
161: /**
162: * Retrieves role value for given role name.
163: * <P>Checks if the role exists and is readable according to the relation
164: * type.
165: *
166: * @param roleName name of role
167: *
168: * @return the ArrayList of ObjectName objects being the role value
169: *
170: * @exception IllegalArgumentException if null role name
171: * @exception RoleNotFoundException if:
172: * <P>- there is no role with given name
173: * <P>- the role is not readable.
174: * @exception RelationServiceNotRegisteredException if the Relation
175: * Service is not registered in the MBean Server
176: */
177: public List getRole(String roleName)
178: throws IllegalArgumentException, RoleNotFoundException,
179: RelationServiceNotRegisteredException {
180:
181: if (roleName == null) {
182: throw new IllegalArgumentException(
183: "Invalid parameter: roleName can not be null");
184: }
185:
186: // Can throw RoleNotFoundException and
187: // RelationServiceNotRegisteredException
188: Object result = this ._getRole(roleName);
189: if (result instanceof RoleUnresolved) {
190: throw new RoleNotFoundException(result.toString());
191: }
192: return ((Role) result).getRoleValue();
193: }
194:
195: /**
196: * Retrieves values of roles with given names.
197: * <P>Checks for each role if it exists and is readable according to the
198: * relation type.
199: *
200: * @param roleNames array of names of roles to be retrieved
201: *
202: * @return a RoleResult object, including a RoleList (for roles
203: * succcessfully retrieved) and a RoleUnresolvedList (for roles not
204: * retrieved).
205: *
206: * @exception IllegalArgumentException if null role name
207: * @exception RelationServiceNotRegisteredException if the Relation
208: * Service is not registered in the MBean Server
209: */
210: public RoleResult getRoles(String[] roleNames)
211: throws IllegalArgumentException,
212: RelationServiceNotRegisteredException {
213:
214: if (roleNames == null || roleNames.length == 0) {
215: throw new IllegalArgumentException(
216: "Invalid parameter: roleNames can not be null or empty.");
217: }
218: RoleList roleList = new RoleList();
219: RoleUnresolvedList roleUnresList = new RoleUnresolvedList();
220:
221: for (int i = 0; i < roleNames.length; i++) {
222: String roleName = roleNames[i];
223: Object result = this ._getRole(roleName);
224: if (result instanceof Role) {
225: roleList.add((Role) result);
226: } else if (result instanceof RoleUnresolved) {
227: roleUnresList.add((RoleUnresolved) result);
228: }
229: }
230:
231: return new RoleResult(roleList, roleUnresList);
232: }
233:
234: /**
235: * Returns all roles present in the relation
236: *
237: * @return a RoleResult object, including a RoleList (for roles
238: * succcessfully retrieved) and a RoleUnresolvedList (for roles not
239: * readable).
240: *
241: * @exception RelationServiceNotRegisteredException if the Relation
242: * Service is not registered in the MBean Server
243: */
244: public RoleResult getAllRoles()
245: throws RelationServiceNotRegisteredException {
246: String[] roleNames = (String[]) roleMap.keySet().toArray(
247: new String[0]);
248: return this .getRoles(roleNames);
249: }
250:
251: /**
252: * Returns all roles in the relation without checking read mode
253: *
254: * @return a RoleList
255: */
256: public RoleList retrieveAllRoles() {
257: return new RoleList(new ArrayList(roleMap.values()));
258: }
259:
260: /**
261: * Returns the number of MBeans currently referenced in the given role
262: *
263: * @param roleName name of role
264: *
265: * @return the number of currently referenced MBeans in that role
266: *
267: * @exception IllegalArgumentException if null role name
268: * @exception RoleNotFoundException if there is no role with given name
269: */
270: public Integer getRoleCardinality(String roleName)
271: throws IllegalArgumentException, RoleNotFoundException {
272:
273: if (roleName == null) {
274: throw new IllegalArgumentException(
275: "Invalid parameter: roleName can not be null.");
276: }
277:
278: // Try to retrieve the role
279: Role role = (Role) (roleMap.get(roleName));
280: if (role == null) {
281: throw new RoleNotFoundException(roleName);
282: }
283:
284: int size = role.getRoleValue().size();
285:
286: return new Integer(size);
287: }
288:
289: /**
290: * Sets the given role.
291: * <P>Will check the role according to its corresponding role definition
292: * provided in relation's relation type
293: * <P>Will send a notification (RelationNotification with type
294: * RELATION_BASIC_UPDATE or RELATION_MBEAN_UPDATE, depending if the
295: * relation is a MBean or not).
296: *
297: * @param role role to be set (name and new value)
298: *
299: * @exception IllegalArgumentException if null role
300: * @exception RoleNotFoundException if the role is not writable (no
301: * test on the write access mode performed when initialising the role)
302: * @exception InvalidRoleValueException if value provided for
303: * role is not valid, i.e.:
304: * <P>- the number of referenced MBeans in given value is less than
305: * expected minimum degree
306: * <P>- the number of referenced MBeans in provided value exceeds expected
307: * maximum degree
308: * <P>- one referenced MBean in the value is not an Object of the MBean
309: * class expected for that role
310: * <P>- a MBean provided for that role does not exist
311: * @exception RelationServiceNotRegisteredException if the Relation
312: * Service is not registered in the MBean Server
313: * @exception RelationTypeNotFoundException if the relation type has not
314: * been declared in the Relation Service
315: * @exception RelationNotFoundException if the relation has not been
316: * added in the Relation Service.
317: */
318: public void setRole(Role role) throws IllegalArgumentException,
319: RoleNotFoundException, RelationTypeNotFoundException,
320: InvalidRoleValueException,
321: RelationServiceNotRegisteredException,
322: RelationNotFoundException {
323:
324: if (role == null) {
325: throw new IllegalArgumentException(
326: "Invalid parameter: role can not be null.");
327: }
328:
329: Object result = this ._setRole(role);
330: if (result instanceof RoleUnresolved) {
331: int status = ((RoleUnresolved) result).getProblemType();
332: if (status == RoleStatus.ROLE_NOT_WRITABLE) { // if ont writeable ,throw RoleNotFoundException
333: throw new RoleNotFoundException(result.toString());
334: } else {
335: throw new InvalidRoleValueException(result.toString());
336: }
337: }
338: }
339:
340: /**
341: * Sets the given roles.
342: * <P>Will check the role according to its corresponding role definition
343: * provided in relation's relation type
344: * <P>Will send one notification (RelationNotification with type
345: * RELATION_BASIC_UPDATE or RELATION_MBEAN_UPDATE, depending if the
346: * relation is a MBean or not) per updated role.
347: *
348: * @param roleList list of roles to be set
349: *
350: * @return a RoleResult object, including a RoleList (for roles
351: * succcessfully set) and a RoleUnresolvedList (for roles not
352: * set).
353: *
354: * @exception IllegalArgumentException if null role name
355: * @exception RelationServiceNotRegisteredException if the Relation
356: * Service is not registered in the MBean Server
357: * @exception RelationTypeNotFoundException if the relation type has not
358: * been declared in the Relation Service.
359: * @exception RelationNotFoundException if the relation MBean has not been
360: * added in the Relation Service.
361: */
362: public RoleResult setRoles(RoleList roleList)
363: throws IllegalArgumentException,
364: RelationServiceNotRegisteredException,
365: RelationTypeNotFoundException, RelationNotFoundException {
366:
367: if (roleList == null) {
368: throw new IllegalArgumentException(
369: "Invalid parameter: roleList can not be null.");
370: }
371: RoleList _roleList = new RoleList();
372: RoleUnresolvedList roleUnresList = new RoleUnresolvedList();
373:
374: for (Iterator it = roleList.iterator(); it.hasNext();) {
375: Role role = (Role) it.next();
376: Object result = this ._setRole(role);
377: if (result instanceof Role) {
378: _roleList.add((Role) result);
379: } else if (result instanceof RoleUnresolved) {
380: roleUnresList.add((RoleUnresolved) result);
381: }
382: }
383:
384: return new RoleResult(_roleList, roleUnresList);
385:
386: }
387:
388: /**
389: * Callback used by the Relation Service when a MBean referenced in a role
390: * is unregistered.
391: * <P>The Relation Service will call this method to let the relation
392: * take action to reflect the impact of such unregistration.
393: * <P>BEWARE. the user is not expected to call this method.
394: * <P>Current implementation is to set the role with its current value
395: * (list of ObjectNames of referenced MBeans) without the unregistered
396: * one.
397: *
398: * @param objectName ObjectName of unregistered MBean
399: * @param roleName name of role where the MBean is referenced
400: *
401: * @exception IllegalArgumentException if null parameter
402: * @exception RoleNotFoundException if role does not exist in the
403: * relation or is not writable
404: * @exception InvalidRoleValueException if role value does not conform to
405: * the associated role info (this will never happen when called from the
406: * Relation Service)
407: * @exception RelationServiceNotRegisteredException if the Relation
408: * Service is not registered in the MBean Server
409: * @exception RelationTypeNotFoundException if the relation type has not
410: * been declared in the Relation Service.
411: * @exception RelationNotFoundException if this method is called for a
412: * relation MBean not added in the Relation Service.
413: */
414: public void handleMBeanUnregistration(ObjectName objectName,
415: String roleName) throws IllegalArgumentException,
416: RoleNotFoundException, InvalidRoleValueException,
417: RelationServiceNotRegisteredException,
418: RelationTypeNotFoundException, RelationNotFoundException {
419:
420: if (objectName == null || roleName == null) {
421: throw new IllegalArgumentException(
422: "Invalid parameter: DEFAULT_OBJECTNAME and roleName can not be null.");
423: }
424: Role role = (Role) roleMap.get(roleName);
425: if (role == null) {
426: throw new RoleNotFoundException(roleName);
427: }
428: List roleValue = role.getRoleValue();
429: synchronized (roleValue) {
430: roleValue.remove(objectName);
431: }
432: this .setRole(new Role(roleName, roleValue));
433:
434: }
435:
436: /**
437: * Retrieves MBeans referenced in the various roles of the relation.
438: * tell us how many role's refrences a MBean has
439: *
440: * @return a HashMap mapping:
441: * <P> ObjectName -> ArrayList of String (role names)
442: */
443: public Map getReferencedMBeans() {
444: HashMap objectName2roleNames = new HashMap();
445: synchronized (roleMap) {
446: for (Iterator roleIter = (roleMap.values()).iterator(); roleIter
447: .hasNext();) {
448: Role role = (Role) (roleIter.next());
449: String roleName = role.getRoleName();
450: // Retrieves ObjectNames of MBeans referenced in current role
451: List roleValue = role.getRoleValue();
452: for (Iterator objectNameIter = roleValue.iterator(); objectNameIter
453: .hasNext();) {
454: ObjectName objectName = (ObjectName) (objectNameIter
455: .next());
456: List roleNames = (List) (objectName2roleNames
457: .get(objectName));
458: // Sees if current MBean has not already referenced in roles , put a new roleNames
459: if (roleNames == null) {
460: roleNames = new ArrayList();
461: objectName2roleNames.put(objectName, roleNames);
462: }
463: roleNames.add(roleName); // add the roleName
464: }
465: }
466: }
467: return objectName2roleNames;
468: }
469:
470: /**
471: * Returns name of associated relation type.
472: */
473: public String getRelationTypeName() {
474: return relationTypeName;
475: }
476:
477: /**
478: * Returns ObjectName of the Relation Service handling the relation
479: */
480: public ObjectName getRelationServiceName() {
481: return relationServiceObjectName;
482: }
483:
484: /**
485: * Returns relation identifier (used to uniquely identify the relation
486: * inside the Relation Service)
487: */
488: public String getRelationId() {
489: return new String(id);
490: }
491:
492: //
493: // MBeanRegistration interface
494: //
495:
496: // Pre-registration: retrieves the MBean Server (useful to access to the
497: // Relation Service)
498: // This is the way to retrieve the MBean Server when the relation object is
499: // a MBean created by the user outside of the Relation Service.
500: //
501: // No exception thrown.
502: public ObjectName preRegister(MBeanServer server, ObjectName name)
503: throws Exception {
504: mbeanServer = server;
505: if (name == null)
506: name = new ObjectName(":name=" + this .toString());
507: return name;
508: }
509:
510: // Post-registration: does nothing
511: public void postRegister(Boolean registrationDone) {
512: return;
513: }
514:
515: // Pre-unregistration: does nothing
516: public void preDeregister() throws Exception {
517:
518: }
519:
520: // Post-unregistration: does nothing
521: public void postDeregister() {
522:
523: }
524:
525: //
526: // Others
527: //
528:
529: /**
530: * Returns an internal flag specifying if the object is still handled by
531: * the Relation Service.
532: */
533: public Boolean isInRelationService() {
534: return new Boolean(isInRelationService);
535: }
536:
537: /**
538: * Sets the flag to specify that it is handled or not by the Relation
539: * Service
540: * <P>BEWARE, this method has to be exposed for the user relation MBeans,
541: * as the Relation Service will access them through their management
542: * interface. It is RECOMMENDED NOT to use this method. Using it does not
543: * affect the registration of the relation object in the Relation Service,
544: * but will provide wrong information about it!!!!
545: *
546: * @exception IllegalArgumentException if null parameter
547: */
548: public void setRelationServiceManagementFlag(Boolean flag)
549: throws IllegalArgumentException {
550: if (flag == null) {
551: throw new IllegalArgumentException(
552: "Invalid parameter: flag can not be null");
553: }
554: isInRelationService = flag.booleanValue();
555: }
556:
557: private Map _constructRoleMap(RoleList roleList)
558: throws InvalidRoleValueException {
559: Map _roleMap = new HashMap();
560: for (Iterator roleIter = roleList.iterator(); roleIter
561: .hasNext();) {
562: // No need to check if role is null, it is not allowed to store
563: // a null role in a RoleList :)
564: Role role = (Role) (roleIter.next());
565: String roleName = role.getRoleName();
566: if (_roleMap.containsKey(roleName)) {
567: throw new InvalidRoleValueException("Role name "
568: + roleName + " used for two roles");
569: }
570:
571: _roleMap.put(roleName, role.clone());
572: }
573: return _roleMap;
574: }
575:
576: // get a role, success,return Role; failed,return RoleUnresolved
577: protected Object _getRole(String roleName)
578: throws IllegalArgumentException,
579: RelationServiceNotRegisteredException {
580: if (roleName == null) {
581: throw new IllegalArgumentException(
582: "Invalid parameter: roleName can not be null.");
583: }
584:
585: Role role = (Role) roleMap.get(roleName);
586: if (role == null) { // role not exist
587: return new RoleUnresolved(roleName, null,
588: RoleStatus.NO_ROLE_WITH_NAME);
589: }
590:
591: if (mbeanServer == null) {
592: throw new JMRuntimeException(
593: "the RelationSupport object doesn't reference a MBeanServer object.");
594: }
595:
596: try {
597: Integer status = (Integer) (mbeanServer.invoke(
598: relationServiceObjectName, "checkRoleReading",
599: new String[] { roleName, relationTypeName },
600: new String[] { "java.lang.String",
601: "java.lang.String" }));
602: if (status.intValue() == 0) {
603: return (Role) (role.clone());
604: } else { // status may be RoleStatus.ROLE_NOT_READABLE,RoleStatus.ROLE_NOT_WRITABLE
605: return new RoleUnresolved(roleName, null, status
606: .intValue());
607: }
608: } catch (InstanceNotFoundException e) {
609: throw new RelationServiceNotRegisteredException(e
610: .getMessage());
611: } catch (Exception e) {
612: throw new JMRuntimeException(e.getMessage());
613: }
614: }
615:
616: // set role to the roleMap, success return itself, failed return RoleUnresolved
617: protected Object _setRole(Role role)
618: throws IllegalArgumentException,
619: RelationServiceNotRegisteredException,
620: RelationTypeNotFoundException, RelationNotFoundException {
621: if (role == null) {
622: throw new IllegalArgumentException(
623: "Invalid parameter: role can not be null.");
624: }
625: if (mbeanServer == null) {
626: throw new JMRuntimeException(
627: "the RelationSupport object doesn't reference a MBeanServer object.");
628: }
629:
630: Object result = null;
631:
632: String roleName = role.getRoleName();
633: List oldValue = new ArrayList();
634: Boolean initFlag = new Boolean(true);
635: Role oldRole = (Role) roleMap.get(roleName);
636: if (oldRole != null) {
637: initFlag = new Boolean(false);
638: oldValue = oldRole.getRoleValue();
639: }
640:
641: try {
642:
643: Integer status = (Integer) (mbeanServer.invoke(
644: relationServiceObjectName, "checkRoleWriting",
645: new Object[] { role, relationTypeName, initFlag },
646: new String[] { "javax.management.relation.Role",
647: "java.lang.String", "java.lang.Boolean" }));
648: if (status.intValue() == 0) { // success,return Role
649:
650: synchronized (roleMap) {
651: roleMap.put(roleName, role);
652: }
653:
654: if (!initFlag.booleanValue()) { // it's update operation, not init, so must sendNotification & updateRoleMap
655: // snedRoleUpdataNotification through RelationService
656: mbeanServer.invoke(relationServiceObjectName,
657: "sendRoleUpdateNotification", new Object[] {
658: id, role, oldValue }, new String[] {
659: "java.lang.String",
660: "javax.management.relation.Role",
661: "java.util.List" });
662: // updateRoleMap of the RelationService
663: mbeanServer.invoke(relationServiceObjectName,
664: "updateRoleMap", new Object[] { id, role,
665: oldValue }, new String[] {
666: "java.lang.String",
667: "javax.management.relation.Role",
668: "java.util.List" });
669:
670: }
671: result = role.clone();
672: } else {
673: result = new RoleUnresolved(roleName, role
674: .getRoleValue(), status.intValue());
675: }
676:
677: } catch (MBeanException e) {
678: // Retrieves underlying exception
679: Exception target = e.getTargetException();
680: if (target instanceof RelationTypeNotFoundException) {
681: throw (RelationTypeNotFoundException) target;
682: } else {
683: throw new JMRuntimeException(target.getMessage());
684: }
685:
686: } catch (ReflectionException e) {
687: throw new JMRuntimeException(e.getMessage());
688:
689: } catch (InstanceNotFoundException e) {
690: throw new RelationServiceNotRegisteredException(e
691: .getMessage());
692: }
693:
694: return result;
695: }
696: }
|