/**
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved
*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* https://opensso.dev.java.net/public/CDDLv1.0.html or
* opensso/legal/CDDLv1.0.txt
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at opensso/legal/CDDLv1.0.txt.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* $Id: SOAPClient.java,v 1.9 2008/09/03 07:06:30 lakshman_abburi Exp $
*
*/
/**
* Portions Copyrighted 2011-2012 ForgeRock Inc
*/
package com.sun.identity.jaxrpc;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.rmi.RemoteException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.TreeSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.Attributes;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import com.iplanet.am.sdk.remote.AMRemoteException;
import com.iplanet.am.util.SystemProperties;
import com.iplanet.sso.SSOException;
import com.sun.identity.common.HttpURLConnectionManager;
import com.sun.identity.entity.EntityException;
import com.sun.identity.idm.IdRepoException;
import com.sun.identity.shared.Constants;
import com.sun.identity.shared.datastruct.OrderedSet;
import com.sun.identity.shared.debug.Debug;
import com.sun.identity.shared.encode.Base64;
import com.sun.identity.shared.xml.XMLUtils;
import com.sun.identity.sm.SMSException;
import com.sun.identity.sm.SMSSchema;
import javax.xml.parsers.SAXParser;
/**
* The class SOAPClient
provides methods for SOAP and JAXRPC
* client to send and receive messages. The method call(..)
will
* be used by SOAP client to send SOAP messages, and JAXRPC clients will use
* encodeMessage
and send
to send JAXRPC requests.
* The method encodeMessage(String functionName,
* Object[] args)
,
* encodes the JAXRPC data in SOAP, which can then be sent using the send(
* String message, String cookies)
.
*
* The SOAPClient
can be initialized either with known SOAP
* endpoint URLs or it will find an active server using Naming service. In the
* case of JAXRPC, the SOAP response is decoded and returns a java
* Object
; else an exception is thrown.
*
* @deprecated As of OpenSSO version 8.0
* {@link com.sun.identity.shared.jaxrpc.SOAPClient}
*/
public class SOAPClient {
// Debug file
static final Debug debug = Debug.getInstance("amJAXRPC");
// Instance variables
String serviceName;
String url;
// Variables for direct URLs
String urls[];
// Variables for managing exceptions
String exceptionClassName, exceptionMessage, exceptionCode,
smsExceptionCode;
int ldapErrorCode;
String resourceBundleName, errorString;
Set messageArgs;
Exception exception;
boolean isException;
private static boolean useCache = Boolean.getBoolean(
SystemProperties.get(Constants.URL_CONNECTION_USE_CACHE, "false"));
/**
* Constructor for applications that would like to dynamically set the SOAP
* endponts using
*
setUrls(String[] urls)
before
* invoking either send()
or
* call()
.
*/
public SOAPClient() throws IOException {
// do nothing
}
/**
* Constructor for services that use JAXRPC as their communication protocol.
* The URL end points for these services will be obtained from Naming
* service for jaxrpc service, and the service name will appended to it as
* the JAXRPC interface name.
*/
public SOAPClient(String serviceName) {
this.serviceName = serviceName;
}
/**
* Constructor for applications that have the list of end point URLs. The
* SOAPClient
will iterate through the URLs in case of server
* failure.
*/
public SOAPClient(String urls[]) {
this.urls = urls;
}
/**
* Performs a raw SOAP call with "message" as the SOAP data and response is
* returned as StringBuffer
*/
public InputStream call(String message, String cookies) throws Exception {
if (debug.messageEnabled()) {
debug.message("SOAP Client: Message being sent:" + message);
}
// Setup the connection, support for failover
InputStream in_buf = null;
boolean done = false;
int urlIndex = 0;
while (!done) {
// Check for a valid URL, if not find one
if (url == null) {
// Check if URLs are provided at the time of
// constructor, else get it from JAXRPCUtils
if (urls != null) {
if (urlIndex >= urls.length) {
// All the URLs have been checked
// throw RemoteException
if (debug.warningEnabled()) {
debug.warning("SOAPClient: No vaild server found");
}
throw (new RemoteException("no-server-found"));
}
url = urls[urlIndex++];
} else {
// This function throws RemoteException
// if no servers are found
url = JAXRPCUtil.getValidURL(serviceName);
}
}
URL endpoint = new URL(url);
HttpURLConnection connection =
HttpURLConnectionManager.getConnection(endpoint);
connection.setDoOutput(true);
connection.setUseCaches(useCache);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type",
"text/xml; charset=\"utf-8\"");
connection.setRequestProperty("SOAPAction", "\"\"");
if (cookies != null) {
connection.setRequestProperty("Cookie", cookies);
}
String userInfo = endpoint.getUserInfo();
if (userInfo != null) {
connection.setRequestProperty("Authorization", "Basic "
+ Base64.encode(userInfo.getBytes("UTF-8")));
}
// Output
byte[] data = message.getBytes("UTF-8");
int requestLength = data.length;
connection.setRequestProperty("Content-Length", Integer
.toString(requestLength));
OutputStream out = null;
try {
out = connection.getOutputStream();
} catch (ConnectException ce) {
// Debug the exception
if (debug.warningEnabled()) {
debug.warning("SOAP Client: Connection Exception: " + url,
ce);
}
// Server may be down, try the next server
JAXRPCUtil.serverFailed(url);
url = null;
continue;
}
// Write out the message
out.write(data);
out.flush();
// Get the response
try {
in_buf = connection.getInputStream();
} catch (IOException ioe) {
// Could be receiving SOAP fault
// Debug the exception
if (debug.messageEnabled()) {
debug.message("SOAP Client: READ Exception", ioe);
}
in_buf = connection.getErrorStream();
isException = true; // Used by send(...)
} finally {
done = true;
}
}
// Debug the input/output messages
if (debug.messageEnabled()) {
StringBuffer inbuf = new StringBuffer();
String line;
BufferedReader reader = new BufferedReader(new InputStreamReader(
in_buf, "UTF-8"));
while ((line = reader.readLine()) != null) {
inbuf.append(line).append("\n");
}
String data = new String(inbuf);
debug.message("SOAP Client: Input: " + message + "\nOutput: "
+ data);
in_buf = new ByteArrayInputStream(data.getBytes("UTF-8"));
}
return (in_buf);
}
/**
* Performs a JAXRPC method call. The parameter
* functionName
* is the JAXRPC function to be called with parameters params
.
* Returns an object on success, else throws an Exception
*
.
*/
public synchronized Object send(String functionName, Object params[],
String cookies) throws Exception {
return (send(encodeMessage(functionName, params), cookies));
}
/**
* Performs a JAXRPC method call. The parameter
* functionName
* is the JAXRPC function to be called with parameter param
.
* Returns an object on success, else throws an Exception
*
.
*/
public synchronized Object send(String functionName, Object param,
String cookies) throws Exception {
return (send(encodeMessage(functionName, param), cookies));
}
/**
* Performs a JAXRPC method call. The parameter
* message
* contains SOAP encoded function call obtained from
* encodeMessage
. Returns an object on success, else throws
* an Exception
*
.
*/
public synchronized Object send(String message, String cookies)
throws Exception {
// Initialize variables
exceptionClassName = exceptionMessage = null;
resourceBundleName = exceptionCode = null;
smsExceptionCode = null;
messageArgs = null;
errorString = null;
ldapErrorCode = 0;
isException = false;
// Send the SOAP request and get the response
InputStream in_buf = call(message, cookies);
// Decode the output. Parse the document using SAX
SOAPContentHandler handler = new SOAPContentHandler();
try {
SAXParser saxParser;
if (debug.warningEnabled()) {
saxParser = XMLUtils.getSafeSAXParser(true);
} else {
saxParser = XMLUtils.getSafeSAXParser(false);
}
XMLReader parser = saxParser.getXMLReader();
parser.setContentHandler(handler);
parser.setErrorHandler(new SOAPErrorHandler());
parser.parse(new InputSource(in_buf));
} catch (ParserConfigurationException pce) {
if (debug.warningEnabled()) {
debug.warning("SOAPClient:send parser config exception", pce);
}
} catch (SAXException saxe) {
if (debug.warningEnabled()) {
debug.warning("SOAPClient:send SAX exception", saxe);
}
}
// Check for exceptions
if (isException) {
throw (exception);
}
return (handler.getObject());
}
public void setURL(String url) {
this.url = url;
}
void setURLs(String[] urls) {
this.urls = urls;
}
String encodeString(String str) {
return (encodeString("String_1", str));
}
String encodeInt(String name, Integer i) {
StringBuilder sb = new StringBuilder(100);
sb.append("<").append(name);
sb.append(" xsi:type=\"xsd:int\">");
sb.append(i).append("").append(name).append(">");
return (sb.toString());
}
String encodeInt(Integer i) {
return (encodeInt("int_1", i));
}
String encodeBoolean(String name, Boolean b) {
StringBuilder sb = new StringBuilder(100);
sb.append("<").append(name);
sb.append(" xsi:type=\"xsd:boolean\">");
sb.append(b).append("").append(name).append(">");
return (sb.toString());
}
String encodeBoolean(Boolean b) {
return (encodeBoolean("boolean_1", b));
}
String encodeString(String name, String str) {
StringBuilder sb = new StringBuilder(100);
sb.append("<").append(name);
sb.append(" xsi:type=\"xsd:string\">");
// Make string to be XML compliant
String data = SMSSchema.escapeSpecialCharacters(str);
sb.append(data).append("").append(name).append(">");
return (sb.toString());
}
String encodeSet(Set set) {
return (encodeSet("Set_1", set));
}
String encodeSet(String name, Set set) {
StringBuilder sb = new StringBuilder(200);
sb.append("<").append(name);
sb.append(" xsi:type=\"ns1:hashSet\" enc:arrayType=\"xsd:anyType[");
sb.append(set.size());
sb.append("]\">");
for (Iterator items = set.iterator(); items.hasNext();) {
sb.append(encodeString("item", items.next().toString()));
}
sb.append("").append(name).append(">");
return (sb.toString());
}
String encodeList(List list) {
return (encodeList("List_1", list));
}
String encodeList(String name, List list) {
StringBuilder sb = new StringBuilder(200);
sb.append("<").append(name);
sb.append(" xsi:type=\"ns1:linkedList\" enc:arrayType=\"xsd:anyType[");
sb.append(list.size());
sb.append("]\">");
for (Iterator items = list.iterator(); items.hasNext();) {
sb.append(encodeString("item", items.next().toString()));
}
sb.append("").append(name).append(">");
return (sb.toString());
}
public String encodeMap(Map map) {
return (encodeMap("Map_1", map));
}
String encodeByteArrayArray(String name, byte[][] data) {
return (null);
}
public String encodeMap(String name, Map map) {
StringBuilder sb = new StringBuilder(200);
sb.append("<").append(name);
sb.append(" xsi:type=\"ns1:hashMap\" enc:arrayType=\"ns1:mapEntry[");
sb.append(map.size()).append("]\">");
for (Iterator items = map.entrySet().iterator(); items.hasNext();) {
Map.Entry entry = (Map.Entry) items.next();
sb.append("function
that takes the parameter param
* as the only argument.
*/
public String encodeMessage(String function, Object param) {
Object params[] = null;
if (param != null) {
params = new Object[1];
params[0] = param;
}
return (encodeMessage(function, params));
}
/**
* Returns a SOAP request compliant with JAXRPC for the provide function
* name function
that takes the parameters
* params
as its arguments.
*/
public synchronized String encodeMessage(String function, Object[] params) {
int index = 1;
StringBuilder sb = new StringBuilder(1000);
sb.append(ENVELOPE).append(HEADSTART).append(HEADEND).append(ENV_BODY);
sb.append("