/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001-2004 Caucho Technology, Inc.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Caucho Technology (http://www.caucho.com/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "Burlap", "Resin", and "Caucho" must not be used to
 *    endorse or promote products derived from this software without prior
 *    written permission. For written permission, please contact
 *    info@caucho.com.
 *
 * 5. Products derived from this software may not be called "Resin"
 *    nor may "Resin" appear in their names without prior written
 *    permission of Caucho Technology.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author Scott Ferguson
 */

package com.caucho.burlap.client;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.TimeZone;
import java.util.Vector;

Output stream for Burlap requests, compatible with microedition Java. It only uses classes and types available to J2ME. In particular, it does not have any support for the <double> type.

MicroBurlapOutput does not depend on any classes other than in J2ME, so it can be extracted independently into a smaller package.

MicroBurlapOutput is unbuffered, so any client needs to provide its own buffering.

OutputStream os = ...; // from http connection
MicroBurlapOutput out = new MicroBurlapOutput(os);
String value;
out.startCall("hello");  // start hello call
out.writeString("arg1"); // write a string argument
out.completeCall();      // complete the call
/** * Output stream for Burlap requests, compatible with microedition * Java. It only uses classes and types available to J2ME. In * particular, it does not have any support for the &lt;double> type. * * <p>MicroBurlapOutput does not depend on any classes other than * in J2ME, so it can be extracted independently into a smaller package. * * <p>MicroBurlapOutput is unbuffered, so any client needs to provide * its own buffering. * * <pre> * OutputStream os = ...; // from http connection * MicroBurlapOutput out = new MicroBurlapOutput(os); * String value; * * out.startCall("hello"); // start hello call * out.writeString("arg1"); // write a string argument * out.completeCall(); // complete the call * </pre> */
public class MicroBurlapOutput { private OutputStream os; private Date date; private Calendar utcCalendar; private Calendar localCalendar;
Creates a new Burlap output stream, initialized with an underlying output stream.
Params:
  • os – the underlying output stream.
/** * Creates a new Burlap output stream, initialized with an * underlying output stream. * * @param os the underlying output stream. */
public MicroBurlapOutput(OutputStream os) { init(os); }
Creates an uninitialized Burlap output stream.
/** * Creates an uninitialized Burlap output stream. */
public MicroBurlapOutput() { } public void init(OutputStream os) { this.os = os; }
Writes a complete method call.
/** * Writes a complete method call. */
public void call(String method, Object []args) throws IOException { startCall(method); if (args != null) { for (int i = 0; i < args.length; i++) writeObject(args[i]); } completeCall(); }
Writes the method call:
<burlap:request>
  <method>add</method>
Params:
  • method – the method name to call.
/** * Writes the method call: * * <code><pre> * &lt;burlap:request> * &lt;method>add&lt;/method> * </pre></code> * * @param method the method name to call. */
public void startCall(String method) throws IOException { print("<burlap:call><method>"); print(method); print("</method>"); }
Writes the method call:
</burlap:request>
/** * Writes the method call: * * <code><pre> * &lt;/burlap:request> * </pre></code> */
public void completeCall() throws IOException { print("</burlap:call>"); }
Writes a boolean value to the stream. The boolean will be written with the following syntax:
<boolean>1</boolean>
Params:
  • value – the boolean value to write.
/** * Writes a boolean value to the stream. The boolean will be written * with the following syntax: * * <code><pre> * &lt;boolean>1&lt;/boolean> * </pre></code> * * @param value the boolean value to write. */
public void writeBoolean(boolean value) throws IOException { print("<boolean>"); printInt(value ? 1 : 0); print("</boolean>"); }
Writes an integer value to the stream. The integer will be written with the following syntax:
<int>123</int>
Params:
  • value – the integer value to write.
/** * Writes an integer value to the stream. The integer will be written * with the following syntax: * * <code><pre> * &lt;int>123&lt;/int> * </pre></code> * * @param value the integer value to write. */
public void writeInt(int value) throws IOException { print("<int>"); printInt(value); print("</int>"); }
Writes a long value to the stream. The long will be written with the following syntax:
<long>123</long>
Params:
  • value – the long value to write.
/** * Writes a long value to the stream. The long will be written * with the following syntax: * * <code><pre> * &lt;long>123&lt;/long> * </pre></code> * * @param value the long value to write. */
public void writeLong(long value) throws IOException { print("<long>"); printLong(value); print("</long>"); }
Writes a null value to the stream. The null will be written with the following syntax
<null></null>
Params:
  • value – the string value to write.
/** * Writes a null value to the stream. * The null will be written with the following syntax * * <code><pre> * &lt;null>&lt;/null> * </pre></code> * * @param value the string value to write. */
public void writeNull() throws IOException { print("<null></null>"); }
Writes a string value to the stream using UTF-8 encoding. The string will be written with the following syntax:
<string>12.3e10</string>
If the value is null, it will be written as
<null></null>
Params:
  • value – the string value to write.
/** * Writes a string value to the stream using UTF-8 encoding. * The string will be written with the following syntax: * * <code><pre> * &lt;string>12.3e10&lt;/string> * </pre></code> * * If the value is null, it will be written as * * <code><pre> * &lt;null>&lt;/null> * </pre></code> * * @param value the string value to write. */
public void writeString(String value) throws IOException { if (value == null) { print("<null></null>"); } else { print("<string>"); printString(value); print("</string>"); } }
Writes a byte array to the stream using base64 encoding. The array will be written with the following syntax:
<base64>dJmO==</base64>
If the value is null, it will be written as
<null></null>
Params:
  • value – the string value to write.
/** * Writes a byte array to the stream using base64 encoding. * The array will be written with the following syntax: * * <code><pre> * &lt;base64>dJmO==&lt;/base64> * </pre></code> * * If the value is null, it will be written as * * <code><pre> * &lt;null>&lt;/null> * </pre></code> * * @param value the string value to write. */
public void writeBytes(byte []buffer, int offset, int length) throws IOException { if (buffer == null) { print("<null></null>"); } else { print("<base64>"); printBytes(buffer, offset, length); print("</base64>"); } }
Writes a date to the stream using ISO8609.
<date>19980508T095131Z</date>
Params:
  • value – the date in milliseconds from the epoch in UTC
/** * Writes a date to the stream using ISO8609. * * <code><pre> * &lt;date>19980508T095131Z&lt;/date> * </pre></code> * * @param value the date in milliseconds from the epoch in UTC */
public void writeUTCDate(long time) throws IOException { print("<date>"); if (utcCalendar == null) { utcCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); date = new Date(); } date.setTime(time); utcCalendar.setTime(date); printDate(utcCalendar); print("</date>"); }
Writes a date to the stream using ISO8609.
<date>19980508T095131Z</date>
Params:
  • value – the date in milliseconds from the epoch in local timezone
/** * Writes a date to the stream using ISO8609. * * <code><pre> * &lt;date>19980508T095131Z&lt;/date> * </pre></code> * * @param value the date in milliseconds from the epoch in local timezone */
public void writeLocalDate(long time) throws IOException { print("<date>"); if (localCalendar == null) { localCalendar = Calendar.getInstance(); date = new Date(); } date.setTime(time); localCalendar.setTime(date); printDate(localCalendar); print("</date>"); }
Writes a reference.
<ref>123</ref>
Params:
  • value – the integer value to write.
/** * Writes a reference. * * <code><pre> * &lt;ref>123&lt;/ref> * </pre></code> * * @param value the integer value to write. */
public void writeRef(int value) throws IOException { print("<ref>"); printInt(value); print("</ref>"); }
Writes a generic object. writeObject understands the following types:
  • null
  • java.lang.String
  • java.lang.Boolean
  • java.lang.Integer
  • java.lang.Long
  • java.util.Date
  • byte[]
  • java.util.Vector
  • java.util.Hashtable
Unknown objects will call writeCustomObject.
/** * Writes a generic object. writeObject understands the following types: * * <ul> * <li>null * <li>java.lang.String * <li>java.lang.Boolean * <li>java.lang.Integer * <li>java.lang.Long * <li>java.util.Date * <li>byte[] * <li>java.util.Vector * <li>java.util.Hashtable * </ul> * * Unknown objects will call <code>writeCustomObject</code>. */
public void writeObject(Object object) throws IOException { if (object == null) writeNull(); else if (object instanceof String) writeString((String) object); else if (object instanceof Boolean) writeBoolean(((Boolean) object).booleanValue()); else if (object instanceof Integer) writeInt(((Integer) object).intValue()); else if (object instanceof Long) writeLong(((Long) object).longValue()); else if (object instanceof Date) writeUTCDate(((Date) object).getTime()); else if (object instanceof byte[]) { byte []data = (byte []) object; writeBytes(data, 0, data.length); } else if (object instanceof Vector) { Vector vector = (Vector) object; int size = vector.size(); writeListBegin(size, null); for (int i = 0; i < size; i++) writeObject(vector.elementAt(i)); writeListEnd(); } else if (object instanceof Hashtable) { Hashtable hashtable = (Hashtable) object; writeMapBegin(null); Enumeration e = hashtable.keys(); while (e.hasMoreElements()) { Object key = e.nextElement(); Object value = hashtable.get(key); writeObject(key); writeObject(value); } writeMapEnd(); } else writeCustomObject(object); }
Applications which override this can do custom serialization.
Params:
  • object – the object to write.
/** * Applications which override this can do custom serialization. * * @param object the object to write. */
public void writeCustomObject(Object object) throws IOException { throw new IOException("unexpected object: " + object); }
Writes the list header to the stream. List writers will call writeListBegin followed by the list contents and then call writeListEnd.
<list>
  <type>java.util.ArrayList</type>
  <length>3</length>
  <int>1</int>
  <int>2</int>
  <int>3</int>
</list>
/** * Writes the list header to the stream. List writers will call * <code>writeListBegin</code> followed by the list contents and then * call <code>writeListEnd</code>. * * <code><pre> * &lt;list> * &lt;type>java.util.ArrayList&lt;/type> * &lt;length>3&lt;/length> * &lt;int>1&lt;/int> * &lt;int>2&lt;/int> * &lt;int>3&lt;/int> * &lt;/list> * </pre></code> */
public void writeListBegin(int length, String type) throws IOException { print("<list><type>"); if (type != null) print(type); print("</type><length>"); printInt(length); print("</length>"); }
Writes the tail of the list to the stream.
/** * Writes the tail of the list to the stream. */
public void writeListEnd() throws IOException { print("</list>"); }
Writes the map header to the stream. Map writers will call writeMapBegin followed by the map contents and then call writeMapEnd.
<map>
  <type>java.util.Hashtable</type>
  <string>a</string;<int>1</int>
  <string>b</string;<int>2</int>
  <string>c</string;<int>3</int>
</map>
/** * Writes the map header to the stream. Map writers will call * <code>writeMapBegin</code> followed by the map contents and then * call <code>writeMapEnd</code>. * * <code><pre> * &lt;map> * &lt;type>java.util.Hashtable&lt;/type> * &lt;string>a&lt;/string;&lt;int>1&lt;/int> * &lt;string>b&lt;/string;&lt;int>2&lt;/int> * &lt;string>c&lt;/string;&lt;int>3&lt;/int> * &lt;/map> * </pre></code> */
public void writeMapBegin(String type) throws IOException { print("<map><type>"); if (type != null) print(type); print("</type>"); }
Writes the tail of the map to the stream.
/** * Writes the tail of the map to the stream. */
public void writeMapEnd() throws IOException { print("</map>"); }
Writes a remote object reference to the stream. The type is the type of the remote interface.
<remote>
  <type>test.account.Account</type>
  <string>http://caucho.com/foo;ejbid=bar</string>
</remote>
/** * Writes a remote object reference to the stream. The type is the * type of the remote interface. * * <code><pre> * &lt;remote> * &lt;type>test.account.Account&lt;/type> * &lt;string>http://caucho.com/foo;ejbid=bar&lt;/string> * &lt;/remote> * </pre></code> */
public void writeRemote(String type, String url) throws IOException { print("<remote><type>"); if (type != null) print(type); print("</type><string>"); print(url); print("</string></remote>"); }
Prints an integer to the stream.
Params:
  • v – the integer to print.
/** * Prints an integer to the stream. * * @param v the integer to print. */
public void printInt(int v) throws IOException { print(String.valueOf(v)); }
Prints a long to the stream.
Params:
  • v – the long to print.
/** * Prints a long to the stream. * * @param v the long to print. */
public void printLong(long v) throws IOException { print(String.valueOf(v)); }
Prints a string to the stream, properly encoded.
Params:
  • v – the string to print.
/** * Prints a string to the stream, properly encoded. * * @param v the string to print. */
public void printString(String v) throws IOException { int len = v.length(); for (int i = 0; i < len; i++) { char ch = v.charAt(i); switch (ch) { case '<': print("&lt;"); break; case '&': print("&amp;"); break; case '\r': print("&#13;"); break; default: if (ch < 0x80) os.write(ch); else if (ch < 0x800) { os.write(0xc0 + ((ch >> 6) & 0x1f)); os.write(0x80 + (ch & 0x3f)); } else { os.write(0xe0 + ((ch >> 12) & 0xf)); os.write(0x80 + ((ch >> 6) & 0x3f)); os.write(0x80 + (ch & 0x3f)); } break; } } }
Prints a byte array to the stream, properly encoded in base64.
Params:
  • data – the bytes to print.
/** * Prints a byte array to the stream, properly encoded in base64. * * @param data the bytes to print. */
public void printBytes(byte []data, int offset, int length) throws IOException { int i; for (; length >= 3; length -= 3) { int chunk = (((data[offset] & 0xff) << 16) + ((data[offset + 1] & 0xff) << 8) + (data[offset + 2] & 0xff)); os.write(base64encode(chunk >> 18)); os.write(base64encode(chunk >> 12)); os.write(base64encode(chunk >> 6)); os.write(base64encode(chunk)); offset += 3; } if (length == 2) { int chunk = ((data[offset] & 0xff) << 8) + (data[offset + 1] & 0xff); os.write(base64encode(chunk >> 12)); os.write(base64encode(chunk >> 6)); os.write(base64encode(chunk)); os.write('='); } else if (length == 1) { int chunk = data[offset] & 0xff; os.write(base64encode(chunk >> 6)); os.write(base64encode(chunk)); os.write('='); os.write('='); } }
Converts the digit to its base64 encoding.
/** * Converts the digit to its base64 encoding. */
public static char base64encode(int d) { d &= 0x3f; if (d < 26) return (char) (d + 'A'); else if (d < 52) return (char) (d + 'a' - 26); else if (d < 62) return (char) (d + '0' - 52); else if (d == 62) return '+'; else return '/'; }
Prints a date.
Params:
  • date – the date to print.
/** * Prints a date. * * @param date the date to print. */
public void printDate(Calendar calendar) throws IOException { int year = calendar.get(Calendar.YEAR); os.write((char) ('0' + (year / 1000 % 10))); os.write((char) ('0' + (year / 100 % 10))); os.write((char) ('0' + (year / 10 % 10))); os.write((char) ('0' + (year % 10))); int month = calendar.get(Calendar.MONTH) + 1; os.write((char) ('0' + (month / 10 % 10))); os.write((char) ('0' + (month % 10))); int day = calendar.get(Calendar.DAY_OF_MONTH); os.write((char) ('0' + (day / 10 % 10))); os.write((char) ('0' + (day % 10))); os.write('T'); int hour = calendar.get(Calendar.HOUR_OF_DAY); os.write((char) ('0' + (hour / 10 % 10))); os.write((char) ('0' + (hour % 10))); int minute = calendar.get(Calendar.MINUTE); os.write((char) ('0' + (minute / 10 % 10))); os.write((char) ('0' + (minute % 10))); int second = calendar.get(Calendar.SECOND); os.write((char) ('0' + (second / 10 % 10))); os.write((char) ('0' + (second % 10))); os.write('Z'); }
Prints a string as ascii to the stream. Used for tags, etc. that are known to the ascii.
Params:
  • s – the ascii string to print.
/** * Prints a string as ascii to the stream. Used for tags, etc. * that are known to the ascii. * * @param s the ascii string to print. */
public void print(String s) throws IOException { int len = s.length(); for (int i = 0; i < len; i++) { int ch = s.charAt(i); os.write(ch); } } }