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