// Copyright 2000-2005 the Contributors, as shown in the revision logs. // Licensed under the Apache Public Source License 2.0 ("the License"). // You may not use this file except in compliance with the License. package org.ibex.js; import java.io.*; import org.ibex.util.*; /** * A partial RPC-style SOAP 1.1 client. Implemented from the SOAP 1.1 * Spec and Dave Winer's "SOAP for Busy Developers". This class * extends XMLRPC in order to share some networking logic. * * Currently unsupported features/hacks: * */ public class SOAP extends XMLRPC { /** the desired content of the SOAPAction header */ String action = null; /** the namespace to use */ String nameSpace = null; /** When you get a property from an SOAP, it just returns another SOAP with the property name tacked onto methodname. */ public Object get(Object name) { return new SOAP(url, (method.equals("") ? "" : method + ".") + name.toString(), this, action, nameSpace); } // Methods to Recieve and parse SOAP Responses //////////////////////////////////////////////////// public void startElement(String name, String[] keys, Object[] vals, int line, int col) { content.reset(); if (name.equals("SOAP-ENV:Envelope")) return; if (name.equals("SOAP-ENV:Body")) return; if (name.equals("SOAP-ENV:Fault")) fault = true; // add a generic struct; we'll change this if our type is different objects.push(new JS.Obj()); for(int i=0; i 0 && content.toString().trim().length() > 0) { // remove ourselves Object me = objects.get(objects.size() - 1); if (fault || me instanceof String) { objects.pop(); objects.push(new String(content.buf, 0, content.size()).intern()); content.reset(); } else if (me instanceof byte[]) { objects.pop(); objects.push(new Fountain.ByteArray(Encode.fromBase64(new String(content.buf, 0, content.size())), null)); content.reset(); } else if (me instanceof Integer) { objects.pop(); objects.push(new Integer(new String(content.buf, 0, content.size()))); content.reset(); } else if (me instanceof Boolean) { objects.pop(); String s = new String(content.buf, 0, content.size()).trim(); if (s.equals("1") || s.equals("true")) objects.push(Boolean.TRUE); else objects.push(Boolean.FALSE); content.reset(); } else if (me instanceof Double) { objects.pop(); objects.push(new Double(new String(content.buf, 0, content.size()))); content.reset(); } else { // okay, we got PCDATA for what is supposedly a // struct... somebody's not adding their type info... String s = new String(content.buf, 0, content.size()).trim(); boolean hasdot = false; for(int i=0; i 1 ? (JS)objects.get(objects.size() - 2) : (JS)null; // we want to fold stuff back into the fault object if (objects.size() < 2) return; // our parent "should" be an aggregate type -- add ourselves to it. if (parent != null && parent instanceof JSArray) { objects.pop(); ((JSArray)parent).push(me); } else if (parent != null && parent instanceof JS) { objects.pop(); try { ((JS)parent).put(JSU.S(name), me); } catch (JSExn e) { throw new Error("this should never happen"); } } } /** Appends the SOAP representation of o to sb */ void appendObject(String name, Object o, StringBuffer sb) throws JSExn { if (o instanceof Number) { if ((double)((Number)o).intValue() == ((Number)o).doubleValue()) { sb.append(" <" + name + " xsi:type=\"xsd:int\">"); sb.append(((Number)o).intValue()); sb.append("\r\n"); } else { sb.append(" <" + name + " xsi:type=\"xsd:double\">"); sb.append(o); sb.append("\r\n"); } } else if (o instanceof Boolean) { sb.append(" <" + name + " xsi:type=\"xsd:boolean\">"); sb.append(((Boolean)o).booleanValue() ? "true" : "false"); sb.append("\r\n"); } else if (o instanceof Fountain) { try { sb.append(" <" + name + " xsi:type=\"SOAP-ENC:base64\">\r\n"); InputStream is = ((Fountain)o).getInputStream(); byte[] buf = new byte[54]; while(true) { int numread = is.read(buf, 0, 54); if (numread == -1) break; byte[] writebuf = buf; if (numread < buf.length) { writebuf = new byte[numread]; System.arraycopy(buf, 0, writebuf, 0, numread); } sb.append(" "); sb.append(new String(Encode.toBase64(writebuf))); sb.append("\r\n"); } sb.append(((Boolean)o).booleanValue() ? "1" : "0"); sb.append("\r\n"); } catch (IOException e) { if (Log.on) Log.info(this, "caught IOException while attempting to send a Fountain via SOAP"); if (Log.on) Log.info(this, e); throw new JSExn("caught IOException while attempting to send a Fountain via SOAP"); } } else if (o instanceof String) { sb.append(" <" + name + " xsi:type=\"xsd:string\">"); String s = (String)o; if (s.indexOf('<') == -1 && s.indexOf('&') == -1) { sb.append(s); } else { char[] cbuf = s.toCharArray(); while(true) { int oldi = 0, i=0; while(i < cbuf.length && cbuf[i] != '<' && cbuf[i] != '&') i++; sb.append(cbuf, oldi, i); if (i == cbuf.length) break; if (cbuf[i] == '<') sb.append("<"); else if (cbuf[i] == '&') sb.append("&"); i = oldi = i + 1; } } sb.append("\r\n"); } else if (o instanceof JSArray) { JSArray a = (JSArray)o; sb.append(" <" + name + " SOAP-ENC:arrayType=\"xsd:ur-type[" + a.size() + "]\">"); for(int i=0; i\r\n"); } else if (o instanceof JS) { JS j = (JS)o; sb.append(" <" + name + ">"); JS.Enumeration e = j.keys(); while(e.hasNext()) { Object key = e.next(); appendObject((String)key, j.get((JS)key), sb); } sb.append("\r\n"); } } protected String buildRequest(JSArray args) throws JSExn, IOException { // build up the request StringBuffer content = new StringBuffer(); content.append("SOAPAction: " + action + "\r\n\r\n"); content.append("\r\n"); content.append("\r\n"); content.append("\r\n"); content.append(" <"); content.append(method); content.append(nameSpace != null ? " xmlns=\"" + nameSpace + "\"" : ""); content.append(">\r\n"); if (args.size() > 0) { JS.Enumeration e = ((JS)args.get(0)).keys(); while(e.hasNext()) { JS key = (JS)e.next(); appendObject(((JSString)key).coerceToString(), ((JS)args.get(0)).get(key), content); } } content.append(" \r\n"); return content.toString(); } public SOAP(String url, String methodname, String action, String nameSpace) { super(url, methodname); this.action = action; this.nameSpace = nameSpace; } public SOAP(String url, String methodname, SOAP httpSource, String action, String nameSpace) { super(url, methodname, httpSource); this.action = action; this.nameSpace = nameSpace; } }