/* * NOTE: This copyright does *not* cover user programs that use HQ * program services by normal system calls through the application * program interfaces provided as part of the Hyperic Plug-in Development * Kit or the Hyperic Client Development Kit - this is merely considered * normal use of the program, and does *not* fall under the heading of * "derived work". * * Copyright (C) [2004, 2005, 2006], Hyperic, Inc. * This file is part of HQ. * * HQ is free software; you can redistribute it and/or modify * it under the terms version 2 of the GNU General Public License as * published by the Free Software Foundation. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. */ package org.hyperic.hq.agent.server; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperic.hq.agent.AgentRemoteValue; import org.hyperic.util.GenericValueMap; import org.hyperic.util.StringUtil; import org.hyperic.util.config.ConfigResponse; public class ConfigStorage { public static final String PROP_TYPE = "entity.type"; public static final String PROP_ID = "entity.id"; public static final String PROP_TYPE_NAME = "entity.typeName"; public static final String CONFIG_PREFIX = "config."; public static final String NO_PREFIX = ""; private static Log log = LogFactory.getLog(ConfigStorage.class.getName()); private AgentStorageProvider storage; private String keylistName; private String rootPrefix; public ConfigStorage(AgentStorageProvider storage, String keylistName, String storagePrefix) { this.storage = storage; this.keylistName = keylistName; this.rootPrefix = storagePrefix; } public Key getKey(AgentRemoteValue args) { return new Key(args.getValue(PROP_TYPE), args.getValue(PROP_ID), args.getValue(PROP_TYPE_NAME)); } private String getKeys() { String keys = this.storage.getValue(this.keylistName); if (keys == null) { keys = ""; } return keys; } public Map<Key, ConfigResponse> load() { Map<Key, ConfigResponse> configs = new HashMap<Key, ConfigResponse>(); StringTokenizer tok = new StringTokenizer(getKeys(), Key.DELIM); while (tok.hasMoreTokens()) { Key key = new Key(tok.nextToken()); ConfigResponse cr = new ConfigResponse(); // read config from agent storage copy(key.storagePrefix, this.storage, NO_PREFIX, cr); configs.put(key, cr); if (log.isDebugEnabled()) { log.debug("Successfully restored config from storage: " + key + "-->" + cr); } } return configs; } public ConfigResponse put(Key key, AgentRemoteValue args) throws AgentStorageException { ConfigResponse cr = new ConfigResponse(); copy(CONFIG_PREFIX, args, NO_PREFIX, cr); put(key, cr); return cr; } public void put(Key key, ConfigResponse cr) throws AgentStorageException { // store config in agent storage copy(NO_PREFIX, cr, key.storagePrefix, this.storage); // if this key doesn't yet exist in out master list, add it String keys = getKeys(); String entry = key.getEntry(); log.debug("Searching for '" + entry + "' " + "within '" + keys + "'"); if (keys.indexOf(entry) == -1) { keys += entry; this.storage.setValue(this.keylistName, keys); } // Flush writes to agent storage this.storage.flush(); if (log.isDebugEnabled()) { log.debug("Successfully stored config into storage: " + key + "-->" + cr); } } public void remove(Key key) throws AgentStorageException { String keys = getKeys(); int len = keys.length(); keys = StringUtil.replace(keys, key.getEntry(), ""); if (len == keys.length()) { log.debug("Remove failed, key not found: " + key); return; } this.storage.setValue(this.keylistName, keys); this.storage.flush(); log.debug("Removed from storage: " + key); // With the key nuked from the keylist entry, it shouldn't matter // (functionally) that the keys for the entity will hang around in // the agent db because they'll never be seen. in the long run, we // should find some reliable way to make sure they're removed // properly. } /** * For all keys in the keySet that begin with prefix, * copy the corresponding key/value pairs from the srcMap to the * destination map. * @param srcPrefix Properties in the source map that begin with this * prefix will be copied. * @param src The source map to search for matching keys. * @param destPrefix This prefix will be prepended to the key names * that are copied. This is prepended after the srcPrefix has * been stripped. * @param dest The destination map to copy key/value pairs to. * @return The destination map. */ public static GenericValueMap copy(String srcPrefix, GenericValueMap src, String destPrefix, GenericValueMap dest) { int srcPrefixLen = srcPrefix.length(); //copy keys to avoid possible ConcurrentModificationException ArrayList keys = new ArrayList(); keys.addAll(src.getKeys()); for (int i=0; i<keys.size(); i++) { String key = (String)keys.get(i); if (key.startsWith(srcPrefix)) { String destKey = destPrefix + key.substring(srcPrefixLen); dest.setValue(destKey, src.getValue(key)); } } return dest; } public class Key { private static final String DELIM = "|"; private int type, id; private String typeName; private String storagePrefix; private String key; private String appdefKey; private void init(String type, String id, String typeName) { //XXX catch NFE init(Integer.parseInt(type), Integer.parseInt(id), typeName); } private void init(int type, int id, String typeName) { this.type = type; this.id = id; this.typeName = typeName; this.appdefKey = this.type + "-" + this.id + "-"; this.key = this.appdefKey + this.typeName; this.storagePrefix = rootPrefix + "." + this.key + "."; } private Key(String type, String id, String typeName) { init(type, id, typeName); } private Key(String key) { String val = key; int ix = val.indexOf("-"); if (ix == -1) { throw new IllegalArgumentException("Invalid key format: " + key); } String type = val.substring(0, ix); val = val.substring(ix+1); ix = val.indexOf("-"); if (ix == -1) { throw new IllegalArgumentException("Invalid key format: " + key); } String id = val.substring(0, ix); String typeName = val.substring(ix+1); init(type, id, typeName); } private String getEntry() { return this.key + DELIM; } public int getType() { return this.type; } public int getId() { return this.id; } public String getTypeName() { return this.typeName; } public int hashCode() { //same as AppdefEntityID.hashCode() return this.type * this.id; } public boolean equals(Object o) { if (o instanceof Key) { Key k = (Key)o; return ((k.type == this.type) && (k.id == this.id)); } else { return false; } } public String toString() { return this.key; } } }