/* * Lokomo OneCMDB - An Open Source Software for Configuration * Management of Datacenter Resources * * Copyright (C) 2006 Lokomo Systems AB * * 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; either version 2 of the License, or (at * your option) any later version. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. * * Lokomo Systems AB can be contacted via e-mail: info@lokomo.com or via * paper mail: Lokomo Systems AB, Sv�rdv�gen 27, SE-182 33 * Danderyd, Sweden. * */ package org.onecmdb.core.internal.model; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.onecmdb.core.ErrorObject; import org.onecmdb.core.IAttribute; import org.onecmdb.core.ICi; import org.onecmdb.core.IPath; import org.onecmdb.core.IType; import org.onecmdb.core.IValue; import org.onecmdb.core.IValueSelector; import org.onecmdb.core.internal.storage.IDaoReader; /** * <p>The basic, underlying, data container used in the system.</p> */ public class ConfigurationItem implements ICi { private ItemId id; private String displayName; private ItemId derivedFrom; private boolean isBlueprint; private String description; private String templatePath; private Long gid; private Date lastModified; private Date createTime; protected transient IDaoReader daoReader; /** * Offspring path */ private String alias; // {{{ springified public ConfigurationItem() { this.id = new ItemId(); } public void setItemId(long id) { this.id = new ItemId(id); } public void setDaoReader(IDaoReader reader) { this.daoReader = reader; } /** * Copy internal state from the item passed in. * * @param item */ protected void copy(ConfigurationItem item) { this.id = item.id; this.displayName = item.displayName; this.derivedFrom = item.derivedFrom; this.isBlueprint = item.isBlueprint; this.description = item.description; this.alias = item.alias; this.daoReader = item.daoReader; } /** * <p>An expression, describing how this configuration item, should be * presented to others others. The expression is built up via ordinary text * and tokens, <em>pointing</em> to attributes owned by this configuration * item.</p> * <blockquote> * Date is ${date} * </blockquote> * * @param expession */ public final void setDisplayNameExpression(String expr) { this.displayName = expr; } public final String getDisplayNameExpression() { return (this.displayName); } // }}} protected ConfigurationItem(ItemId id) { this.id = id; } protected ConfigurationItem(String a, String b) { } /** * Fetch all attributes as an iterator, which gives the possibility to * retreive the attributes in a lazy manner. * * @return */ public Set<IAttribute> getAttributes() { if (this.daoReader == null) { return (Collections.emptySet()); } return (this.daoReader.getAttributesFor(this.getId())); } /** * Search for, and return the attribute with a certain id. * * @param attrId Identifier for the attribute to search for * @return The attribute, as reference, or <code>null</code> in case it * does not exist */ public IAttribute getAttribute(ItemId attrId) { return(getAttributeWithId(attrId)); } public IAttribute getAttributeWithId(ItemId attrId) { IAttribute attr = daoReader.findAttributeById(attrId); return(attr); /* for (IAttribute attr : this.getAttributes()) { if (attr.getId().equals(attrId)) { return attr; } } return null; */ } public List<IAttribute> getAttributesWithAlias(String alias) { HashMap<String, Object> crit = new HashMap<String, Object>(); crit.put("ownerId", id.asLong()); crit.put("alias", alias); List list = daoReader.query(BasicAttribute.class, crit); return(list); /* List<IAttribute> list = new ArrayList<IAttribute>(); for (IAttribute attr : this.getAttributes()) { if (alias.equals(attr.getAlias())) { list.add(attr); } } return(list); */ } public Set<IAttribute> getAttributeDefinitions() { // Query this ci for attribute that have no derivedFrom. HashMap<String, Object> crit = new HashMap<String, Object>(); crit.put("ownerId", id.asLong()); crit.put("derivedFromId", null); List list = daoReader.query(BasicAttribute.class, crit); // The result map. Set<IAttribute> resultSet = new HashSet<IAttribute>(); for (IAttribute a : (List<IAttribute>)list) { resultSet.add(a); } ICi parent = getDerivedFrom(); if (parent != null) { resultSet.addAll(parent.getAttributeDefinitions()); } return(resultSet); } public IAttribute getAttributeDefinitionWithAlias(String alias) { // Query datasource HashMap<String, Object> crit = new HashMap<String, Object>(); crit.put("ownerId", id.asLong()); crit.put("alias", alias); crit.put("derivedFromId", null); List list = daoReader.query(BasicAttribute.class, crit); // Found it? if (list.size() == 1) { return((IAttribute)list.get(0)); } // Ask parent. ICi parent = getDerivedFrom(); if (parent != null) { return(parent.getAttributeDefinitionWithAlias(alias)); } // Not found return(null); } public List<IAttribute> getAddableAttributes() { ICi derivedFrom = this.getDerivedFrom(); if (derivedFrom == null) { return(Collections.emptyList()); } HashMap<String, IAttribute> addableAttributes = new HashMap<String, IAttribute>(); // Add what derived from think is ok. List<IAttribute> parentAttributes = derivedFrom.getAddableAttributes(); for (IAttribute a : parentAttributes) { addableAttributes.put(a.getAlias(), a); } // Check my attributes. HashMap<String, List<IAttribute>> thisAttributeMap = new HashMap<String, List<IAttribute>>(); for (IAttribute a : getAttributes()) { List<IAttribute> list = thisAttributeMap.get(a.getAlias()); if (list == null) { list = new ArrayList<IAttribute>(); thisAttributeMap.put(a.getAlias(), list); } list.add(a); } for (String alias : thisAttributeMap.keySet()) { List<IAttribute> attributes = thisAttributeMap.get(alias); IAttribute definition = getAttributeDefinitionWithAlias(alias); if ((definition.getMaxOccurs() < 0) || (attributes.size() < definition.getMaxOccurs())) { addableAttributes.put(definition.getAlias(), definition); } else { addableAttributes.remove(definition.getAlias()); } } List<IAttribute> result = new ArrayList<IAttribute>(addableAttributes.values()); return(result); } /** * A CI is identified by its ID, which once set never changes. Note, there * is no support to set the hash in this interface. Each implementation must * deal with the details regarding this issue. * * @param newId */ public ItemId getId() { return this.id; } protected IDaoReader getDaoReader() { return (this.daoReader); } public boolean isComplex(){ return true; } public ICi getDerivedFrom() { if (this.derivedFrom == null) { return (null); } if (getDaoReader() == null) { return (null); } ICi derived = getDaoReader().findById(this.derivedFrom); return (derived); } public void setDerivedFrom(ICi ci) { this.derivedFrom = ci.getId(); } public Set<ICi> getOffsprings() { if (getDaoReader() == null) { return (Collections.emptySet()); } return (daoReader.getOffsprings(this.getId())); } public Object getAdapter(Class type) { return null; } public boolean isBlueprint() { return (isBlueprint); } public void setIsBlueprint(boolean value) { this.isBlueprint = value; } public boolean getIsBlueprint() { return (this.isBlueprint); } /** * * protected ConfigurationItem copy() { Moved to RFCCopyCi * * @return */ /* * protected ConfigurationItem copy() { * * ConfigurationItem copy = new ConfigurationItem(); for (IAttribute a : * getAttributes()) { * // {{{ strategy 1) * * find the policy.... from (?) * { * } // }}} * // {{{ strategy 2) * * send a message, and provide a callback, where the the new offspring can * be feteched from * { * * final Map<String key , IAttribute offspring > data = new HashMap<String,IAttribute>(1); * * NotificationCallback callback = new NotificationCallback() { public void * callback(Object callbackData) { data.put("offspring", (IAttribute) * callbackData); * // let others now (should be handled more generically though) beencalled = * true; synchronized(this) { notify(); } } }; * * * * callback.waitForRProceessed(); IAttribute newOffspring = * data.get("offspring"); * * * // wait for callback (at most 2 seconds) * // clone the attribute, by accessing IExtensibleAttribute ea = * (IExtensibleAttribute) * a.getDerivedFrom().getAdapter(IExtensibleAttribute.class); * ea.createOffspring(a.getName(), a.getType(), null); * * copy.addAttribute(a); } // }}} * } * * * * return copy; } */ public void setAlias(String value) { this.alias = value; } public String getAlias() { return (this.alias); } /** * Generates a display name according to displayName expression. * If no expression is set an empty string is returned. */ public final String getDisplayName() { String name = ""; if (getDisplayNameExpression() != null) { name = evaluate(getDisplayNameExpression()); } return name; } /** * The beginning of an <em>interpreted</em> expression language which may * be used to extract information from this configuration item. * <p> * Via an expresion in the form: * </p> * <blockqoute> * <code>[<em>text</em>]${<em>token</em>[.<em>token</em>]}[<em>text</em>]</code>... * </blockqoute> * <p> * Text can be interpersed with attribute values from this configuration * item * </p> * <p> * The token <em>points</em> out attributes. With punctatition, nested * attributes may be reached</em> * * @param expr * The expression to evalaute * @return A string representation of the evalaution */ public String evaluate(final String expr) { StringBuffer sb = new StringBuffer(); Pattern p = Pattern.compile("\\$\\{([^}]*)\\}"); int start = 0; Matcher m = p.matcher(expr); while (m.find()) { int end = m.start(); sb.append(expr.substring(start, end)); String tok = m.group(1); sb.append(resolve(tok)); start = m.end(); } if (start < expr.length()) { sb.append(this.displayName.substring(start)); } return sb.toString().trim(); } private String resolve(String tokens) { final int p = tokens.indexOf('.'); final String tok; if (p != -1) { tok = tokens.substring(0, p); tokens = p + 1 < tokens.length() ? tokens.substring(p + 1) : null; } else { tok = tokens; tokens = null; } for (ICi ciAttr : getAttributesWithAlias(tok)) { if (ciAttr instanceof IAttribute) { IAttribute attr = (IAttribute) ciAttr; if (tokens == null) { IValue v = attr.getValue(); return v != null ? v.getDisplayName() : ""; } else { IValue deref = attr.getValue(); if (deref == null || !(deref instanceof ConfigurationItem)) { return ""; } ConfigurationItem ci = (ConfigurationItem) deref; return ci.resolve(tokens); } } } if ("alias".equals(tok)) { return(this.getAlias()); } if ("id".equals(tok)) { return(this.getId().toString()); } return ""; } /** * Creates a string repesentation of this configuration item by * concatenating the identifier, the displayname, and every contained * attributes recursivly * * <pre> * * #nnnnn: displayname [{ * attr : [type] value { * attr : [type] value * ... * } * } * attr * } * * </pre> */ public String toString() { return (toString(0, 3)); } protected String toString(int level, int maxLevel) { StringBuffer sb = new StringBuffer(getTab(level) + getId() + ":" + getAlias()); sb.append(toStringAttributes(level + 1, maxLevel)); return sb.toString(); } /** * TODO: We must discover cycles, for now we add a max level instead. * @param level * @return */ protected final String toStringAttributes(int level, int maxLevel) { if (level > maxLevel) { return "..."; } Set<IAttribute> attributeDefinitions = getAttributes(); if (level > maxLevel || attributeDefinitions.size() == 0) { return (""); } StringBuffer sb = new StringBuffer(); sb.append("\n" + getTab(level) + "{"); for (ICi attr : attributeDefinitions) { if (attr instanceof ConfigurationItem) { sb.append("\n"); sb.append(((ConfigurationItem) attr).toString(level, maxLevel)); } } sb.append("\n" + getTab(level) + "}"); return (sb.toString()); } /* * protected final String toStringAttrbutes(int level) { if * (getAttributes().size() == 0) { return ""; } StringBuffer sb = new * StringBuffer(); String tab = getTab(level); for (ICi attr : * getAttributes()) { if (sb.length() > 0) sb.append("\n"); sb.append(tab + * attr.toString());; AbstractAttribute aa = (AbstractAttribute) attr; * String nested = aa.toStringAttrbutes(level + 1); if (nested.length() != 0 ) { * sb.append(" {\n"); sb.append(nested + "\n"); sb.append(tab+"}"); } } * return sb.toString(); } */ protected String getTab(int level) { String tab = ""; for (int i = 0; i < level; i++) tab += " "; return tab; } @Override public boolean equals(Object obj) { if (obj == null || !getClass().equals(obj.getClass())) return false; ICi other = (ICi) obj; return getId().equals(other.getId()); } @Override public int hashCode() { return getId().hashCode(); } /** * {{{ Hibernate Setter/Getters!!! Could be done by Hibernate support of * PropertyAccessor * * Should we instead use Hiberante support for decoration. Persistent state * in Ci. longId : Long derivedFromId : Long */ public void setLongId(Long id) { this.id = ObjectConverter.convertLongToItemId(id); } public Long getLongId() { return (ObjectConverter.convertItemIdToLong(this.id)); } public void setDerivedFromId(Long id) { this.derivedFrom = ObjectConverter.convertLongToItemId(id); } public Long getDerivedFromId() { return (ObjectConverter.convertItemIdToLong(this.derivedFrom)); } /* * Hibernate }}} */ public IValue parseString(String s) { IValue value = ObjectConverter .convertUniqueStringToIValue(daoReader, s); return (value); } public IValue getNullValue() { IValue value = ObjectConverter .convertUniqueStringToIValue(daoReader, null); return value; } public String getAsString() { String s = ObjectConverter.convertICiToUniqueName(daoReader, this); return (s); } /** * This need to be in sync with the dao reader's getItemByUniqueName(). */ public String getUniqueName() { String s = ObjectConverter.convertICiToUniqueName(daoReader, this); return (s); } public IValue parseInputStream(InputStream in) { throw new IllegalAccessError("Not implemented!"); } /** * Defined in the IValue interface. */ public IType getValueType() { return (this); } public OutputStream asOutputStream() { throw new IllegalAccessError("Not implemented!"); } public IPath<IType> getOffspringPath() { IPath<IType> path = new Path<IType>(); ICi parent = getDerivedFrom(); if (parent != null) { IPath<IType> parentPath = parent.getOffspringPath(); path.addPath(parentPath); } path.addElement(this); return path; } public String getIcon() { List<IAttribute> iconAttrs = getAttributesWithAlias("icon"); if (!iconAttrs.isEmpty()) { IValue v = iconAttrs.get(0).getValue(); return v != null ? v.getAsString() : null; } return null; } public IValueSelector getValueSelector() { IValueSelector selector = new InstanceValueSelector(this); return (selector); } public String getDescription() { return this.description; } public void setDescription(String description) { this.description = description; } public IValue fromValue(IValue value) { return parseString(value != null ? value.getAsString() : null); } public ErrorObject validate(IValue v) { // TODO Auto-generated method stub return null; } /** * Can not perform this. */ public Object getAsJavaObject() { return null; } public boolean isNullValue() { return false; } public Set<IType> getAllOffspringTypes() { // Retrive offsprings templates. QueryCriteria<IType> crit = new QueryCriteria<IType>(); // Search for offsprings. crit.setOffspringOfId("" + this.getId().asLong()); // Only search for templates. crit.setMatchCiTemplates(true); crit.setMatchCiInstances(false); // Only serach for Ci's crit.setMatchCi(true); List<IType> cis = this.daoReader.query(crit, false); HashSet<IType> set = new HashSet<IType>(); set.addAll(cis); for (IType child: cis) { set.addAll(child.getAllOffspringTypes()); } return(set); } public String getTemplatePath() { return templatePath; } public void setTemplatePath(String templatePath) { this.templatePath = templatePath; } /** * Reference data.... */ private Long sourceId; private Long targetId; private String sourceTemplatePath; private String targetTemplatePath; private Long sourceAttributeId; public Long getSourceId() { return sourceId; } public void setSourceId(Long sourceId) { this.sourceId = sourceId; } public Long getTargetId() { return targetId; } public void setTargetId(Long targetId) { this.targetId = targetId; } public String getSourceTemplatePath() { return sourceTemplatePath; } public void setSourceTemplatePath(String sourceTemplatePath) { this.sourceTemplatePath = sourceTemplatePath; } public String getTargetTemplatePath() { return targetTemplatePath; } public void setTargetTemplatePath(String targetTemplatePath) { this.targetTemplatePath = targetTemplatePath; } public Long getSourceAttributeId() { return sourceAttributeId; } public void setSourceAttributeId(Long sourceAttributeId) { this.sourceAttributeId = sourceAttributeId; } public Long getGid() { return gid; } public void setGid(Long gid) { this.gid = gid; } public ICi getGroup() { if (this.gid == null) { return(null); } ICi group = daoReader.findById(new ItemId(this.gid)); return(group); } public boolean isDerivedFrom(ICi parent) { return(parent.getDerivedPath().isParent(getDerivedPath())); } public IPath<String> getDerivedPath() { return(new Path<String>(this.templatePath)); } public Date getLastModified() { return lastModified; } public void setLastModified(Date lastModified) { this.lastModified = lastModified; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } }