001 /*
002 * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.management.relation;
027
028 import static com.sun.jmx.defaults.JmxProperties.RELATION_LOGGER;
029 import static com.sun.jmx.mbeanserver.Util.cast;
030 import com.sun.jmx.mbeanserver.GetPropertyAction;
031
032 import java.io.IOException;
033 import java.io.ObjectInputStream;
034 import java.io.ObjectOutputStream;
035 import java.io.ObjectStreamField;
036
037 import java.security.AccessController;
038
039 import java.util.ArrayList;
040 import java.util.HashMap;
041 import java.util.HashSet;
042 import java.util.List;
043 import java.util.Map;
044 import java.util.Set;
045 import java.util.logging.Level;
046
047 /**
048 * A RelationTypeSupport object implements the RelationType interface.
049 * <P>It represents a relation type, providing role information for each role
050 * expected to be supported in every relation of that type.
051 *
052 * <P>A relation type includes a relation type name and a list of
053 * role infos (represented by RoleInfo objects).
054 *
055 * <P>A relation type has to be declared in the Relation Service:
056 * <P>- either using the createRelationType() method, where a RelationTypeSupport
057 * object will be created and kept in the Relation Service
058 * <P>- either using the addRelationType() method where the user has to create
059 * an object implementing the RelationType interface, and this object will be
060 * used as representing a relation type in the Relation Service.
061 *
062 * <p>The <b>serialVersionUID</b> of this class is <code>4611072955724144607L</code>.
063 *
064 * @since 1.5
065 */
066 @SuppressWarnings("serial")
067 // serialVersionUID not constant
068 public class RelationTypeSupport implements RelationType {
069
070 // Serialization compatibility stuff:
071 // Two serial forms are supported in this class. The selected form depends
072 // on system property "jmx.serial.form":
073 // - "1.0" for JMX 1.0
074 // - any other value for JMX 1.1 and higher
075 //
076 // Serial version for old serial form
077 private static final long oldSerialVersionUID = -8179019472410837190L;
078 //
079 // Serial version for new serial form
080 private static final long newSerialVersionUID = 4611072955724144607L;
081 //
082 // Serializable fields in old serial form
083 private static final ObjectStreamField[] oldSerialPersistentFields = {
084 new ObjectStreamField("myTypeName", String.class),
085 new ObjectStreamField("myRoleName2InfoMap", HashMap.class),
086 new ObjectStreamField("myIsInRelServFlg", boolean.class) };
087 //
088 // Serializable fields in new serial form
089 private static final ObjectStreamField[] newSerialPersistentFields = {
090 new ObjectStreamField("typeName", String.class),
091 new ObjectStreamField("roleName2InfoMap", Map.class),
092 new ObjectStreamField("isInRelationService", boolean.class) };
093 //
094 // Actual serial version and serial form
095 private static final long serialVersionUID;
096 /**
097 * @serialField typeName String Relation type name
098 * @serialField roleName2InfoMap Map {@link Map} holding the mapping:
099 * <role name ({@link String})> -> <role info ({@link RoleInfo} object)>
100 * @serialField isInRelationService boolean Flag specifying whether the relation type has been declared in the
101 * Relation Service (so can no longer be updated)
102 */
103 private static final ObjectStreamField[] serialPersistentFields;
104 private static boolean compat = false;
105 static {
106 try {
107 GetPropertyAction act = new GetPropertyAction(
108 "jmx.serial.form");
109 String form = AccessController.doPrivileged(act);
110 compat = (form != null && form.equals("1.0"));
111 } catch (Exception e) {
112 // OK : Too bad, no compat with 1.0
113 }
114 if (compat) {
115 serialPersistentFields = oldSerialPersistentFields;
116 serialVersionUID = oldSerialVersionUID;
117 } else {
118 serialPersistentFields = newSerialPersistentFields;
119 serialVersionUID = newSerialVersionUID;
120 }
121 }
122 //
123 // END Serialization compatibility stuff
124
125 //
126 // Private members
127 //
128
129 /**
130 * @serial Relation type name
131 */
132 private String typeName = null;
133
134 /**
135 * @serial {@link Map} holding the mapping:
136 * <role name ({@link String})> -> <role info ({@link RoleInfo} object)>
137 */
138 private Map<String, RoleInfo> roleName2InfoMap = new HashMap<String, RoleInfo>();
139
140 /**
141 * @serial Flag specifying whether the relation type has been declared in the
142 * Relation Service (so can no longer be updated)
143 */
144 private boolean isInRelationService = false;
145
146 //
147 // Constructors
148 //
149
150 /**
151 * Constructor where all role definitions are dynamically created and
152 * passed as parameter.
153 *
154 * @param relationTypeName Name of relation type
155 * @param roleInfoArray List of role definitions (RoleInfo objects)
156 *
157 * @exception IllegalArgumentException if null parameter
158 * @exception InvalidRelationTypeException if:
159 * <P>- the same name has been used for two different roles
160 * <P>- no role info provided
161 * <P>- one null role info provided
162 */
163 public RelationTypeSupport(String relationTypeName,
164 RoleInfo[] roleInfoArray) throws IllegalArgumentException,
165 InvalidRelationTypeException {
166
167 if (relationTypeName == null || roleInfoArray == null) {
168 String excMsg = "Invalid parameter.";
169 throw new IllegalArgumentException(excMsg);
170 }
171
172 RELATION_LOGGER.entering(RelationTypeSupport.class.getName(),
173 "RelationTypeSupport", relationTypeName);
174
175 // Can throw InvalidRelationTypeException, ClassNotFoundException
176 // and NotCompliantMBeanException
177 initMembers(relationTypeName, roleInfoArray);
178
179 RELATION_LOGGER.exiting(RelationTypeSupport.class.getName(),
180 "RelationTypeSupport");
181 return;
182 }
183
184 /**
185 * Constructor to be used for subclasses.
186 *
187 * @param relationTypeName Name of relation type.
188 *
189 * @exception IllegalArgumentException if null parameter.
190 */
191 protected RelationTypeSupport(String relationTypeName) {
192 if (relationTypeName == null) {
193 String excMsg = "Invalid parameter.";
194 throw new IllegalArgumentException(excMsg);
195 }
196
197 RELATION_LOGGER.entering(RelationTypeSupport.class.getName(),
198 "RelationTypeSupport", relationTypeName);
199
200 typeName = relationTypeName;
201
202 RELATION_LOGGER.exiting(RelationTypeSupport.class.getName(),
203 "RelationTypeSupport");
204 return;
205 }
206
207 //
208 // Accessors
209 //
210
211 /**
212 * Returns the relation type name.
213 *
214 * @return the relation type name.
215 */
216 public String getRelationTypeName() {
217 return typeName;
218 }
219
220 /**
221 * Returns the list of role definitions (ArrayList of RoleInfo objects).
222 */
223 public List<RoleInfo> getRoleInfos() {
224 return new ArrayList<RoleInfo>(roleName2InfoMap.values());
225 }
226
227 /**
228 * Returns the role info (RoleInfo object) for the given role info name
229 * (null if not found).
230 *
231 * @param roleInfoName role info name
232 *
233 * @return RoleInfo object providing role definition
234 * does not exist
235 *
236 * @exception IllegalArgumentException if null parameter
237 * @exception RoleInfoNotFoundException if no role info with that name in
238 * relation type.
239 */
240 public RoleInfo getRoleInfo(String roleInfoName)
241 throws IllegalArgumentException, RoleInfoNotFoundException {
242
243 if (roleInfoName == null) {
244 String excMsg = "Invalid parameter.";
245 throw new IllegalArgumentException(excMsg);
246 }
247
248 RELATION_LOGGER.entering(RelationTypeSupport.class.getName(),
249 "getRoleInfo", roleInfoName);
250
251 // No null RoleInfo allowed, so use get()
252 RoleInfo result = roleName2InfoMap.get(roleInfoName);
253
254 if (result == null) {
255 StringBuilder excMsgStrB = new StringBuilder();
256 String excMsg = "No role info for role ";
257 excMsgStrB.append(excMsg);
258 excMsgStrB.append(roleInfoName);
259 throw new RoleInfoNotFoundException(excMsgStrB.toString());
260 }
261
262 RELATION_LOGGER.exiting(RelationTypeSupport.class.getName(),
263 "getRoleInfo");
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.entering(RelationTypeSupport.class.getName(),
295 "addRoleInfo", 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
312 .toString());
313 }
314
315 roleName2InfoMap.put(roleName, new RoleInfo(roleInfo));
316
317 RELATION_LOGGER.exiting(RelationTypeSupport.class.getName(),
318 "addRoleInfo");
319 return;
320 }
321
322 // Sets the internal flag to specify that the relation type has been
323 // declared in the Relation Service
324 void setRelationServiceFlag(boolean flag) {
325 isInRelationService = flag;
326 return;
327 }
328
329 // Initializes the members, i.e. type name and role info list.
330 //
331 // -param relationTypeName Name of relation type
332 // -param roleInfoArray List of role definitions (RoleInfo objects)
333 //
334 // -exception IllegalArgumentException if null parameter
335 // -exception InvalidRelationTypeException If:
336 // - the same name has been used for two different roles
337 // - no role info provided
338 // - one null role info provided
339 private void initMembers(String relationTypeName,
340 RoleInfo[] roleInfoArray) throws IllegalArgumentException,
341 InvalidRelationTypeException {
342
343 if (relationTypeName == null || roleInfoArray == null) {
344 String excMsg = "Invalid parameter.";
345 throw new IllegalArgumentException(excMsg);
346 }
347
348 RELATION_LOGGER.entering(RelationTypeSupport.class.getName(),
349 "initMembers", relationTypeName);
350
351 typeName = relationTypeName;
352
353 // Verifies role infos before setting them
354 // Can throw InvalidRelationTypeException
355 checkRoleInfos(roleInfoArray);
356
357 for (int i = 0; i < roleInfoArray.length; i++) {
358 RoleInfo currRoleInfo = roleInfoArray[i];
359 roleName2InfoMap.put(currRoleInfo.getName(), new RoleInfo(
360 currRoleInfo));
361 }
362
363 RELATION_LOGGER.exiting(RelationTypeSupport.class.getName(),
364 "initMembers");
365 return;
366 }
367
368 // Checks the given RoleInfo array to verify that:
369 // - the array is not empty
370 // - it does not contain a null element
371 // - a given role name is used only for one RoleInfo
372 //
373 // -param roleInfoArray array to be checked
374 //
375 // -exception IllegalArgumentException
376 // -exception InvalidRelationTypeException If:
377 // - the same name has been used for two different roles
378 // - no role info provided
379 // - one null role info provided
380 static void checkRoleInfos(RoleInfo[] roleInfoArray)
381 throws IllegalArgumentException,
382 InvalidRelationTypeException {
383
384 if (roleInfoArray == null) {
385 String excMsg = "Invalid parameter.";
386 throw new IllegalArgumentException(excMsg);
387 }
388
389 if (roleInfoArray.length == 0) {
390 // No role info provided
391 String excMsg = "No role info provided.";
392 throw new InvalidRelationTypeException(excMsg);
393 }
394
395 Set<String> roleNames = new HashSet<String>();
396
397 for (int i = 0; i < roleInfoArray.length; i++) {
398 RoleInfo currRoleInfo = roleInfoArray[i];
399
400 if (currRoleInfo == null) {
401 String excMsg = "Null role info provided.";
402 throw new InvalidRelationTypeException(excMsg);
403 }
404
405 String roleName = currRoleInfo.getName();
406
407 // Checks if the role info has already been described
408 if (roleNames.contains(roleName)) {
409 StringBuilder excMsgStrB = new StringBuilder();
410 String excMsg = "Two role infos provided for role ";
411 excMsgStrB.append(excMsg);
412 excMsgStrB.append(roleName);
413 throw new InvalidRelationTypeException(excMsgStrB
414 .toString());
415 }
416 roleNames.add(roleName);
417 }
418
419 return;
420 }
421
422 /**
423 * Deserializes a {@link RelationTypeSupport} from an {@link ObjectInputStream}.
424 */
425 private void readObject(ObjectInputStream in) throws IOException,
426 ClassNotFoundException {
427 if (compat) {
428 // Read an object serialized in the old serial form
429 //
430 ObjectInputStream.GetField fields = in.readFields();
431 typeName = (String) fields.get("myTypeName", null);
432 if (fields.defaulted("myTypeName")) {
433 throw new NullPointerException("myTypeName");
434 }
435 roleName2InfoMap = cast(fields.get("myRoleName2InfoMap",
436 null));
437 if (fields.defaulted("myRoleName2InfoMap")) {
438 throw new NullPointerException("myRoleName2InfoMap");
439 }
440 isInRelationService = fields.get("myIsInRelServFlg", false);
441 if (fields.defaulted("myIsInRelServFlg")) {
442 throw new NullPointerException("myIsInRelServFlg");
443 }
444 } else {
445 // Read an object serialized in the new serial form
446 //
447 in.defaultReadObject();
448 }
449 }
450
451 /**
452 * Serializes a {@link RelationTypeSupport} to an {@link ObjectOutputStream}.
453 */
454 private void writeObject(ObjectOutputStream out) throws IOException {
455 if (compat) {
456 // Serializes this instance in the old serial form
457 //
458 ObjectOutputStream.PutField fields = out.putFields();
459 fields.put("myTypeName", typeName);
460 fields.put("myRoleName2InfoMap", roleName2InfoMap);
461 fields.put("myIsInRelServFlg", isInRelationService);
462 out.writeFields();
463 } else {
464 // Serializes this instance in the new serial form
465 //
466 out.defaultWriteObject();
467 }
468 }
469 }
|