venice-main-classic/src/com/silverwrist/venice/ui/rpc/XmlRpcSerializer.java
Eric J. Bowersox 4ee98731e6 the XML-RPC code passed its first validation - OK, next: add the "boxcarring"
call system.multicall, and tweak the implementation a bit after seeing the
output "live"
2002-01-17 08:33:40 +00:00

496 lines
15 KiB
Java

/*
* The contents of this file are subject to the Mozilla Public License Version 1.1
* (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
*
* Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
* WARRANTY OF ANY KIND, either express or implied. See the License for the specific
* language governing rights and limitations under the License.
*
* The Original Code is the Venice Web Communities System.
*
* The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
* for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
* Copyright (C) 2002 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
*
* Contributor(s):
*/
package com.silverwrist.venice.ui.rpc;
import java.beans.*;
import java.io.*;
import java.lang.ref.*;
import java.lang.reflect.*;
import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import org.apache.log4j.*;
import com.silverwrist.util.*;
public class XmlRpcSerializer
{
/*--------------------------------------------------------------------------------
* Static data members
*--------------------------------------------------------------------------------
*/
private static Category logger = Category.getInstance(XmlRpcSerializer.class);
private static XmlRpcSerializer self = null;
private static SimpleTimeZone utc = new SimpleTimeZone(0,"UTC");
/*--------------------------------------------------------------------------------
* Constructor
*--------------------------------------------------------------------------------
*/
private XmlRpcSerializer()
{ // do nothing
} // end constructor
/*--------------------------------------------------------------------------------
* External operations
*--------------------------------------------------------------------------------
*/
public final String serialize(boolean b)
{
return "<boolean>" + (b ? "1" : "0") + "</boolean>";
} // end serialize
public final String serialize(byte b)
{
return this.serialize((int)b);
} // end serialize
public final String serialize(char c)
{
return "<string>" + c + "</string>";
} // end serialize
public final String serialize(double d)
{
return "<double>" + d + "</double>";
} // end serialize
public final String serialize(float f)
{
return this.serialize((double)f);
} // end serialize
public final String serialize(int i)
{
return "<int>" + i + "</int>";
} // end serialize
public final String serialize(long l)
{
return this.serialize((double)l);
} // end serialize
public final String serialize(short s)
{
return this.serialize((int)s);
} // end serialize
public final String serialize(Object obj) throws XmlRpcFault
{
if (obj.getClass().isArray())
{ // treat arrays specially
Class ct = obj.getClass().getComponentType();
if (ct==Byte.TYPE)
return this.serialize((byte[])obj);
else if (ct==Boolean.TYPE)
return this.serialize((boolean[])obj);
else if (ct==Character.TYPE)
return this.serialize((char[])obj);
else if (ct==Short.TYPE)
return this.serialize((short[])obj);
else if (ct==Integer.TYPE)
return this.serialize((int[])obj);
else if (ct==Long.TYPE)
return this.serialize((long[])obj);
else if (ct==Float.TYPE)
return this.serialize((float[])obj);
else if (ct==Double.TYPE)
return this.serialize((double[])obj);
else
return this.serialize((Object[])obj);
} // end if
if (obj==null)
return serializeString(null);
// self-serializing objects
if (obj instanceof XmlRpcSelfSerializer)
return ((XmlRpcSelfSerializer)obj).serializeXmlRpc();
// types from java.io.*
if (obj instanceof InputStream)
return serializeInputStream((InputStream)obj);
// types from java.sql.*
if (obj instanceof java.sql.Blob)
{ // serialize the blob as a binary stream
try
{ // just get its input stream
return serializeInputStream(((java.sql.Blob)obj).getBinaryStream());
} // end try
catch (java.sql.SQLException e)
{ // fault on error here
throw new XmlRpcFault(XmlRpcFault.INTERNAL_ERROR,"unable to save binary data");
} // end catch
} // end if
// types from java.util.*
if (obj instanceof Calendar)
return serializeCalendar((Calendar)obj);
if (obj instanceof Collection)
return serializeCollection((Collection)obj);
if (obj instanceof java.util.Date)
return serializeDate((java.util.Date)obj);
if (obj instanceof Enumeration)
return serializeEnumeration((Enumeration)obj);
if (obj instanceof Iterator)
return serializeIterator((Iterator)obj);
if (obj instanceof Map)
return serializeMap((Map)obj);
// types from java.lang.ref.*
if (obj instanceof Reference) // for a reference, encode its referent
return this.serialize(((Reference)obj).get());
// types from java.lang.*
if (obj instanceof Boolean)
return this.serialize(((Boolean)obj).booleanValue());
if (obj instanceof Character)
return this.serialize(((Character)obj).charValue());
if ((obj instanceof Byte) || (obj instanceof Short) || (obj instanceof Integer))
return this.serialize(((Number)obj).intValue());
if ((obj instanceof Long) || (obj instanceof Float) || (obj instanceof Double))
return this.serialize(((Number)obj).doubleValue());
// String and StringBuffer are handled properly by the fallback mechanism below
// last-ditch fallback method - use toString, get the string, and encode it
return serializeString(obj.toString());
} // end serialize
public final String serialize(boolean[] ba)
{
StringBuffer b = new StringBuffer("<array>\r\n<data>\r\n");
for (int i=0; i<ba.length; i++)
b.append("<value>").append(this.serialize(ba[i])).append("</value>\r\n");
b.append("</data>\r\n</array>");
return b.toString();
} // end serialize
public final String serialize(byte[] ba) throws XmlRpcFault
{
return this.serializeInputStream(new ByteArrayInputStream(ba));
} // end serialize
public final String serialize(char[] ca)
{
StringBuffer b = new StringBuffer("<array>\r\n<data>\r\n");
for (int i=0; i<ca.length; i++)
b.append("<value>").append(this.serialize(ca[i])).append("</value>\r\n");
b.append("</data>\r\n</array>");
return b.toString();
} // end serialize
public final String serialize(double[] da)
{
StringBuffer b = new StringBuffer("<array>\r\n<data>\r\n");
for (int i=0; i<da.length; i++)
b.append("<value>").append(this.serialize(da[i])).append("</value>\r\n");
b.append("</data>\r\n</array>");
return b.toString();
} // end serialize
public final String serialize(float[] fa)
{
StringBuffer b = new StringBuffer("<array>\r\n<data>\r\n");
for (int i=0; i<fa.length; i++)
b.append("<value>").append(this.serialize((double)(fa[i]))).append("</value>\r\n");
b.append("</data>\r\n</array>");
return b.toString();
} // end serialize
public final String serialize(int[] ia)
{
StringBuffer b = new StringBuffer("<array>\r\n<data>\r\n");
for (int i=0; i<ia.length; i++)
b.append("<value>").append(this.serialize(ia[i])).append("</value>\r\n");
b.append("</data>\r\n</array>");
return b.toString();
} // end serialize
public final String serialize(long[] la)
{
StringBuffer b = new StringBuffer("<array>\r\n<data>\r\n");
for (int i=0; i<la.length; i++)
b.append("<value>").append(this.serialize((double)(la[i]))).append("</value>\r\n");
b.append("</data>\r\n</array>");
return b.toString();
} // end serialize
public final String serialize(short[] sa)
{
StringBuffer b = new StringBuffer("<array>\r\n<data>\r\n");
for (int i=0; i<sa.length; i++)
b.append("<value>").append(this.serialize((int)(sa[i]))).append("</value>\r\n");
b.append("</data>\r\n</array>");
return b.toString();
} // end serialize
public final String serialize(Object[] arr) throws XmlRpcFault
{
StringBuffer b = new StringBuffer("<array>\r\n<data>\r\n");
for (int i=0; i<arr.length; i++)
b.append("<value>").append(this.serialize(arr[i])).append("</value>\r\n");
b.append("</data>\r\n</array>");
return b.toString();
} // end serialize
public final String serializeBean(Object obj, BeanInfo binf) throws XmlRpcFault
{
PropertyDescriptor[] props = binf.getPropertyDescriptors();
StringBuffer b = new StringBuffer("<struct>\r\n");
final Object[] no_params = new Object[0];
for (int i=0; i<props.length; i++)
{ // get property names and values
Method mread = props[i].getReadMethod();
if (mread!=null)
{ // use the method to get the value associated with the name
try
{ // append the member data
b.append("<member>\r\n<name>").append(StringUtil.encodeHTML(props[i].getName()));
b.append("</name>\r\n<value>").append(this.serialize(mread.invoke(obj,no_params)));
b.append("</value>\r\n</member>\r\n");
} // end try
catch (IllegalAccessException iae)
{ // unable to access that method
throw new XmlRpcFault(XmlRpcFault.INTERNAL_ERROR,iae);
} // end catch
catch (InvocationTargetException ite)
{ // the method we invoked threw an exception
throw new XmlRpcFault(XmlRpcFault.INTERNAL_ERROR,ite.getTargetException());
} // end catch
} // end if
} // end for
b.append("</struct>\r\n");
return b.toString();
} // end serializeBean
public final String serializeBean(Object obj) throws XmlRpcFault
{
try
{ // get the BeanInfo via introspection
return serializeBean(obj,Introspector.getBeanInfo(obj.getClass()));
} // end try
catch (IntrospectionException e)
{ // introspection failed
throw new XmlRpcFault(XmlRpcFault.INTERNAL_ERROR,e);
} // end catch
} // end serializeBean
public final String serializeCalendar(Calendar cal)
{
// Create the two string buffers converting the date.
StringBuffer rc = new StringBuffer("<dateTime.iso8601>");
StringBuffer conv = new StringBuffer();
String c;
// Encode the year first.
conv.append("0000").append(cal.get(Calendar.YEAR));
c = conv.toString();
rc.append(c.substring(c.length()-4));
// Now the month...
conv.setLength(0);
conv.append("00").append(cal.get(Calendar.MONTH) - Calendar.JANUARY + 1);
c = conv.toString();
rc.append(c.substring(c.length()-2));
// And the day...
conv.setLength(0);
conv.append("00").append(cal.get(Calendar.DAY_OF_MONTH));
c = conv.toString();
rc.append(c.substring(c.length()-2)).append('T');
// And the hour...
conv.setLength(0);
conv.append("00").append(cal.get(Calendar.HOUR_OF_DAY));
c = conv.toString();
rc.append(c.substring(c.length()-2)).append(':');
// And the minute...
conv.setLength(0);
conv.append("00").append(cal.get(Calendar.MINUTE));
c = conv.toString();
rc.append(c.substring(c.length()-2)).append(':');
// And the second...
conv.setLength(0);
conv.append("00").append(cal.get(Calendar.SECOND));
c = conv.toString();
rc.append(c.substring(c.length()-2)).append("</dateTime.iso8601>");
// This is the resulting date/time value.
return rc.toString();
} // end serializeCalendar
public final String serializeCollection(Collection coll) throws XmlRpcFault
{
return serializeIterator(coll.iterator());
} // end serializeCollection
public final String serializeDate(java.util.Date date) throws XmlRpcFault
{
GregorianCalendar cal = new GregorianCalendar(utc);
cal.setTime(date);
return serializeCalendar(cal);
} // end serializeDate
public final String serializeEnumeration(Enumeration enum) throws XmlRpcFault
{
StringBuffer b = new StringBuffer("<array>\r\n<data>\r\n");
while (enum.hasMoreElements())
b.append("<value>").append(this.serialize(enum.nextElement())).append("</value>");
b.append("</data>\r\n</array>");
return b.toString();
} // end serializeIterator
public final String serializeInputStream(InputStream stm) throws XmlRpcFault
{
String rc = null;
try
{ // convert the data bytes to encoded bytes first
ByteArrayOutputStream internal_stm = new ByteArrayOutputStream();
OutputStream encode_stm = MimeUtility.encode(internal_stm,"base64");
IOUtil.copy(stm,encode_stm);
encode_stm.flush();
// turn our encoded output into an InputStream
ByteArrayInputStream internal2_stm = new ByteArrayInputStream(internal_stm.toByteArray());
IOUtil.shutdown(encode_stm);
IOUtil.shutdown(internal_stm);
// build a StringWriter containing the converted contents
StringWriter wr = new StringWriter();
wr.write("<base64>");
InputStreamReader rd = new InputStreamReader(internal2_stm,"US-ASCII");
IOUtil.copy(rd,wr);
IOUtil.shutdown(rd);
IOUtil.shutdown(internal2_stm);
wr.write("</base64>");
// send out the complete string
rc = wr.toString();
IOUtil.shutdown(wr);
} // end try
catch (IOException ioe)
{ // this is an internal error
throw new XmlRpcFault(XmlRpcFault.INTERNAL_ERROR,"unable to save binary data");
} // end catch
catch (MessagingException me)
{ // and so is this
throw new XmlRpcFault(XmlRpcFault.INTERNAL_ERROR,"unable to encode binary data");
} // end catch
return rc;
} // end serializeInputStream
public final String serializeIterator(Iterator it) throws XmlRpcFault
{
StringBuffer b = new StringBuffer("<array>\r\n<data>\r\n");
while (it.hasNext())
b.append("<value>").append(this.serialize(it.next())).append("</value>\r\n");
b.append("</data>\r\n</array>");
return b.toString();
} // end serializeIterator
public final String serializeMap(Map m) throws XmlRpcFault
{
StringBuffer b = new StringBuffer("<struct>\r\n");
Iterator it = m.entrySet().iterator();
while (it.hasNext())
{ // serialize each entry in turn
Map.Entry ntry = (Map.Entry)(it.next());
b.append("<member>\r\n<name>").append(StringUtil.encodeHTML(ntry.getKey().toString()));
b.append("</name>\r\n<value>").append(this.serialize(ntry.getValue()));
b.append("</value>\r\n</member>\r\n");
} // end while
b.append("</struct>\r\n");
return b.toString();
} // end serializeMap
public final String serializeString(String s)
{
if (s==null)
return "<string></string>";
return "<string>" + StringUtil.encodeHTML(s) + "</string>";
} // end serializeString
/*--------------------------------------------------------------------------------
* External static operations
*--------------------------------------------------------------------------------
*/
public static final synchronized XmlRpcSerializer get()
{
if (self==null)
self = new XmlRpcSerializer();
return self;
} // end get
} // end class XmlRpcSerializer