1 /*
2  * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package javax.management.relation;
27 
28 import javax.management.Notification;
29 import javax.management.ObjectName;
30 
31 import java.io.InvalidObjectException;
32 import java.io.IOException;
33 import java.io.ObjectInputStream;
34 import java.io.ObjectOutputStream;
35 import java.io.ObjectStreamField;
36 
37 import java.security.AccessController;
38 import java.security.PrivilegedAction;
39 
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.Collections;
43 import java.util.HashSet;
44 import java.util.List;
45 import java.util.Set;
46 
47 import com.sun.jmx.mbeanserver.GetPropertyAction;
48 import static com.sun.jmx.mbeanserver.Util.cast;
49 
50 /**
51  * A notification of a change in the Relation Service.
52  * A RelationNotification notification is sent when a relation is created via
53  * the Relation Service, or an MBean is added as a relation in the Relation
54  * Service, or a role is updated in a relation, or a relation is removed from
55  * the Relation Service.
56  *
57  * <p>The <b>serialVersionUID</b> of this class is <code>-6871117877523310399L</code>.
58  *
59  * @since 1.5
60  */
61 @SuppressWarnings("serial")  // serialVersionUID not constant
62 public class RelationNotification extends Notification {
63 
64     // Serialization compatibility stuff:
65     // Two serial forms are supported in this class. The selected form depends
66     // on system property "jmx.serial.form":
67     //  - "1.0" for JMX 1.0
68     //  - any other value for JMX 1.1 and higher
69     //
70     // Serial version for old serial form
71     private static final long oldSerialVersionUID = -2126464566505527147L;
72     //
73     // Serial version for new serial form
74     private static final long newSerialVersionUID = -6871117877523310399L;
75     //
76     // Serializable fields in old serial form
77     private static final ObjectStreamField[] oldSerialPersistentFields =
78     {
79         new ObjectStreamField("myNewRoleValue", ArrayList.class),
80         new ObjectStreamField("myOldRoleValue", ArrayList.class),
81         new ObjectStreamField("myRelId", String.class),
82         new ObjectStreamField("myRelObjName", ObjectName.class),
83         new ObjectStreamField("myRelTypeName", String.class),
84         new ObjectStreamField("myRoleName", String.class),
85         new ObjectStreamField("myUnregMBeanList", ArrayList.class)
86     };
87     //
88     // Serializable fields in new serial form
89     private static final ObjectStreamField[] newSerialPersistentFields =
90     {
91         new ObjectStreamField("newRoleValue", List.class),
92         new ObjectStreamField("oldRoleValue", List.class),
93         new ObjectStreamField("relationId", String.class),
94         new ObjectStreamField("relationObjName", ObjectName.class),
95         new ObjectStreamField("relationTypeName", String.class),
96         new ObjectStreamField("roleName", String.class),
97         new ObjectStreamField("unregisterMBeanList", List.class)
98     };
99     //
100     // Actual serial version and serial form
101     private static final long serialVersionUID;
102     /**
103      * @serialField relationId String Relation identifier of
104      * created/removed/updated relation
105      * @serialField relationTypeName String Relation type name of
106      * created/removed/updated relation
107      * @serialField relationObjName ObjectName {@link ObjectName} of
108      * the relation MBean of created/removed/updated relation (only if
109      * the relation is represented by an MBean)
110      * @serialField unregisterMBeanList List List of {@link
111      * ObjectName}s of referenced MBeans to be unregistered due to
112      * relation removal
113      * @serialField roleName String Name of updated role (only for role update)
114      * @serialField oldRoleValue List Old role value ({@link
115      * ArrayList} of {@link ObjectName}s) (only for role update)
116      * @serialField newRoleValue List New role value ({@link
117      * ArrayList} of {@link ObjectName}s) (only for role update)
118      */
119     private static final ObjectStreamField[] serialPersistentFields;
120     private static boolean compat = false;
121     static {
122         try {
123             GetPropertyAction act = new GetPropertyAction("jmx.serial.form");
124             String form = AccessController.doPrivileged(act);
125             compat = (form != null && form.equals("1.0"));
126         } catch (Exception e) {
127             // OK : Too bad, no compat with 1.0
128         }
129         if (compat) {
130             serialPersistentFields = oldSerialPersistentFields;
131             serialVersionUID = oldSerialVersionUID;
132         } else {
133             serialPersistentFields = newSerialPersistentFields;
134             serialVersionUID = newSerialVersionUID;
135         }
136     }
137     //
138     // END Serialization compatibility stuff
139 
140     //
141     // Notification types
142     //
143 
144     /**
145      * Type for the creation of an internal relation.
146      */
147     public static final String RELATION_BASIC_CREATION = "jmx.relation.creation.basic";
148     /**
149      * Type for the relation MBean added into the Relation Service.
150      */
151     public static final String RELATION_MBEAN_CREATION = "jmx.relation.creation.mbean";
152     /**
153      * Type for an update of an internal relation.
154      */
155     public static final String RELATION_BASIC_UPDATE = "jmx.relation.update.basic";
156     /**
157      * Type for the update of a relation MBean.
158      */
159     public static final String RELATION_MBEAN_UPDATE = "jmx.relation.update.mbean";
160     /**
161      * Type for the removal from the Relation Service of an internal relation.
162      */
163     public static final String RELATION_BASIC_REMOVAL = "jmx.relation.removal.basic";
164     /**
165      * Type for the removal from the Relation Service of a relation MBean.
166      */
167     public static final String RELATION_MBEAN_REMOVAL = "jmx.relation.removal.mbean";
168 
169     //
170     // Private members
171     //
172 
173     /**
174      * @serial Relation identifier of created/removed/updated relation
175      */
176     private String relationId = null;
177 
178     /**
179      * @serial Relation type name of created/removed/updated relation
180      */
181     private String relationTypeName = null;
182 
183     /**
184      * @serial {@link ObjectName} of the relation MBean of created/removed/updated relation
185      *         (only if the relation is represented by an MBean)
186      */
187     private ObjectName relationObjName = null;
188 
189     /**
190      * @serial List of {@link ObjectName}s of referenced MBeans to be unregistered due to
191      *         relation removal
192      */
193     private List<ObjectName> unregisterMBeanList = null;
194 
195     /**
196      * @serial Name of updated role (only for role update)
197      */
198     private String roleName = null;
199 
200     /**
201      * @serial Old role value ({@link ArrayList} of {@link ObjectName}s) (only for role update)
202      */
203     private List<ObjectName> oldRoleValue = null;
204 
205     /**
206      * @serial New role value ({@link ArrayList} of {@link ObjectName}s) (only for role update)
207      */
208     private List<ObjectName> newRoleValue = null;
209 
210     //
211     // Constructors
212     //
213 
214     /**
215      * Creates a notification for either a relation creation (RelationSupport
216      * object created internally in the Relation Service, or an MBean added as a
217      * relation) or for a relation removal from the Relation Service.
218      *
219      * @param notifType  type of the notification; either:
220      * <P>- RELATION_BASIC_CREATION
221      * <P>- RELATION_MBEAN_CREATION
222      * <P>- RELATION_BASIC_REMOVAL
223      * <P>- RELATION_MBEAN_REMOVAL
224      * @param sourceObj  source object, sending the notification.  This is either
225      * an ObjectName or a RelationService object.  In the latter case it must be
226      * the MBean emitting the notification; the MBean Server will rewrite the
227      * source to be the ObjectName under which that MBean is registered.
228      * @param sequence  sequence number to identify the notification
229      * @param timeStamp  time stamp
230      * @param message  human-readable message describing the notification
231      * @param id  relation id identifying the relation in the Relation
232      * Service
233      * @param typeName  name of the relation type
234      * @param objectName  ObjectName of the relation object if it is an MBean
235      * (null for relations internally handled by the Relation Service)
236      * @param unregMBeanList  list of ObjectNames of referenced MBeans
237      * expected to be unregistered due to relation removal (only for removal,
238      * due to CIM qualifiers, can be null)
239      *
240      * @exception IllegalArgumentException  if:
241      * <P>- no value for the notification type
242      * <P>- the notification type is not RELATION_BASIC_CREATION,
243      * RELATION_MBEAN_CREATION, RELATION_BASIC_REMOVAL or
244      * RELATION_MBEAN_REMOVAL
245      * <P>- no source object
246      * <P>- the source object is not a Relation Service
247      * <P>- no relation id
248      * <P>- no relation type name
249      */
RelationNotification(String notifType, Object sourceObj, long sequence, long timeStamp, String message, String id, String typeName, ObjectName objectName, List<ObjectName> unregMBeanList)250     public RelationNotification(String notifType,
251                                 Object sourceObj,
252                                 long sequence,
253                                 long timeStamp,
254                                 String message,
255                                 String id,
256                                 String typeName,
257                                 ObjectName objectName,
258                                 List<ObjectName> unregMBeanList)
259         throws IllegalArgumentException {
260 
261         super(notifType, sourceObj, sequence, timeStamp, message);
262 
263         if (!isValidBasicStrict(notifType,sourceObj,id,typeName) || !isValidCreate(notifType)) {
264             throw new IllegalArgumentException("Invalid parameter.");
265         }
266 
267         relationId = id;
268         relationTypeName = typeName;
269         relationObjName = safeGetObjectName(objectName);
270         unregisterMBeanList = safeGetObjectNameList(unregMBeanList);
271     }
272 
273     /**
274      * Creates a notification for a role update in a relation.
275      *
276      * @param notifType  type of the notification; either:
277      * <P>- RELATION_BASIC_UPDATE
278      * <P>- RELATION_MBEAN_UPDATE
279      * @param sourceObj  source object, sending the notification. This is either
280      * an ObjectName or a RelationService object.  In the latter case it must be
281      * the MBean emitting the notification; the MBean Server will rewrite the
282      * source to be the ObjectName under which that MBean is registered.
283      * @param sequence  sequence number to identify the notification
284      * @param timeStamp  time stamp
285      * @param message  human-readable message describing the notification
286      * @param id  relation id identifying the relation in the Relation
287      * Service
288      * @param typeName  name of the relation type
289      * @param objectName  ObjectName of the relation object if it is an MBean
290      * (null for relations internally handled by the Relation Service)
291      * @param name  name of the updated role
292      * @param newValue  new role value (List of ObjectName objects)
293      * @param oldValue  old role value (List of ObjectName objects)
294      *
295      * @exception IllegalArgumentException  if null parameter
296      */
RelationNotification(String notifType, Object sourceObj, long sequence, long timeStamp, String message, String id, String typeName, ObjectName objectName, String name, List<ObjectName> newValue, List<ObjectName> oldValue )297     public RelationNotification(String notifType,
298                                 Object sourceObj,
299                                 long sequence,
300                                 long timeStamp,
301                                 String message,
302                                 String id,
303                                 String typeName,
304                                 ObjectName objectName,
305                                 String name,
306                                 List<ObjectName> newValue,
307                                 List<ObjectName> oldValue
308                                 )
309             throws IllegalArgumentException {
310 
311         super(notifType, sourceObj, sequence, timeStamp, message);
312 
313         if (!isValidBasicStrict(notifType,sourceObj,id,typeName) || !isValidUpdate(notifType,name,newValue,oldValue)) {
314             throw new IllegalArgumentException("Invalid parameter.");
315         }
316 
317         relationId = id;
318         relationTypeName = typeName;
319         relationObjName = safeGetObjectName(objectName);
320 
321         roleName = name;
322         oldRoleValue = safeGetObjectNameList(oldValue);
323         newRoleValue = safeGetObjectNameList(newValue);
324     }
325 
326     //
327     // Accessors
328     //
329 
330     /**
331      * Returns the relation identifier of created/removed/updated relation.
332      *
333      * @return the relation id.
334      */
getRelationId()335     public String getRelationId() {
336         return relationId;
337     }
338 
339     /**
340      * Returns the relation type name of created/removed/updated relation.
341      *
342      * @return the relation type name.
343      */
getRelationTypeName()344     public String getRelationTypeName() {
345         return relationTypeName;
346     }
347 
348     /**
349      * Returns the ObjectName of the
350      * created/removed/updated relation.
351      *
352      * @return the ObjectName if the relation is an MBean, otherwise null.
353      */
getObjectName()354     public ObjectName getObjectName() {
355         return relationObjName;
356     }
357 
358     /**
359      * Returns the list of ObjectNames of MBeans expected to be unregistered
360      * due to a relation removal (only for relation removal).
361      *
362      * @return a {@link List} of {@link ObjectName}.
363      */
getMBeansToUnregister()364     public List<ObjectName> getMBeansToUnregister() {
365         List<ObjectName> result;
366         if (unregisterMBeanList != null) {
367             result = new ArrayList<ObjectName>(unregisterMBeanList);
368         } else {
369             result = Collections.emptyList();
370         }
371         return result;
372     }
373 
374     /**
375      * Returns name of updated role of updated relation (only for role update).
376      *
377      * @return the name of the updated role.
378      */
getRoleName()379     public String getRoleName() {
380         String result = null;
381         if (roleName != null) {
382             result = roleName;
383         }
384         return result;
385     }
386 
387     /**
388      * Returns old value of updated role (only for role update).
389      *
390      * @return the old value of the updated role.
391      */
getOldRoleValue()392     public List<ObjectName> getOldRoleValue() {
393         List<ObjectName> result;
394         if (oldRoleValue != null) {
395             result = new ArrayList<ObjectName>(oldRoleValue);
396         } else {
397             result = Collections.emptyList();
398         }
399         return result;
400     }
401 
402     /**
403      * Returns new value of updated role (only for role update).
404      *
405      * @return the new value of the updated role.
406      */
getNewRoleValue()407     public List<ObjectName> getNewRoleValue() {
408         List<ObjectName> result;
409         if (newRoleValue != null) {
410             result = new ArrayList<ObjectName>(newRoleValue);
411         } else {
412             result = Collections.emptyList();
413         }
414         return result;
415     }
416 
417     //
418     // Misc
419     //
420 
421     // Initializes members
422     //
423     // -param notifKind  1 for creation/removal, 2 for update
424     // -param notifType  type of the notification; either:
425     //  - RELATION_BASIC_UPDATE
426     //  - RELATION_MBEAN_UPDATE
427     //  for an update, or:
428     //  - RELATION_BASIC_CREATION
429     //  - RELATION_MBEAN_CREATION
430     //  - RELATION_BASIC_REMOVAL
431     //  - RELATION_MBEAN_REMOVAL
432     //  for a creation or removal
433     // -param sourceObj  source object, sending the notification. Will always
434     //  be a RelationService object.
435     // -param sequence  sequence number to identify the notification
436     // -param timeStamp  time stamp
437     // -param message  human-readable message describing the notification
438     // -param id  relation id identifying the relation in the Relation
439     //  Service
440     // -param typeName  name of the relation type
441     // -param objectName  ObjectName of the relation object if it is an MBean
442     //  (null for relations internally handled by the Relation Service)
443     // -param unregMBeanList  list of ObjectNames of MBeans expected to be
444     //  removed due to relation removal
445     // -param name  name of the updated role
446     // -param newValue  new value (List of ObjectName objects)
447     // -param oldValue  old value (List of ObjectName objects)
448     //
449     // -exception IllegalArgumentException  if:
450     //  - no value for the notification type
451     //  - incorrect notification type
452     //  - no source object
453     //  - the source object is not a Relation Service
454     //  - no relation id
455     //  - no relation type name
456     //  - no role name (for role update)
457     //  - no role old value (for role update)
458     //  - no role new value (for role update)
459 
460     // Despite the fact, that validation in constructor of RelationNotification prohibit
461     // creation of the class instance with null sourceObj its possible to set it to null later
462     // by public setSource() method.
463     // So we should relax validation rules to preserve serialization behavior compatibility.
464 
isValidBasicStrict(String notifType, Object sourceObj, String id, String typeName)465     private boolean isValidBasicStrict(String notifType, Object sourceObj, String id, String typeName){
466         if (sourceObj == null) {
467             return false;
468         }
469         return isValidBasic(notifType,sourceObj,id,typeName);
470     }
471 
isValidBasic(String notifType, Object sourceObj, String id, String typeName)472     private boolean isValidBasic(String notifType, Object sourceObj, String id, String typeName){
473         if (notifType == null || id == null || typeName == null) {
474             return false;
475         }
476 
477         if (sourceObj != null && (
478             !(sourceObj instanceof RelationService) &&
479             !(sourceObj instanceof ObjectName))) {
480             return false;
481         }
482 
483         return true;
484     }
485 
isValidCreate(String notifType)486     private boolean isValidCreate(String notifType) {
487         String[] validTypes= {RelationNotification.RELATION_BASIC_CREATION,
488                               RelationNotification.RELATION_MBEAN_CREATION,
489                               RelationNotification.RELATION_BASIC_REMOVAL,
490                               RelationNotification.RELATION_MBEAN_REMOVAL};
491 
492         Set<String> ctSet = new HashSet<String>(Arrays.asList(validTypes));
493         return ctSet.contains(notifType);
494     }
495 
isValidUpdate(String notifType, String name, List<ObjectName> newValue, List<ObjectName> oldValue)496     private boolean isValidUpdate(String notifType, String name,
497                                   List<ObjectName> newValue, List<ObjectName> oldValue) {
498 
499         if (!(notifType.equals(RelationNotification.RELATION_BASIC_UPDATE)) &&
500             !(notifType.equals(RelationNotification.RELATION_MBEAN_UPDATE))) {
501             return false;
502         }
503 
504         if (name == null || oldValue == null || newValue == null) {
505             return false;
506         }
507 
508         return true;
509     }
510 
safeGetObjectNameList(List<ObjectName> src)511     private ArrayList<ObjectName> safeGetObjectNameList(List<ObjectName> src){
512         ArrayList<ObjectName> dest = null;
513         if (src != null) {
514             dest = new ArrayList<ObjectName>();
515             for (ObjectName item : src) {
516                 // NPE thrown if we attempt to add null object
517                 dest.add(ObjectName.getInstance(item));
518             }
519         }
520         return dest;
521     }
522 
safeGetObjectName(ObjectName src)523     private ObjectName safeGetObjectName(ObjectName src){
524         ObjectName dest = null;
525         if (src != null) {
526             dest = ObjectName.getInstance(src);
527         }
528         return dest;
529     }
530 
531     /**
532      * Deserializes a {@link RelationNotification} from an {@link ObjectInputStream}.
533      */
readObject(ObjectInputStream in)534     private void readObject(ObjectInputStream in)
535             throws IOException, ClassNotFoundException {
536 
537         String tmpRelationId, tmpRelationTypeName, tmpRoleName;
538 
539         ObjectName tmpRelationObjName;
540         List<ObjectName> tmpNewRoleValue, tmpOldRoleValue, tmpUnregMBeanList;
541 
542         ObjectInputStream.GetField fields = in.readFields();
543 
544         if (compat) {
545             tmpRelationId = (String)fields.get("myRelId", null);
546             tmpRelationTypeName = (String)fields.get("myRelTypeName", null);
547             tmpRoleName = (String)fields.get("myRoleName", null);
548 
549             tmpRelationObjName = (ObjectName)fields.get("myRelObjName", null);
550             tmpNewRoleValue = cast(fields.get("myNewRoleValue", null));
551             tmpOldRoleValue = cast(fields.get("myOldRoleValue", null));
552             tmpUnregMBeanList = cast(fields.get("myUnregMBeanList", null));
553         }
554         else {
555             tmpRelationId = (String)fields.get("relationId", null);
556             tmpRelationTypeName = (String)fields.get("relationTypeName", null);
557             tmpRoleName = (String)fields.get("roleName", null);
558 
559             tmpRelationObjName = (ObjectName)fields.get("relationObjName", null);
560             tmpNewRoleValue = cast(fields.get("newRoleValue", null));
561             tmpOldRoleValue = cast(fields.get("oldRoleValue", null));
562             tmpUnregMBeanList = cast(fields.get("unregisterMBeanList", null));
563         }
564 
565         // Validate fields we just read, throw InvalidObjectException
566         // if something goes wrong
567 
568         String notifType = super.getType();
569         if (!isValidBasic(notifType,super.getSource(),tmpRelationId,tmpRelationTypeName)  ||
570             (!isValidCreate(notifType) &&
571              !isValidUpdate(notifType,tmpRoleName,tmpNewRoleValue,tmpOldRoleValue))) {
572 
573             super.setSource(null);
574             throw new InvalidObjectException("Invalid object read");
575         }
576 
577         // assign deserialized vaules to object fields
578         relationObjName = safeGetObjectName(tmpRelationObjName);
579         newRoleValue = safeGetObjectNameList(tmpNewRoleValue);
580         oldRoleValue = safeGetObjectNameList(tmpOldRoleValue);
581         unregisterMBeanList = safeGetObjectNameList(tmpUnregMBeanList);
582 
583         relationId = tmpRelationId;
584         relationTypeName = tmpRelationTypeName;
585         roleName = tmpRoleName;
586     }
587 
588 
589     /**
590      * Serializes a {@link RelationNotification} to an {@link ObjectOutputStream}.
591      */
writeObject(ObjectOutputStream out)592     private void writeObject(ObjectOutputStream out)
593             throws IOException {
594       if (compat)
595       {
596         // Serializes this instance in the old serial form
597         //
598         ObjectOutputStream.PutField fields = out.putFields();
599         fields.put("myNewRoleValue", newRoleValue);
600         fields.put("myOldRoleValue", oldRoleValue);
601         fields.put("myRelId", relationId);
602         fields.put("myRelObjName", relationObjName);
603         fields.put("myRelTypeName", relationTypeName);
604         fields.put("myRoleName",roleName);
605         fields.put("myUnregMBeanList", unregisterMBeanList);
606         out.writeFields();
607       }
608       else
609       {
610         // Serializes this instance in the new serial form
611         //
612         out.defaultWriteObject();
613       }
614     }
615 }
616