1 /* 2 * Copyright (c) 2000, 2021, 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 static com.sun.jmx.defaults.JmxProperties.RELATION_LOGGER; 29 import static com.sun.jmx.mbeanserver.Util.cast; 30 import com.sun.jmx.mbeanserver.GetPropertyAction; 31 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 39 import java.util.ArrayList; 40 import java.util.HashMap; 41 import java.util.HashSet; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Set; 45 import java.lang.System.Logger.Level; 46 47 /** 48 * A RelationTypeSupport object implements the RelationType interface. 49 * <P>It represents a relation type, providing role information for each role 50 * expected to be supported in every relation of that type. 51 * 52 * <P>A relation type includes a relation type name and a list of 53 * role infos (represented by RoleInfo objects). 54 * 55 * <P>A relation type has to be declared in the Relation Service: 56 * <P>- either using the createRelationType() method, where a RelationTypeSupport 57 * object will be created and kept in the Relation Service 58 * <P>- either using the addRelationType() method where the user has to create 59 * an object implementing the RelationType interface, and this object will be 60 * used as representing a relation type in the Relation Service. 61 * 62 * <p>The <b>serialVersionUID</b> of this class is <code>4611072955724144607L</code>. 63 * 64 * @since 1.5 65 */ 66 @SuppressWarnings("serial") // serialVersionUID not constant 67 public class RelationTypeSupport implements RelationType { 68 69 // Serialization compatibility stuff: 70 // Two serial forms are supported in this class. The selected form depends 71 // on system property "jmx.serial.form": 72 // - "1.0" for JMX 1.0 73 // - any other value for JMX 1.1 and higher 74 // 75 // Serial version for old serial form 76 private static final long oldSerialVersionUID = -8179019472410837190L; 77 // 78 // Serial version for new serial form 79 private static final long newSerialVersionUID = 4611072955724144607L; 80 // 81 // Serializable fields in old serial form 82 private static final ObjectStreamField[] oldSerialPersistentFields = 83 { 84 new ObjectStreamField("myTypeName", String.class), 85 new ObjectStreamField("myRoleName2InfoMap", HashMap.class), 86 new ObjectStreamField("myIsInRelServFlg", boolean.class) 87 }; 88 // 89 // Serializable fields in new serial form 90 private static final ObjectStreamField[] newSerialPersistentFields = 91 { 92 new ObjectStreamField("typeName", String.class), 93 new ObjectStreamField("roleName2InfoMap", Map.class), 94 new ObjectStreamField("isInRelationService", boolean.class) 95 }; 96 // 97 // Actual serial version and serial form 98 private static final long serialVersionUID; 99 /** 100 * @serialField typeName String Relation type name 101 * @serialField roleName2InfoMap Map {@link Map} holding the mapping: 102 * <role name ({@link String})> -> <role info ({@link RoleInfo} object)> 103 * @serialField isInRelationService boolean Flag specifying whether the relation type has been declared in the 104 * Relation Service (so can no longer be updated) 105 */ 106 private static final ObjectStreamField[] serialPersistentFields; 107 private static boolean compat = false; 108 static { 109 try { 110 GetPropertyAction act = new GetPropertyAction("jmx.serial.form"); 111 @SuppressWarnings("removal") 112 String form = AccessController.doPrivileged(act); 113 compat = (form != null && form.equals("1.0")); 114 } catch (Exception e) { 115 // OK : Too bad, no compat with 1.0 116 } 117 if (compat) { 118 serialPersistentFields = oldSerialPersistentFields; 119 serialVersionUID = oldSerialVersionUID; 120 } else { 121 serialPersistentFields = newSerialPersistentFields; 122 serialVersionUID = newSerialVersionUID; 123 } 124 } 125 // 126 // END Serialization compatibility stuff 127 128 // 129 // Private members 130 // 131 132 /** 133 * @serial Relation type name 134 */ 135 private String typeName = null; 136 137 /** 138 * @serial {@link Map} holding the mapping: 139 * <role name ({@link String})> -> <role info ({@link RoleInfo} object)> 140 */ 141 private Map<String,RoleInfo> roleName2InfoMap = 142 new HashMap<String,RoleInfo>(); 143 144 /** 145 * @serial Flag specifying whether the relation type has been declared in the 146 * Relation Service (so can no longer be updated) 147 */ 148 private boolean isInRelationService = false; 149 150 // 151 // Constructors 152 // 153 154 /** 155 * Constructor where all role definitions are dynamically created and 156 * passed as parameter. 157 * 158 * @param relationTypeName Name of relation type 159 * @param roleInfoArray List of role definitions (RoleInfo objects) 160 * 161 * @exception IllegalArgumentException if null parameter 162 * @exception InvalidRelationTypeException if: 163 * <P>- the same name has been used for two different roles 164 * <P>- no role info provided 165 * <P>- one null role info provided 166 */ RelationTypeSupport(String relationTypeName, RoleInfo[] roleInfoArray)167 public RelationTypeSupport(String relationTypeName, 168 RoleInfo[] roleInfoArray) 169 throws IllegalArgumentException, 170 InvalidRelationTypeException { 171 172 if (relationTypeName == null || roleInfoArray == null) { 173 String excMsg = "Invalid parameter."; 174 throw new IllegalArgumentException(excMsg); 175 } 176 177 RELATION_LOGGER.log(Level.TRACE, "ENTRY {0}", relationTypeName); 178 179 // Can throw InvalidRelationTypeException, ClassNotFoundException 180 // and NotCompliantMBeanException 181 initMembers(relationTypeName, roleInfoArray); 182 183 RELATION_LOGGER.log(Level.TRACE, "RETURN"); 184 return; 185 } 186 187 /** 188 * Constructor to be used for subclasses. 189 * 190 * @param relationTypeName Name of relation type. 191 * 192 * @exception IllegalArgumentException if null parameter. 193 */ RelationTypeSupport(String relationTypeName)194 protected RelationTypeSupport(String relationTypeName) 195 { 196 if (relationTypeName == null) { 197 String excMsg = "Invalid parameter."; 198 throw new IllegalArgumentException(excMsg); 199 } 200 201 RELATION_LOGGER.log(Level.TRACE, "ENTRY {0}", relationTypeName); 202 203 typeName = relationTypeName; 204 205 RELATION_LOGGER.log(Level.TRACE, "RETURN"); 206 return; 207 } 208 209 // 210 // Accessors 211 // 212 213 /** 214 * Returns the relation type name. 215 * 216 * @return the relation type name. 217 */ getRelationTypeName()218 public String getRelationTypeName() { 219 return typeName; 220 } 221 222 /** 223 * Returns the list of role definitions (ArrayList of RoleInfo objects). 224 */ getRoleInfos()225 public List<RoleInfo> getRoleInfos() { 226 return new ArrayList<RoleInfo>(roleName2InfoMap.values()); 227 } 228 229 /** 230 * Returns the role info (RoleInfo object) for the given role info name 231 * (null if not found). 232 * 233 * @param roleInfoName role info name 234 * 235 * @return RoleInfo object providing role definition 236 * does not exist 237 * 238 * @exception IllegalArgumentException if null parameter 239 * @exception RoleInfoNotFoundException if no role info with that name in 240 * relation type. 241 */ getRoleInfo(String roleInfoName)242 public RoleInfo getRoleInfo(String roleInfoName) 243 throws IllegalArgumentException, 244 RoleInfoNotFoundException { 245 246 if (roleInfoName == null) { 247 String excMsg = "Invalid parameter."; 248 throw new IllegalArgumentException(excMsg); 249 } 250 251 RELATION_LOGGER.log(Level.TRACE, "ENTRY {0}", roleInfoName); 252 253 // No null RoleInfo allowed, so use get() 254 RoleInfo result = roleName2InfoMap.get(roleInfoName); 255 256 if (result == null) { 257 StringBuilder excMsgStrB = new StringBuilder(); 258 String excMsg = "No role info for role "; 259 excMsgStrB.append(excMsg); 260 excMsgStrB.append(roleInfoName); 261 throw new RoleInfoNotFoundException(excMsgStrB.toString()); 262 } 263 264 RELATION_LOGGER.log(Level.TRACE, "RETURN"); 265 return result; 266 } 267 268 // 269 // Misc 270 // 271 272 /** 273 * Add a role info. 274 * This method of course should not be used after the creation of the 275 * relation type, because updating it would invalidate that the relations 276 * created associated to that type still conform to it. 277 * Can throw a RuntimeException if trying to update a relation type 278 * declared in the Relation Service. 279 * 280 * @param roleInfo role info to be added. 281 * 282 * @exception IllegalArgumentException if null parameter. 283 * @exception InvalidRelationTypeException if there is already a role 284 * info in current relation type with the same name. 285 */ addRoleInfo(RoleInfo roleInfo)286 protected void addRoleInfo(RoleInfo roleInfo) 287 throws IllegalArgumentException, 288 InvalidRelationTypeException { 289 290 if (roleInfo == null) { 291 String excMsg = "Invalid parameter."; 292 throw new IllegalArgumentException(excMsg); 293 } 294 295 RELATION_LOGGER.log(Level.TRACE, "ENTRY {0}", roleInfo); 296 297 if (isInRelationService) { 298 // Trying to update a declared relation type 299 String excMsg = "Relation type cannot be updated as it is declared in the Relation Service."; 300 throw new RuntimeException(excMsg); 301 } 302 303 String roleName = roleInfo.getName(); 304 305 // Checks if the role info has already been described 306 if (roleName2InfoMap.containsKey(roleName)) { 307 StringBuilder excMsgStrB = new StringBuilder(); 308 String excMsg = "Two role infos provided for role "; 309 excMsgStrB.append(excMsg); 310 excMsgStrB.append(roleName); 311 throw new InvalidRelationTypeException(excMsgStrB.toString()); 312 } 313 314 roleName2InfoMap.put(roleName, new RoleInfo(roleInfo)); 315 316 RELATION_LOGGER.log(Level.TRACE, "RETURN"); 317 return; 318 } 319 320 // Sets the internal flag to specify that the relation type has been 321 // declared in the Relation Service setRelationServiceFlag(boolean flag)322 void setRelationServiceFlag(boolean flag) { 323 isInRelationService = flag; 324 return; 325 } 326 327 // Initializes the members, i.e. type name and role info list. 328 // 329 // -param relationTypeName Name of relation type 330 // -param roleInfoArray List of role definitions (RoleInfo objects) 331 // 332 // -exception IllegalArgumentException if null parameter 333 // -exception InvalidRelationTypeException If: 334 // - the same name has been used for two different roles 335 // - no role info provided 336 // - one null role info provided initMembers(String relationTypeName, RoleInfo[] roleInfoArray)337 private void initMembers(String relationTypeName, 338 RoleInfo[] roleInfoArray) 339 throws IllegalArgumentException, 340 InvalidRelationTypeException { 341 342 if (relationTypeName == null || roleInfoArray == null) { 343 String excMsg = "Invalid parameter."; 344 throw new IllegalArgumentException(excMsg); 345 } 346 347 RELATION_LOGGER.log(Level.TRACE, "ENTRY {0}", relationTypeName); 348 349 typeName = relationTypeName; 350 351 // Verifies role infos before setting them 352 // Can throw InvalidRelationTypeException 353 checkRoleInfos(roleInfoArray); 354 355 for (int i = 0; i < roleInfoArray.length; i++) { 356 RoleInfo currRoleInfo = roleInfoArray[i]; 357 roleName2InfoMap.put(currRoleInfo.getName(), 358 new RoleInfo(currRoleInfo)); 359 } 360 361 RELATION_LOGGER.log(Level.TRACE, "RETURN"); 362 return; 363 } 364 365 // Checks the given RoleInfo array to verify that: 366 // - the array is not empty 367 // - it does not contain a null element 368 // - a given role name is used only for one RoleInfo 369 // 370 // -param roleInfoArray array to be checked 371 // 372 // -exception IllegalArgumentException 373 // -exception InvalidRelationTypeException If: 374 // - the same name has been used for two different roles 375 // - no role info provided 376 // - one null role info provided checkRoleInfos(RoleInfo[] roleInfoArray)377 static void checkRoleInfos(RoleInfo[] roleInfoArray) 378 throws IllegalArgumentException, 379 InvalidRelationTypeException { 380 381 if (roleInfoArray == null) { 382 String excMsg = "Invalid parameter."; 383 throw new IllegalArgumentException(excMsg); 384 } 385 386 if (roleInfoArray.length == 0) { 387 // No role info provided 388 String excMsg = "No role info provided."; 389 throw new InvalidRelationTypeException(excMsg); 390 } 391 392 393 Set<String> roleNames = new HashSet<String>(); 394 395 for (int i = 0; i < roleInfoArray.length; i++) { 396 RoleInfo currRoleInfo = roleInfoArray[i]; 397 398 if (currRoleInfo == null) { 399 String excMsg = "Null role info provided."; 400 throw new InvalidRelationTypeException(excMsg); 401 } 402 403 String roleName = currRoleInfo.getName(); 404 405 // Checks if the role info has already been described 406 if (roleNames.contains(roleName)) { 407 StringBuilder excMsgStrB = new StringBuilder(); 408 String excMsg = "Two role infos provided for role "; 409 excMsgStrB.append(excMsg); 410 excMsgStrB.append(roleName); 411 throw new InvalidRelationTypeException(excMsgStrB.toString()); 412 } 413 roleNames.add(roleName); 414 } 415 416 return; 417 } 418 419 420 /** 421 * Deserializes a {@link RelationTypeSupport} from an {@link ObjectInputStream}. 422 */ readObject(ObjectInputStream in)423 private void readObject(ObjectInputStream in) 424 throws IOException, ClassNotFoundException { 425 if (compat) 426 { 427 // Read an object serialized in the old serial form 428 // 429 ObjectInputStream.GetField fields = in.readFields(); 430 typeName = (String) fields.get("myTypeName", null); 431 if (fields.defaulted("myTypeName")) 432 { 433 throw new NullPointerException("myTypeName"); 434 } 435 roleName2InfoMap = cast(fields.get("myRoleName2InfoMap", null)); 436 if (fields.defaulted("myRoleName2InfoMap")) 437 { 438 throw new NullPointerException("myRoleName2InfoMap"); 439 } 440 isInRelationService = fields.get("myIsInRelServFlg", false); 441 if (fields.defaulted("myIsInRelServFlg")) 442 { 443 throw new NullPointerException("myIsInRelServFlg"); 444 } 445 } 446 else 447 { 448 // Read an object serialized in the new serial form 449 // 450 in.defaultReadObject(); 451 } 452 } 453 454 455 /** 456 * Serializes a {@link RelationTypeSupport} to an {@link ObjectOutputStream}. 457 */ writeObject(ObjectOutputStream out)458 private void writeObject(ObjectOutputStream out) 459 throws IOException { 460 if (compat) 461 { 462 // Serializes this instance in the old serial form 463 // 464 ObjectOutputStream.PutField fields = out.putFields(); 465 fields.put("myTypeName", typeName); 466 fields.put("myRoleName2InfoMap", roleName2InfoMap); 467 fields.put("myIsInRelServFlg", isInRelationService); 468 out.writeFields(); 469 } 470 else 471 { 472 // Serializes this instance in the new serial form 473 // 474 out.defaultWriteObject(); 475 } 476 } 477 } 478