/*
* RHQ Management Platform
* Copyright (C) 2005-2008 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.core.domain.resource.group;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* A ClusterKey represents an AutoCluster of resources in a Cluster Hierarchy. Given the key it is
* possible to determine specific membership of the AutoCluster at any time. It represents the Cluster
* Root (a Compatible Group) and the hierarchy of resource cluster nodes. Each level of the hierarchy
* represents a resource cluster node defined by a Plugin|ResourceType|ResourceKey tuple.<br/>
* <br/>
* The ClusterKey has the following form:
* <pre>
* Expressed iteratively:
*
* CompatibleGroupId::ResourceTypeId1:ResourceKey1::ResourceTypeId2:ResourceKey2:: ... ::ResourceTypeIdN:ResourceKeyN
*
* Expressed Recursively:
*
* Depth-1 AutoCluster CompatibleGroupId::ResourceTypeId1:ResourceKey1
* Depth-N AutoCluster <ParentClusterKey>::ResourceTypeIdN:ResourceKeyN
* </pre>
* @author jay shaughnessy
*
*/
public class ClusterKey implements Serializable {
private static final long serialVersionUID = 1L;
static final String DELIM = ":";
static final String DELIM_NODE = "::";
// Id of the compatible resource group containing the root set of clustered resources.
private int clusterGroupId = 0;
private List<ClusterKey.Node> hierarchy;
private String key = null;
public ClusterKey() {
}
/** Construct ClusterKey with to-be-defined Hierarchy */
public ClusterKey(int clusterResourceGroupId) {
this.clusterGroupId = clusterResourceGroupId;
this.hierarchy = new ArrayList<ClusterKey.Node>();
}
/** Construct ClusterKey for a top level AutoCluster */
public ClusterKey(int clusterResourceGroupId, int resourceTypeId, String resourceKey) {
this.clusterGroupId = clusterResourceGroupId;
this.hierarchy = new ArrayList<ClusterKey.Node>();
this.hierarchy.add(new ClusterKey.Node(resourceTypeId, resourceKey));
}
/** Construct a new ClusterKey for a child AutoCluster of the provided parentKey*/
public ClusterKey(ClusterKey parentKey, int childResourceTypeId, String childResourceKey) {
List<ClusterKey.Node> rootClusterNodes = parentKey.getHierarchy();
this.clusterGroupId = parentKey.getClusterGroupId();
this.hierarchy = new ArrayList<ClusterKey.Node>(rootClusterNodes); //.size() + 1);
// Collections.copy(this.hierarchy, rootClusterNodes);
this.hierarchy.add(new ClusterKey.Node(childResourceTypeId, childResourceKey));
}
public int getClusterGroupId() {
return clusterGroupId;
}
public List<ClusterKey.Node> getHierarchy() {
return hierarchy;
}
/**
* Increase depth of hierarchy with new child node.
* @param childResourceTypeId
* @param childResourceKey
* @return The updated hierarchy
*/
public List<ClusterKey.Node> addChildToHierarchy(int childResourceTypeId, String childResourceKey) {
this.hierarchy.add(new ClusterKey.Node(childResourceTypeId, childResourceKey));
return hierarchy;
}
/**
* @return the depth of the AutoCluster hierarchy. Just another way of getting the hierarchy size.
*/
public int getDepth() {
return hierarchy.size();
}
/**
* @param newDepth > 0, keep only the hierarchy up to an including the specified depth.
*/
public void setDepth(int newDepth) {
while ((newDepth > 0) && (this.hierarchy.size() > newDepth)) {
this.hierarchy.remove(this.hierarchy.size() - 1);
}
}
public String getKey() {
if (null == key) {
StringBuilder b = new StringBuilder();
b.append(clusterGroupId);
for (ClusterKey.Node node : hierarchy) {
b.append(DELIM_NODE);
b.append(node.toString());
}
key = b.toString();
}
return key;
}
/**
* format: see class doc, used delimiters ClusterKey.DELIM_NODE and ClusterKey.DELIM
*/
@Override
public String toString() {
return getKey();
}
public static ClusterKey valueOf(String clusterKey) {
ClusterKey result = null;
try {
String[] nodes = clusterKey.split(DELIM_NODE);
int groupId = Integer.valueOf(nodes[0]);
result = new ClusterKey(groupId);
for (int i = 1; i < nodes.length; ++i) {
String[] nodeInfo = nodes[i].split(DELIM);
if ((nodeInfo.length != 2) || "".equals(nodeInfo[0].trim()) || "".equals(nodeInfo[1].trim())) {
throw new IllegalArgumentException("Invalid cluster key node: " + nodeInfo);
}
result.addChildToHierarchy(Integer.valueOf(nodeInfo[0]), nodeInfo[1]);
}
} catch (Exception e) {
result = null;
}
return result;
}
/**
* @param clusterKey
* @return The ResourceType id of the resource group this clusterKey defines.
*/
public static int getResourceType(ClusterKey clusterKey) {
List<ClusterKey.Node> nodes = clusterKey.getHierarchy();
return nodes.get(nodes.size() - 1).getResourceTypeId();
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof ClusterKey))
return false;
ClusterKey that = (ClusterKey) o;
if (!getKey().equals(that.getKey()))
return false;
return true;
}
@Override
public int hashCode() {
return getKey().hashCode();
}
/**
* Immutable class representing a node in an AutoCluster hierarchy. The node describes a
* Set of like resources (same type and same resource key). By itself the node lacks any context
* and so typically this class is for use within a ClusterKey, which qualifies the node with
* the root group and constraining node ancestry.. */
public static class Node implements Serializable {
private static final long serialVersionUID = 1L;
int resourceTypeId;
String resourceKey;
public Node() {
}
public Node(int resourceTypeId, String resourceKey) {
super();
this.resourceTypeId = resourceTypeId;
this.resourceKey = encode(resourceKey);
}
public int getResourceTypeId() {
return resourceTypeId;
}
public String getResourceKey() {
return decode(resourceKey);
}
/**
* format: resourceTypeId:resourceKey (actual delimiter is ClusterKey.DELIM)
*/
@Override
public String toString() {
return resourceTypeId + DELIM + resourceKey;
}
private String encode(String info) {
return info.replace(":", "%3a");
}
private String decode(String code) {
return code.replace("%3a", ":");
}
}
}