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