/* * 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.util.config; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.Map.Entry; import org.hyperic.util.GenericValueMap; import org.hyperic.util.StringUtil; public class ConfigResponse implements GenericValueMap, Serializable { public static final String CONCEALED_SECRET_VALUE = "#CONCEALED_SECRET_VALUE#"; Map attributes; private Map schemaOptionsMap; private ConfigSchema schema = null; /** * Empty, encoded ConfigResponse. */ public static final byte[] EMPTY_CONFIG; static { try { EMPTY_CONFIG = new ConfigResponse().encode(); } catch (EncodingException e) { //aint gonna happen. throw new IllegalArgumentException(e.getMessage(), e); } } /** * Create a ConfigResponse that will be validated against * the specified schema. Any default values in the Schema will * be set in the ConfigResponse * * @param schema The schema to validate option settings against. * If this is null then no schema validation will be done, this is * equivalent to using the no-argument constructor. */ public ConfigResponse ( ConfigSchema schema ) { this.attributes = new HashMap(); this.schema = schema; this.setSchema(schema) ; } /** * Create a ConfigResponse that will * not be validated against any schema. */ public ConfigResponse () { this.attributes = new HashMap(); this.schema = null; } /** * Create a ConfigResponse that will not be validated * against any schema. * @param attributes The config properties normally * populated by setValue(). */ public ConfigResponse (Map attributes) { this.attributes = attributes; this.schema = null; } public final void setSchema(final ConfigSchema configSchema) { if ( configSchema != null ) { this.schema = configSchema ; this.schemaOptionsMap = new HashMap(); List options = schema.getOptions(); ConfigOption opt = null; String sOptionKey = null ; for ( int i=0; i<options.size(); i++ ) { opt = (ConfigOption) options.get(i); sOptionKey = opt.getName() ; this.schemaOptionsMap.put(sOptionKey, opt); // Fill in any default values String def = opt.getDefault(); if (def != null && !attributes.containsKey(sOptionKey)) { this.attributes.put(sOptionKey, def); } } } }//EOM public void setValue(String key, boolean value) { setValue(key, String.valueOf(value)); } public void setValue(String key, int value) { setValue(key, String.valueOf(value)); } public void setValue(String key, long value) { setValue(key, String.valueOf(value)); } /** * Set the value for an option. * @param key The name of the option to set * @param value The value to set the option to. * @exception InvalidOptionException If this ConfigResponse does * not support the specified option. * @exception InvalidOptionValueException If the value supplied * is not a legal/valid value for the option. */ public void setValue(String key, String value) throws InvalidOptionException, InvalidOptionValueException { if ( this.schema != null ) { ConfigOption option = (ConfigOption) schemaOptionsMap.get(key); if ( option == null ) throw new InvalidOptionException(key); option.checkOptionIsValid(value); } this.attributes.put(key, value); } /** * Set the value for an option. * @param key The name of the option to set * @exception InvalidOptionException If this ConfigResponse does * not support the specified option. * @exception InvalidOptionValueException If the value supplied * is not a legal/valid value for the option. */ public void unsetValue(String key) throws InvalidOptionException, InvalidOptionValueException { if ( this.schema != null ) { ConfigOption option = (ConfigOption) schemaOptionsMap.get(key); if ( option == null ) throw new InvalidOptionException(key); } this.attributes.remove(key); } public String getValue(String key){ return this.getValue(key, false); } public String getValue(String key, boolean concealSecretValues){ if (concealSecretValues && ConfigSchema.isSecret(key)) { return CONCEALED_SECRET_VALUE; } return (String) this.attributes.get(key); } public String getValue(String key, String defaultValue){ String val = (String)this.attributes.get(key); return (val != null) ? val : defaultValue; } public Set<String> getKeys() { return this.attributes.keySet(); } /** * Decode a ConfigResponse from a byte array with * no schema validation. * @param data The response data to decode. * @exception EncodingException If the encoding is incorrect. */ public static ConfigResponse decode(byte[] data) throws EncodingException { try { return decode(null, data); } catch ( InvalidOptionException ioe ) { // XXX should never happen throw new EncodingException(ioe); } catch ( InvalidOptionValueException iove ) { throw new EncodingException(iove); } } /** * Decode a ConfigResponse from a byte array according * to the specified schema. * @param schema The schema to validate against. * @param data The response data to decode. * @exception EncodingException If the encoding is incorrect. * @exception InvalidOptionException If the data specifies as option * that is not valid for the given schema. * @exception InvalidOptionValueException If the data specifies * an illegal/invalid value for one of the options it contains. */ public static ConfigResponse decode(ConfigSchema schema, byte[] data) throws EncodingException, InvalidOptionException, InvalidOptionValueException { ObjectInputStream objectStream; ByteArrayInputStream byteStream; ConfigResponse res; try { byteStream = new ByteArrayInputStream(data); objectStream = new ObjectInputStream(byteStream); res = new ConfigResponse(schema); // Read attributes while(true){ String key, val; if((key = (String)objectStream.readObject()) == null) { break; } val = (String)objectStream.readObject(); res.setValue(key, val); } return res; } catch(IOException exc){ throw new EncodingException(exc.toString(), exc); } catch(ClassNotFoundException exc){ throw new EncodingException(exc.toString(), exc); } } /** * @return the encoded config, or null if the ConfigResponse passed in * is null. This is a handy method to avoid NPEs. */ public static byte[] safeEncode(ConfigResponse cr) throws EncodingException{ if (cr == null) return null; return cr.encode(); } public byte[] encode() throws EncodingException { ObjectOutputStream objectStream; objectStream = null; byte[] retVal = null; try { ByteArrayOutputStream byteStream; Iterator i; Set keys; byteStream = new ByteArrayOutputStream(); objectStream = new ObjectOutputStream(byteStream); keys = this.attributes.keySet(); i = keys.iterator(); while(i.hasNext()){ String key = (String) i.next(); objectStream.writeObject(key); objectStream.writeObject((String)this.attributes.get(key)); } objectStream.writeObject(null); objectStream.flush(); retVal = byteStream.toByteArray(); } catch(IOException exc){ throw new EncodingException(exc.toString(), exc); } finally { // ObjectStreams MUST be closed. if (objectStream != null ) try {objectStream.close();} catch(Exception ex){} } return retVal; } /** * Merge the values from another ConfigResponse into this object. * * @param other Other ConfigResponse to merge data from * @param overWrite If true, values from the 'other' response will * overwrite values with the same name in the object. */ //Do not change this method signature, else break plugin compat. public void merge(ConfigResponse other, boolean overWrite) { Set entrySet = other.attributes.entrySet(); for (Iterator i = entrySet.iterator(); i.hasNext();) { Entry entry = (Entry) i.next(); if (overWrite || this.attributes.get(entry.getKey()) == null) { this.attributes.put(entry.getKey(), entry.getValue()); } } } /** * @return The ConfigResponse as a Properties object */ public Properties toProperties() { if (this.attributes instanceof Properties) { return (Properties)this.attributes; } Properties props = new Properties(); Set entries = this.attributes.entrySet(); Iterator it = entries.iterator(); while(it.hasNext()) { Entry entry = (Entry)it.next(); String value = (String) entry.getValue(); if (value == null) { continue; } props.setProperty((String) entry.getKey(), value.trim()); } return props; } public final Map getConfig() { return this.attributes ; }//EOM public String toString(){ StringBuffer rtn = new StringBuffer(); for (Iterator it=attributes.entrySet().iterator(); it.hasNext(); ) { Entry entry = (Entry) it.next(); String key = entry.getKey().toString(); String val = null; if (ConfigSchema.isSecret(key)) { val = "******"; } else { if (entry.getValue() != null) { val = entry.getValue().toString(); } } rtn.append(key).append("=").append(val).append(","); } if (rtn.length() == 0) { return ""; } rtn.substring(0, rtn.length()-1); return rtn.toString(); } public int size(){ return this.attributes.size(); } public boolean equals(Object o) { if (o == null || !(o instanceof ConfigResponse)) { return false; } return ((ConfigResponse)o).toProperties().equals(this.toProperties()); } public int hashCode() { return this.toProperties().hashCode(); } /** * Break the named preference * @param delimiter the delimeter to break it up by * @param key the name of the preference * @return <code>List</code> of <code>String</code> tokens */ public List getPreferenceAsList(String key, String delimiter) throws InvalidOptionException { return StringUtil.explode(getValue(key), delimiter); } public final boolean supportsOption(final String sOptionKey) { return (this.schemaOptionsMap != null && this.schemaOptionsMap.containsKey(sOptionKey)) ; }//EOM }