and debugged the handling of parameter serialization so that I think it can actually pass now...
496 lines
15 KiB
Java
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>");
|
|
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
|