Package winterwell.utils.web

Source Code of winterwell.utils.web.ConcurrentMapConverter

package winterwell.utils.web;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import winterwell.utils.Key;
import winterwell.utils.Utils;
import winterwell.utils.containers.ArrayMap;
import winterwell.utils.containers.ArraySet;
import winterwell.utils.io.FileUtils;
import winterwell.utils.io.XStreamBinaryConverter;
import winterwell.utils.io.XStreamBinaryConverter.BinaryXML;
import winterwell.utils.reporting.Log;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.SingleValueConverter;
import com.thoughtworks.xstream.io.xml.CompactWriter;

/**
* Special case serialiser for {@link ConcurrentHashMap}.
* <p>
* NB: ConcurrentHashMap also has a special case in Java serialisation.
*
* @author daniel
*/
final class ConcurrentMapConverter implements SingleValueConverter {

  @Override
  public boolean canConvert(Class type) {
    return type == ConcurrentHashMap.class;
  }

  @Override
  public Object fromString(String str) {
    HashMap<?, ?> amap = XStreamUtils.serialiseFromXml(str);
    ConcurrentHashMap map = new ConcurrentHashMap(amap);
    return map;
  }

  @Override
  public String toString(Object obj) {
    Map<?, ?> map = (Map) obj;
    HashMap<?, ?> amap = new HashMap(map);
    return XStreamUtils.serialiseToXml(amap);
  }

}

/**
* Special case serialiser for {@link Timestamp}, due to bugs in transmission of
* Timestamps between different servers.
* <p>
* This loses some accuracy (nano vs milli-seconds) -- does anyone care?
*
* @author daniel
*/
final class TimestampConverter implements SingleValueConverter {

  @Override
  public boolean canConvert(Class type) {
    return type == Timestamp.class;
  }

  @Override
  public Object fromString(String str) {
    // TODO should we also handle the xml from an un-modified XStream here?
    Long utc = Long.valueOf(str);
    return new Timestamp(utc);
  }

  @Override
  public String toString(Object obj) {
    Timestamp ts = (Timestamp) obj;
    return Long.toString(ts.getTime());
  }

}

/**
* Separated out to isolate the XStream dependency.
*
* @author daniel
* @testedby {@link XStreamUtilsTest}
*/
public class XStreamUtils {

  private static XStream _xstream;

  // static final XStream JSON_XSTREAM = new XStream(new
  // JsonHierarchicalStreamDriver());

  /**
   * Setup aliases to give prettier shorter xml. E.g. &lt;S>hello&lt;/S>
   * instead of &lt;java.lang.String>hello&lt;/java.lang.String>
   */
  static void initShorterXml(XStream xstream) {
    // prettier shorter xml
    xstream.alias("key", Key.class);
    xstream.alias("map", HashMap.class);
    xstream.alias("amap", ArrayMap.class);
    xstream.alias("cmap", ConcurrentHashMap.class);
    xstream.alias("list", ArrayList.class);
    xstream.alias("set", HashSet.class);
    xstream.alias("aset", ArraySet.class);
    // primitives (this is ugly)
    xstream.alias("I", Integer.class);
    xstream.alias("i", int.class);
    xstream.alias("L", Long.class);
    xstream.alias("l", long.class);
    xstream.alias("D", Double.class);
    xstream.alias("d", double.class);
    xstream.alias("S", String.class);
    xstream.alias("B", Boolean.class);
    xstream.alias("b", boolean.class);
  }

  @SuppressWarnings("unchecked")
  public static <X> X serialiseFromXml(InputStream xml) {
    X x = (X) xstream().fromXML(xml);
    FileUtils.close(xml);
    return x;
  }

  @SuppressWarnings("unchecked")
  public static <X> X serialiseFromXml(Reader xml) {
    X x = (X) xstream().fromXML(xml);
    FileUtils.close(xml);
    return x;
  }

  @SuppressWarnings("unchecked")
  public static <X> X serialiseFromXml(String xml) {
    return (X) xstream().fromXML(xml);
  }

  /**
   * Uses XStream
   * <p>
   * <h3>Thread Safety</h3>
   * XStream serialisation is not thread safe :( If your object is edited by
   * another thread during a save, it can lead to a corrupted version getting
   * saved.
   * <p>
   * This method synchronises on the top-level object to provide a bit of
   * safety. However there are cases where this is not enough: e.g. if child
   * objects have synchronised blocks but the top level object doesn't.
   *
   * @param object
   * @return an xml representation for object
   * @see #serialiseFromXml(String)
   */
  public static String serialiseToXml(Object object) {
    // neither can nor need to sychronise on null - this is what xstream
    // would return
    if (object == null)
      return "<null/>";
    synchronized (object) {
      StringWriter sw = new StringWriter();
      CompactWriter compact = new CompactWriter(sw);
      xstream().marshal(object, compact);
      return sw.toString();
    }
  }

  /**
   * Uses XStream
   * <p>
   * <h3>Thread Safety</h3>
   * XStream serialisation is not thread safe :( If your object is edited by
   * another thread during a save, it can lead to a corrupted version getting
   * saved.
   * <p>
   * This method synchronises on the top-level object to provide a bit of
   * safety. However there are cases where this is not enough: e.g. if child
   * objects have synchronised blocks but the top level object doesn't.
   *
   * @param writer
   *            Serialise out to here. This will NOT be closed here.
   * @param object
   * @see #serialiseFromXml(String)
   */
  public static void serialiseToXml(Writer writer, Object object) {
    // We neither can nor need to sychronise on null - this is what xstream
    // would return
    if (object == null) {
      try {
        writer.write("<null/>");
      } catch (IOException e) {
        throw Utils.runtime(e);
      }
    }
    synchronized (object) {
      CompactWriter compact = new CompactWriter(writer);
      xstream().marshal(object, compact);
    }
  }

  static XStream setupXStream() {
    try {
      XStream xstream = new XStream();
      // prettier shorter xml
      XStreamUtils.initShorterXml(xstream);
      // Binary fields
      xstream.registerConverter(new XStreamBinaryConverter());
      // ConcurrentHashMaps
      xstream.registerConverter(new ConcurrentMapConverter());
      // Hack: SQL Timestamps
      xstream.registerConverter(new TimestampConverter());
      return xstream;
    } catch (Throwable e) {
      Log.report(e);
      return null;
    }
  }

  /**
   * @return default XStream instance with: - shorter xml c.f.
   *         {@link #initShorterXml(XStream)} - {@link BinaryXML} via
   *         {@link XStreamBinaryConverter} - better ConcurrentHashMap output
   *         via {@link ConcurrentMapConverter}
   */
  public static XStream xstream() {
    if (XStreamUtils._xstream == null) {
      XStreamUtils._xstream = setupXStream();
    }
    return XStreamUtils._xstream;
  }

}
TOP

Related Classes of winterwell.utils.web.ConcurrentMapConverter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.