JivePropertiesExtension.java

  1. /**
  2.  *
  3.  * Copyright 2003-2007 Jive Software.
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smackx.jiveproperties.packet;

  18. import java.io.ByteArrayOutputStream;
  19. import java.io.ObjectOutputStream;
  20. import java.io.Serializable;
  21. import java.util.Collection;
  22. import java.util.Collections;
  23. import java.util.HashMap;
  24. import java.util.HashSet;
  25. import java.util.Map;
  26. import java.util.logging.Level;
  27. import java.util.logging.Logger;

  28. import org.jivesoftware.smack.packet.ExtensionElement;
  29. import org.jivesoftware.smack.util.XmlStringBuilder;
  30. import org.jivesoftware.smack.util.stringencoder.Base64;

  31. /**
  32.  * Properties provide an easy mechanism for clients to share data. Each property has a
  33.  * String name, and a value that is a Java primitive (int, long, float, double, boolean)
  34.  * or any Serializable object (a Java object is Serializable when it implements the
  35.  * Serializable interface).
  36.  *
  37.  */
  38. public class JivePropertiesExtension implements ExtensionElement {
  39.     /**
  40.      * Namespace used to store packet properties.
  41.      */
  42.     public static final String NAMESPACE = "http://www.jivesoftware.com/xmlns/xmpp/properties";

  43.     public static final String ELEMENT = "properties";

  44.     private static final Logger LOGGER = Logger.getLogger(JivePropertiesExtension.class.getName());
  45.    
  46.     private final Map<String, Object> properties;

  47.     public JivePropertiesExtension() {
  48.         properties = new HashMap<String, Object>();
  49.     }

  50.     public JivePropertiesExtension(Map<String, Object> properties) {
  51.         this.properties = properties;
  52.     }

  53.     /**
  54.      * Returns the packet property with the specified name or <tt>null</tt> if the
  55.      * property doesn't exist. Property values that were originally primitives will
  56.      * be returned as their object equivalent. For example, an int property will be
  57.      * returned as an Integer, a double as a Double, etc.
  58.      *
  59.      * @param name the name of the property.
  60.      * @return the property, or <tt>null</tt> if the property doesn't exist.
  61.      */
  62.     public synchronized Object getProperty(String name) {
  63.         if (properties == null) {
  64.             return null;
  65.         }
  66.         return properties.get(name);
  67.     }

  68.     /**
  69.      * Sets a property with an Object as the value. The value must be Serializable
  70.      * or an IllegalArgumentException will be thrown.
  71.      *
  72.      * @param name the name of the property.
  73.      * @param value the value of the property.
  74.      */
  75.     public synchronized void setProperty(String name, Object value) {
  76.         if (!(value instanceof Serializable)) {
  77.             throw new IllegalArgumentException("Value must be serialiazble");
  78.         }
  79.         properties.put(name, value);
  80.     }

  81.     /**
  82.      * Deletes a property.
  83.      *
  84.      * @param name the name of the property to delete.
  85.      */
  86.     public synchronized void deleteProperty(String name) {
  87.         if (properties == null) {
  88.             return;
  89.         }
  90.         properties.remove(name);
  91.     }

  92.     /**
  93.      * Returns an unmodifiable collection of all the property names that are set.
  94.      *
  95.      * @return all property names.
  96.      */
  97.     public synchronized Collection<String> getPropertyNames() {
  98.         if (properties == null) {
  99.             return Collections.emptySet();
  100.         }
  101.         return Collections.unmodifiableSet(new HashSet<String>(properties.keySet()));
  102.     }

  103.     /**
  104.      * Returns an unmodifiable map of all properties.
  105.      *
  106.      * @return all properties.
  107.      */
  108.     public synchronized Map<String, Object> getProperties() {
  109.         if (properties == null) {
  110.             return Collections.emptyMap();
  111.         }
  112.         return Collections.unmodifiableMap(new HashMap<String, Object>(properties));
  113.     }

  114.     @Override
  115.     public String getElementName() {
  116.         return ELEMENT;
  117.     }

  118.     @Override
  119.     public String getNamespace() {
  120.         return NAMESPACE;
  121.     }

  122.     @Override
  123.     public CharSequence toXML() {
  124.         XmlStringBuilder xml = new XmlStringBuilder(this);
  125.         xml.rightAngleBracket();
  126.         // Loop through all properties and write them out.
  127.         for (String name : getPropertyNames()) {
  128.             Object value = getProperty(name);
  129.             xml.openElement("property");
  130.             xml.element("name", name);
  131.             xml.halfOpenElement("value");

  132.             String type;
  133.             String valueStr;
  134.             if (value instanceof Integer) {
  135.                 type = "integer";
  136.                 valueStr = Integer.toString((Integer) value);
  137.             }
  138.             else if (value instanceof Long) {
  139.                 type = "long";
  140.                 valueStr = Long.toString((Long) value);
  141.             }
  142.             else if (value instanceof Float) {
  143.                 type = "float";
  144.                 valueStr = Float.toString((Float) value);
  145.             }
  146.             else if (value instanceof Double) {
  147.                 type = "double";
  148.                 valueStr = Double.toString((Double) value);
  149.             }
  150.             else if (value instanceof Boolean) {
  151.                 type = "boolean";
  152.                 valueStr = Boolean.toString((Boolean) value);
  153.             }
  154.             else if (value instanceof String) {
  155.                 type = "string";
  156.                 valueStr = (String) value;
  157.             }
  158.             // Otherwise, it's a generic Serializable object. Serialized objects are in
  159.             // a binary format, which won't work well inside of XML. Therefore, we base-64
  160.             // encode the binary data before adding it.
  161.             else {
  162.                 ByteArrayOutputStream byteStream = null;
  163.                 ObjectOutputStream out = null;
  164.                 try {
  165.                     byteStream = new ByteArrayOutputStream();
  166.                     out = new ObjectOutputStream(byteStream);
  167.                     out.writeObject(value);
  168.                     type = "java-object";
  169.                     valueStr = Base64.encodeToString(byteStream.toByteArray());
  170.                 }
  171.                 catch (Exception e) {
  172.                     LOGGER.log(Level.SEVERE, "Error encoding java object", e);
  173.                     type = "java-object";
  174.                     valueStr = "Serializing error: " + e.getMessage();
  175.                 }
  176.                 finally {
  177.                     if (out != null) {
  178.                         try {
  179.                             out.close();
  180.                         }
  181.                         catch (Exception e) {
  182.                             // Ignore.
  183.                         }
  184.                     }
  185.                     if (byteStream != null) {
  186.                         try {
  187.                             byteStream.close();
  188.                         }
  189.                         catch (Exception e) {
  190.                             // Ignore.
  191.                         }
  192.                     }
  193.                 }
  194.             }
  195.             xml.attribute("type", type);
  196.             xml.rightAngleBracket();
  197.             xml.escape(valueStr);
  198.             xml.closeElement("value");
  199.             xml.closeElement("property");
  200.         }
  201.         xml.closeElement(this);

  202.         return xml;
  203.     }

  204. }