/*
* @(#)RelationTypeSupport.java 1.35 05/12/01
*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package javax.management.relation;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.sun.jmx.mbeanserver.GetPropertyAction;
import com.sun.jmx.trace.Trace;
/**
* A RelationTypeSupport object implements the RelationType interface.
* <P>It represents a relation type, providing role information for each role
* expected to be supported in every relation of that type.
*
* <P>A relation type includes a relation type name and a list of
* role infos (represented by RoleInfo objects).
*
* <P>A relation type has to be declared in the Relation Service:
* <P>- either using the createRelationType() method, where a RelationTypeSupport
* object will be created and kept in the Relation Service
* <P>- either using the addRelationType() method where the user has to create
* an object implementing the RelationType interface, and this object will be
* used as representing a relation type in the Relation Service.
*
* <p>The <b>serialVersionUID</b> of this class is <code>4611072955724144607L</code>.
*
* @since 1.5
*/
public class RelationTypeSupport implements RelationType {
// Serialization compatibility stuff:
// Two serial forms are supported in this class. The selected form depends
// on system property "jmx.serial.form":
// - "1.0" for JMX 1.0
// - any other value for JMX 1.1 and higher
//
// Serial version for old serial form
private static final long oldSerialVersionUID = -8179019472410837190L;
//
// Serial version for new serial form
private static final long newSerialVersionUID = 4611072955724144607L;
//
// Serializable fields in old serial form
private static final ObjectStreamField[] oldSerialPersistentFields =
{
new ObjectStreamField("myTypeName", String.class),
new ObjectStreamField("myRoleName2InfoMap", HashMap.class),
new ObjectStreamField("myIsInRelServFlg", boolean.class)
};
//
// Serializable fields in new serial form
private static final ObjectStreamField[] newSerialPersistentFields =
{
new ObjectStreamField("typeName", String.class),
new ObjectStreamField("roleName2InfoMap", Map.class),
new ObjectStreamField("isInRelationService", boolean.class)
};
//
// Actual serial version and serial form
private static final long serialVersionUID;
/**
* @serialField typeName String Relation type name
* @serialField roleName2InfoMap Map {@link Map} holding the mapping:
* <role name ({@link String})> -> <role info ({@link RoleInfo} object)>
* @serialField isInRelationService boolean Flag specifying whether the relation type has been declared in the
* Relation Service (so can no longer be updated)
*/
private static final ObjectStreamField[] serialPersistentFields;
private static boolean compat = false;
static {
try {
GetPropertyAction act = new GetPropertyAction("jmx.serial.form");
String form = AccessController.doPrivileged(act);
compat = (form != null && form.equals("1.0"));
} catch (Exception e) {
// OK : Too bad, no compat with 1.0
}
if (compat) {
serialPersistentFields = oldSerialPersistentFields;
serialVersionUID = oldSerialVersionUID;
} else {
serialPersistentFields = newSerialPersistentFields;
serialVersionUID = newSerialVersionUID;
}
}
//
// END Serialization compatibility stuff
//
// Private members
//
/**
* @serial Relation type name
*/
private String typeName = null;
/**
* @serial {@link Map} holding the mapping:
* <role name ({@link String})> -> <role info ({@link RoleInfo} object)>
*/
private Map<String,RoleInfo> roleName2InfoMap =
new HashMap<String,RoleInfo>();
/**
* @serial Flag specifying whether the relation type has been declared in the
* Relation Service (so can no longer be updated)
*/
private boolean isInRelationService = false;
//
// Constructors
//
/**
* Constructor where all role definitions are dynamically created and
* passed as parameter.
*
* @param relationTypeName Name of relation type
* @param roleInfoArray List of role definitions (RoleInfo objects)
*
* @exception IllegalArgumentException if null parameter
* @exception InvalidRelationTypeException if:
* <P>- the same name has been used for two different roles
* <P>- no role info provided
* <P>- one null role info provided
*/
public RelationTypeSupport(String relationTypeName,
RoleInfo[] roleInfoArray)
throws IllegalArgumentException,
InvalidRelationTypeException {
if (relationTypeName == null || roleInfoArray == null) {
String excMsg = "Invalid parameter.";
throw new IllegalArgumentException(excMsg);
}
if (isTraceOn())
trace("Constructor: entering", relationTypeName);
// Can throw InvalidRelationTypeException, ClassNotFoundException
// and NotCompliantMBeanException
initMembers(relationTypeName, roleInfoArray);
if (isTraceOn())
trace("Constructor: exiting", null);
return;
}
/**
* Constructor to be used for subclasses.
*
* @param relationTypeName Name of relation type.
*
* @exception IllegalArgumentException if null parameter.
*/
protected RelationTypeSupport(String relationTypeName)
{
if (relationTypeName == null) {
String excMsg = "Invalid parameter.";
throw new IllegalArgumentException(excMsg);
}
if (isTraceOn())
trace("Protected constructor: entering", relationTypeName);
typeName = relationTypeName;
if (isTraceOn())
trace("Protected constructor: exiting", null);
return;
}
//
// Accessors
//
/**
* Returns the relation type name.
*
* @return the relation type name.
*/
public String getRelationTypeName() {
return typeName;
}
/**
* Returns the list of role definitions (ArrayList of RoleInfo objects).
*/
public List<RoleInfo> getRoleInfos() {
return new ArrayList<RoleInfo>(roleName2InfoMap.values());
}
/**
* Returns the role info (RoleInfo object) for the given role info name
* (null if not found).
*
* @param roleInfoName role info name
*
* @return RoleInfo object providing role definition
* does not exist
*
* @exception IllegalArgumentException if null parameter
* @exception RoleInfoNotFoundException if no role info with that name in
* relation type.
*/
public RoleInfo getRoleInfo(String roleInfoName)
throws IllegalArgumentException,
RoleInfoNotFoundException {
if (roleInfoName == null) {
String excMsg = "Invalid parameter.";
throw new IllegalArgumentException(excMsg);
}
if (isTraceOn())
trace("getRoleInfo: entering", roleInfoName);
// No null RoleInfo allowed, so use get()
RoleInfo result = roleName2InfoMap.get(roleInfoName);
if (result == null) {
StringBuilder excMsgStrB = new StringBuilder();
String excMsg = "No role info for role ";
excMsgStrB.append(excMsg);
excMsgStrB.append(roleInfoName);
throw new RoleInfoNotFoundException(excMsgStrB.toString());
}
if (isTraceOn())
trace("getRoleInfo: exiting", null);
return result;
}
//
// Misc
//
/**
* Add a role info.
* This method of course should not be used after the creation of the
* relation type, because updating it would invalidate that the relations
* created associated to that type still conform to it.
* Can throw a RuntimeException if trying to update a relation type
* declared in the Relation Service.
*
* @param roleInfo role info to be added.
*
* @exception IllegalArgumentException if null parameter.
* @exception InvalidRelationTypeException if there is already a role
* info in current relation type with the same name.
*/
protected void addRoleInfo(RoleInfo roleInfo)
throws IllegalArgumentException,
InvalidRelationTypeException {
if (roleInfo == null) {
String excMsg = "Invalid parameter.";
throw new IllegalArgumentException(excMsg);
}
if (isDebugOn())
debug("addRoleInfo: entering", roleInfo.toString());
if (isInRelationService) {
// Trying to update a declared relation type
String excMsg = "Relation type cannot be updated as it is declared in the Relation Service.";
throw new RuntimeException(excMsg);
}
String roleName = roleInfo.getName();
// Checks if the role info has already been described
if (roleName2InfoMap.containsKey(roleName)) {
StringBuilder excMsgStrB = new StringBuilder();
String excMsg = "Two role infos provided for role ";
excMsgStrB.append(excMsg);
excMsgStrB.append(roleName);
throw new InvalidRelationTypeException(excMsgStrB.toString());
}
roleName2InfoMap.put(roleName, new RoleInfo(roleInfo));
if (isDebugOn())
debug("addRoleInfo: exiting", null);
return;
}
// Sets the internal flag to specify that the relation type has been
// declared in the Relation Service
void setRelationServiceFlag(boolean flag) {
isInRelationService = flag;
return;
}
// Initializes the members, i.e. type name and role info list.
//
// -param relationTypeName Name of relation type
// -param roleInfoArray List of role definitions (RoleInfo objects)
//
// -exception IllegalArgumentException if null parameter
// -exception InvalidRelationTypeException If:
// - the same name has been used for two different roles
// - no role info provided
// - one null role info provided
private void initMembers(String relationTypeName,
RoleInfo[] roleInfoArray)
throws IllegalArgumentException,
InvalidRelationTypeException {
if (relationTypeName == null || roleInfoArray == null) {
String excMsg = "Invalid parameter.";
throw new IllegalArgumentException(excMsg);
}
if (isDebugOn())
debug("initMembers: entering", relationTypeName);
typeName = relationTypeName;
// Verifies role infos before setting them
// Can throw InvalidRelationTypeException
checkRoleInfos(roleInfoArray);
for (int i = 0; i < roleInfoArray.length; i++) {
RoleInfo currRoleInfo = roleInfoArray[i];
roleName2InfoMap.put(currRoleInfo.getName(),
new RoleInfo(currRoleInfo));
}
if (isDebugOn())
debug("initMembers: exiting", null);
return;
}
// Checks the given RoleInfo array to verify that:
// - the array is not empty
// - it does not contain a null element
// - a given role name is used only for one RoleInfo
//
// -param roleInfoArray array to be checked
//
// -exception IllegalArgumentException
// -exception InvalidRelationTypeException If:
// - the same name has been used for two different roles
// - no role info provided
// - one null role info provided
static void checkRoleInfos(RoleInfo[] roleInfoArray)
throws IllegalArgumentException,
InvalidRelationTypeException {
if (roleInfoArray == null) {
String excMsg = "Invalid parameter.";
throw new IllegalArgumentException(excMsg);
}
if (roleInfoArray.length == 0) {
// No role info provided
String excMsg = "No role info provided.";
throw new InvalidRelationTypeException(excMsg);
}
Set<String> roleNames = new HashSet<String>();
for (int i = 0; i < roleInfoArray.length; i++) {
RoleInfo currRoleInfo = roleInfoArray[i];
if (currRoleInfo == null) {
String excMsg = "Null role info provided.";
throw new InvalidRelationTypeException(excMsg);
}
String roleName = currRoleInfo.getName();
// Checks if the role info has already been described
if (roleNames.contains(roleName)) {
StringBuilder excMsgStrB = new StringBuilder();
String excMsg = "Two role infos provided for role ";
excMsgStrB.append(excMsg);
excMsgStrB.append(roleName);
throw new InvalidRelationTypeException(excMsgStrB.toString());
}
roleNames.add(roleName);
}
return;
}
// stuff for Tracing
private static String localClassName = "RelationTypeSupport";
// trace level
private boolean isTraceOn() {
return Trace.isSelected(Trace.LEVEL_TRACE, Trace.INFO_RELATION);
}
// private void trace(String className, String methodName, String info) {
// Trace.send(Trace.LEVEL_TRACE, Trace.INFO_RELATION, className, methodName, info);
// }
private void trace(String methodName, String info) {
Trace.send(Trace.LEVEL_TRACE, Trace.INFO_RELATION, localClassName, methodName, info);
Trace.send(Trace.LEVEL_TRACE, Trace.INFO_RELATION, "", "", "\n");
}
// private void trace(String className, String methodName, Exception e) {
// Trace.send(Trace.LEVEL_TRACE, Trace.INFO_RELATION, className, methodName, e);
// }
// private void trace(String methodName, Exception e) {
// Trace.send(Trace.LEVEL_TRACE, Trace.INFO_RELATION, localClassName, methodName, e);
// }
// debug level
private boolean isDebugOn() {
return Trace.isSelected(Trace.LEVEL_DEBUG, Trace.INFO_RELATION);
}
// private void debug(String className, String methodName, String info) {
// Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_RELATION, className, methodName, info);
// }
private void debug(String methodName, String info) {
Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_RELATION, localClassName, methodName, info);
Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_RELATION, "", "", "\n");
}
// private void debug(String className, String methodName, Exception e) {
// Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_RELATION, className, methodName, e);
// }
// private void debug(String methodName, Exception e) {
// Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_RELATION, localClassName, methodName, e);
// }
/**
* Deserializes a {@link RelationTypeSupport} from an {@link ObjectInputStream}.
*/
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
if (compat)
{
// Read an object serialized in the old serial form
//
ObjectInputStream.GetField fields = in.readFields();
typeName = (String) fields.get("myTypeName", null);
if (fields.defaulted("myTypeName"))
{
throw new NullPointerException("myTypeName");
}
roleName2InfoMap =
(Map<String,RoleInfo>) fields.get("myRoleName2InfoMap", null);
if (fields.defaulted("myRoleName2InfoMap"))
{
throw new NullPointerException("myRoleName2InfoMap");
}
isInRelationService = fields.get("myIsInRelServFlg", false);
if (fields.defaulted("myIsInRelServFlg"))
{
throw new NullPointerException("myIsInRelServFlg");
}
}
else
{
// Read an object serialized in the new serial form
//
in.defaultReadObject();
}
}
/**
* Serializes a {@link RelationTypeSupport} to an {@link ObjectOutputStream}.
*/
private void writeObject(ObjectOutputStream out)
throws IOException {
if (compat)
{
// Serializes this instance in the old serial form
//
ObjectOutputStream.PutField fields = out.putFields();
fields.put("myTypeName", typeName);
fields.put("myRoleName2InfoMap", roleName2InfoMap);
fields.put("myIsInRelServFlg", isInRelationService);
out.writeFields();
}
else
{
// Serializes this instance in the new serial form
//
out.defaultWriteObject();
}
}
}