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