Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.xmlrpc.client; 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.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import org.apache.commons.io.IOUtils; import org.apache.xmlrpc.XmlRpcException; import org.apache.xmlrpc.XmlRpcRequest; import org.apache.xmlrpc.common.XmlRpcStreamConfig; import org.apache.xmlrpc.common.XmlRpcStreamRequestConfig; import org.apache.xmlrpc.parser.XmlRpcResponseParser; import org.apache.xmlrpc.serializer.XmlRpcWriter; import org.apache.xmlrpc.util.SAXParsers; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; /** Implementation of a transport class, which is based on an output * stream for sending the request and an input stream for receiving * the response, */ /** * Note: This file was modified, because the xml-parser in the method * readResponse fails with certain invalid XML-characters like the * unicode-characters: cx0, 0x8, etc. * * @Contributor Harold Valdivia-Garcia * @date June 26, 2014 */ public abstract class XmlRpcStreamTransport extends XmlRpcTransportImpl { protected interface ReqWriter { /** * Writes the requests data to the given output stream. * The method ensures, that the target is being closed. */ void write(OutputStream pStream) throws XmlRpcException, IOException, SAXException; } protected class ReqWriterImpl implements ReqWriter { private final XmlRpcRequest request; protected ReqWriterImpl(XmlRpcRequest pRequest) { request = pRequest; } /** Writes the requests uncompressed XML data to the given * output stream. Ensures, that the output stream is being * closed. */ public void write(OutputStream pStream) throws XmlRpcException, IOException, SAXException { final XmlRpcStreamConfig config = (XmlRpcStreamConfig) request.getConfig(); try { ContentHandler h = getClient().getXmlWriterFactory().getXmlWriter(config, pStream); XmlRpcWriter xw = new XmlRpcWriter(config, h, getClient().getTypeFactory()); xw.write(request); pStream.close(); pStream = null; } finally { if (pStream != null) { try { pStream.close(); } catch (Throwable ignore) { } } } } } protected class GzipReqWriter implements ReqWriter { private final ReqWriter reqWriter; protected GzipReqWriter(ReqWriter pReqWriter) { reqWriter = pReqWriter; } public void write(OutputStream pStream) throws XmlRpcException, IOException, SAXException { try { GZIPOutputStream gStream = new GZIPOutputStream(pStream); reqWriter.write(gStream); pStream.close(); pStream = null; } catch (IOException e) { throw new XmlRpcException("Failed to write request: " + e.getMessage(), e); } finally { if (pStream != null) { try { pStream.close(); } catch (Throwable ignore) { } } } } } /** Creates a new instance on behalf of the given client. */ protected XmlRpcStreamTransport(XmlRpcClient pClient) { super(pClient); } /** Closes the connection and ensures, that all resources are being * released. */ protected abstract void close() throws XmlRpcClientException; /** Returns, whether the response is gzip compressed. * @param pConfig The clients configuration. * @return Whether the response stream is gzip compressed. */ protected abstract boolean isResponseGzipCompressed(XmlRpcStreamRequestConfig pConfig); /** Returns the input stream, from which the response is * being read. */ protected abstract InputStream getInputStream() throws XmlRpcException; protected boolean isCompressingRequest(XmlRpcStreamRequestConfig pConfig) { return pConfig.isEnabledForExtensions() && pConfig.isGzipCompressing(); } /** * Creates a new instance of {@link ReqWriter}. * @throws XmlRpcException Creating the instance failed. * @throws IOException Creating the instance failed, because * an {@link IOException} occurs. * @throws SAXException Creating the instance failed, because * the request could not be parsed. */ protected ReqWriter newReqWriter(XmlRpcRequest pRequest) throws XmlRpcException, IOException, SAXException { ReqWriter reqWriter = new ReqWriterImpl(pRequest); if (isCompressingRequest((XmlRpcStreamRequestConfig) pRequest.getConfig())) { reqWriter = new GzipReqWriter(reqWriter); } return reqWriter; } protected abstract void writeRequest(ReqWriter pWriter) throws XmlRpcException, IOException, SAXException; public Object sendRequest(XmlRpcRequest pRequest) throws XmlRpcException { XmlRpcStreamRequestConfig config = (XmlRpcStreamRequestConfig) pRequest.getConfig(); boolean closed = false; try { ReqWriter reqWriter = newReqWriter(pRequest); writeRequest(reqWriter); InputStream istream = getInputStream(); if (isResponseGzipCompressed(config)) { istream = new GZIPInputStream(istream); } Object result = readResponse(config, istream); closed = true; close(); return result; } catch (IOException e) { throw new XmlRpcException("Failed to read server's response: " + e.getMessage(), e); } catch (SAXException e) { Exception ex = e.getException(); if (ex != null && ex instanceof XmlRpcException) { throw (XmlRpcException) ex; } throw new XmlRpcException("Failed to generate request: " + e.getMessage(), e); } finally { if (!closed) { try { close(); } catch (Throwable ignore) { } } } } protected XMLReader newXMLReader() throws XmlRpcException { return SAXParsers.newXMLReader(); } /** * This method was modified, because the xml-parser fails with certain * invalid XML-characters like the unicode-characters: cx0, 0x8, etc. * */ protected Object readResponse(XmlRpcStreamRequestConfig pConfig, InputStream pStream) throws XmlRpcException { InputSource isource = new InputSource(pStream); XMLReader xr = newXMLReader(); XmlRpcResponseParser xp; try { xp = new XmlRpcResponseParser(pConfig, getClient().getTypeFactory()); xr.setContentHandler(xp); // Remove the invalid characters before parsing String pStreamAsStr = convertInputStreamIntoString(pStream); String pStreamAsStrWithValidChars = stripNonValidXMLCharacters(pStreamAsStr); InputStream is = IOUtils.toInputStream(pStreamAsStrWithValidChars, "UTF-8"); isource = new InputSource(is); xr.parse(isource); } catch (SAXException e) { throw new XmlRpcClientException("Failed to parse server's response: " + e.getMessage(), e); } catch (IOException e) { throw new XmlRpcClientException("Failed to read server's response: " + e.getMessage(), e); } if (xp.isSuccess()) { return xp.getResult(); } Throwable t = xp.getErrorCause(); if (t == null) { throw new XmlRpcException(xp.getErrorCode(), xp.getErrorMessage()); } if (t instanceof XmlRpcException) { throw (XmlRpcException) t; } if (t instanceof RuntimeException) { throw (RuntimeException) t; } throw new XmlRpcException(xp.getErrorCode(), xp.getErrorMessage(), t); } /** * This method ensures that the output String has only * valid XML unicode characters as specified by the * XML 1.0 standard. For reference, please see * <a href="http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char">the * standard</a>. This method will return an empty * String if the input is null or empty. * * @param in The String whose non-valid characters we want to remove. * @return The in String, stripped of non-valid characters. */ public static String stripNonValidXMLCharacters(String in) { StringBuffer out = new StringBuffer(); // Used to hold the output. char current; // Used to reference the current character. if (in == null || ("".equals(in))) return ""; // vacancy test. for (int i = 0; i < in.length(); i++) { current = in.charAt(i); // NOTE: No IndexOutOfBoundsException caught here; it should not happen. if ((current == 0x9) || (current == 0xA) || (current == 0xD) || ((current >= 0x20) && (current <= 0xD7FF)) || ((current >= 0xE000) && (current <= 0xFFFD)) || ((current >= 0x10000) && (current <= 0x10FFFF))) out.append(current); } return out.toString(); } public static String convertInputStreamIntoString(InputStream pStream) throws IOException { StringBuilder inputStringBuilder = new StringBuilder(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(pStream, "UTF-8")); String line = bufferedReader.readLine(); while (line != null) { inputStringBuilder.append(line); inputStringBuilder.append('\n'); line = bufferedReader.readLine(); } return inputStringBuilder.toString(); } }