aXMLRPC-1.8.1/000077500000000000000000000000001256106573100126755ustar00rootroot00000000000000aXMLRPC-1.8.1/.gitignore000066400000000000000000000000661256106573100146670ustar00rootroot00000000000000nbproject/ build/ dist/ .gradle/ /.nb-gradle/private/ aXMLRPC-1.8.1/LICENSE000066400000000000000000000020521256106573100137010ustar00rootroot00000000000000Copyright (c) 2011-2012 Tim Roes Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. aXMLRPC-1.8.1/README.md000066400000000000000000000262211256106573100141570ustar00rootroot00000000000000What is aXMLRPC? ================ aXMLRPC is a Java library with a leightweight XML-RPC client. XML-RPC is a specification for making remote procedure calls over the HTTP protocol in an XML format. The specificationc can be found under http://www.xmlrpc.com/spec. The library was developed for the use with Android. Since it has no dependencies to any Android library or any other 3rd-party library, it is fully functional in any common java virtual machine (not only on Android). You can control the client with some flags to extend its functionality. See the section about flags. How to include it? ================== How to include the aXMLRPC client into your project? There are four different ways to do that: ### Include the source code You can just include all the source code from the `src` directory into the sourcecode of your project. If you use git yourself, you can use submodules to include the code as a module to yours. So you will always stay up to date with the library. ### Compile it as library aXMLRPC uses gradle, so you can build it using ./gradlew jar ### Use Maven To use it on your Maven project, add it as a dependency on your pom.xml file: ```xml de.timroes aXMLRPC X.Y.Z ``` where X.Y.Z is the current aXMLRPC version ### Download the JAR library You can download a compiled jar file from the below list and use it as a library for your project. [aXMLRPC v1.7.2](https://dl.dropbox.com/u/56970236/aXMLRPC/aXMLRPC_v1.7.2.jar) * Bugfix in double representation * Allow usage of any Iterable for array parameters * Allow usage of BigDecimal as double parameters [aXMLRPC v1.7.1](https://dl.dropbox.com/u/56970236/aXMLRPC/aXMLRPC_v1.7.1.jar) * Bugfix in String escaping (New default behavior: Escape Strings according to spec) * Bugfix in Float encoding [aXMLRPC v1.7.0](https://dl.dropbox.com/u/56970236/aXMLRPC/aXMLRPC_v1.7.0.jar) * Added funtionality for custom TrustManagers [aXMLRPC v1.6.0](https://dl.dropbox.com/u/56970236/aXMLRPC/aXMLRPC_v1.6.0.jar) * Added support for timeout * Minor code improvements [aXMLRPC v1.5.0](https://dl.dropbox.com/u/56970236/aXMLRPC/aXMLRPC_v1.5.0.jar) * Added Proxy Support * Fixed #13, Support for cookie reading [aXMLRPC v1.4.0](https://dl.dropbox.com/u/56970236/aXMLRPC/aXMLRPC_v1.4.0.jar) * Added FLAGS_IGNORE_NAMESPACE, FLAGS_DEFAULT_TYPE_STRING [aXMLRPC v1.3.5](https://dl.dropbox.com/u/56970236/aXMLRPC/aXMLRPC_v1.3.5.jar) * Fixed bug in canceling async calls [aXMLRPC v1.3.4](https://dl.dropbox.com/u/56970236/aXMLRPC/aXMLRPC_v1.3.4.jar) * Added getURL-method * Added FLAGS_IGNORE_SSL_ERRORS * Removed debugging information from JAR [aXMLRPC v1.3.3](https://dl.dropbox.com/u/56970236/aXMLRPC/aXMLRPC_v1.3.3.jar) * Improved handling of 40X http errors [aXMLRPC v1.3.2](https://dl.dropbox.com/u/56970236/aXMLRPC/aXMLRPC_v1.3.2.jar) * Fixed handling of 40x http errors [aXMLRPC v1.3.1](https://dl.dropbox.com/u/56970236/aXMLRPC/aXMLRPC_v1.3.1.jar) * Fixed bug in XML struct creation * Method to clear HTTP basic auth login data [aXMLRPC v1.3.0](https://dl.dropbox.com/u/56970236/aXMLRPC/aXMLRPC_v1.3.0.jar) * Flags to ignore SSL warnings * HTTP forwarding * Improved code quality **Real old versions:** * [aXMLRPC v1.2.0](https://dl.dropbox.com/u/56970236/aXMLRPC/aXMLRPC_v1.2.0.jar) * [aXMLRPC v1.1.0](https://dl.dropbox.com/u/56970236/aXMLRPC/aXMLRPC_v1.1.0.jar) * [aXMLRPC v1.0.3](https://dl.dropbox.com/u/56970236/aXMLRPC/aXMLRPC_v1.0.3.jar) * [aXMLRPC v1.0.2](https://dl.dropbox.com/u/56970236/aXMLRPC/aXMLRPC_v1.0.2.jar) * [aXMLRPC v1.0.1](https://dl.dropbox.com/u/56970236/aXMLRPC/aXMLRPC_v1.0.1.jar) How to use the library? ======================= You can use the library by initiating an `XMLRPCClient` and make calls over it: ```java try { XMLRPCClient client = new XMLRPCClient(new URL("http://example.com/xmlrpc")); Boolean b = (Boolean)client.call("isServerOk"); Integer i = (Integer)client.call("add", 5, 10); } catch(XMLRPCServerException ex) { // The server throw an error. } catch(XMLRPCException ex) { // An error occured in the client. } catch(Exception ex) { // Any other exception } ``` Instead of passing the parameters as seperated values, you can also pack them in an array and pass the array to the method, like in the following example: ```java // ... The try-catch has been ommited for clarity. XMLRPCClient client = new XMLRPCClient(url, "MyUserAgentString"); client.call("someMethod", new Object[]{ o1, o2, o3 }); // ... ``` #### Asynchronous Calls The above method calls are synchronous. So the method `call` will return when the server responded or an error occured. There is also a possibility for asynchronous server calls. You need to implement an XMLRPCCallback that will get noticed about the respone (or error) from the server. The `callAsync` method can be used to make asynchronous calls. It returns an identifier that will also be send to the XMLRPCCallback instance with the response of the server, so your application can make multiple calls concurrently and use one listener for them, that distinguish between the different request by their ids. ```java XMLRPCCallback listener = new XMLRPCCallback() { public void onResponse(long id, Object result) { // Handling the servers response } public void onError(long id, XMLRPCException error) { // Handling any error in the library } public void onServerError(long id, XMLRPCServerException error) { // Handling an error response from the server } }; XMLRPCClient client = new XMLRPCClient(url); long id = client.callAsync(listener, "add", 5, 10); ``` You will be also able to cancel an asynchonous call. Just use the `cancel` method on the `XMLRPCClient` instance, like in the following example. The listener will not be notified, if the call is canceled. ```java XMLRPCClient client = new XMLRPCClient(url); long id = client.callAsync(listener, "method", params); // ... client.cancel(id); ``` The data types -------------- The specification give some data tags for the server response. If you want to work on the type you must cast the returning `Object` from the `call` method to its specific type. Which type to cast which XML server response, tells the following list: `i4`,`int` => `Integer` `boolean` => `Boolean` `string` => `String` `double` => `Double` `dateTime.iso8601` => `Date` `base64` => `byte[]` (`Byte[]` won't work) `array` => `Object[]` `struct` => `Map` `i8` => `Long` (see Flags) Flags ----- The client takes as second parameter (or third if an user agent is given) a combination of multiple flags. It could work like the following example: ```java // ... XMLRPCClient client = new XMLRPCClient(url, XMLRPCClient.FLAGS_STRICT | XMLRPCClient.FLAGS_8BYTE_INT); // ... ``` The following flags are implemented: #### FLAGS_STRICT The client should parse responses strict to specification. It will check if the given content-type is right. The method name in a call must only contain of A-Z, a-z, 0-9, _, ., :, / Normally this is not needed. #### FLAGS_8BYTE_INT The client will be able to handle 8 byte integer values (longs). The xml type tag `` will be used. This is not in the specification but some libraries and servers support this behaviour. If this isn't enabled you cannot recieve 8 byte integers and if you try to send a long, the value must be within the 4 byte integer range. #### FLAGS_ENABLE_COOKIES With this flag, the client will be able to handle cookies, meaning saving cookies from the server and sending it with every other request again. This is needed for some XML-RPC interfaces that support login. #### FLAGS_NIL The client will be able to send `null` values. A `null` value will be send as ``. This extension is described under: http://ontosys.com/xml-rpc/extensions.php #### FLAGS_IGNORE_STATUSCODE With this flag enabled, the XML-RPC client will ignore the HTTP status code of the response from the server. According to specification the status code must be 200. This flag is only needed for the use with not standard compliant servers. #### FLAGS_FORWARD With this flag enabled, the client will forward the request, if the 301 or 302 HTTP status code has been received. If this flag is not set, the client will throw an exception on these HTTP status codes. #### FLAGS_SSL_IGNORE_INVALID_HOST With this flag enabled, the client will ignore, if the URL doesn't match the SSL Certificate. This should be used with caution. Normally the URL should always match the URL in the SSL certificate, even with self signed certificates. #### FLAGS_SSL_INGORE_INVALID_CERT With this flag enabled, the client will ignore all unverified SSL/TLS certificates. This must be used, if you use self-signed certificates or certificated from unknown (or untrusted) authorities. #### FLAGS_DEFAULT_TYPE_STRING With this flag enabled, a value with a missing type tag, will be parsed as a string element. This is just for incoming messages. Outgoing messages will still be generated according to specification. #### FLAGS_IGNORE_NAMESPACES With this flag enabled, the client ignores all namespaces used within the response from the server. #### FLAGS_USE_SYSTEM_PROXY With this flag enabled, the XMLRPCClient will use the system http proxy to connect to the XML-RPC server. #### FLAGS_NO_STRING_ENCODE By default outgoing string values will be encoded according to specification. Meaning the & sign will be encoded to `&` and the "less then" sign to `<`. If you set this flag, the encoding won't be done for outgoing string values. See `FLAGS_NO_STRING_ENCODE` for the counterpart. #### FLAGS_NO_STRING_DECODE This prevents the decoding of incoming strings, meaning `&` and `<` won't be decoded to the & sign and the "less then" sign. See `FLAGS_NO_STRING_ENCODE` for the counterpart. Meta Flags ---------- This can be used exactly the same as normal flags. But each meta flag is just a collection of different other flags. There is no functional difference in using a meta flag or all the containing flags. For detailed documentation on the single flags read the above section. #### FLAGS_SSL_IGNORE_ERRORS This flag disables all SSL warnings. It is an alternative to use FLAGS_SSL_IGNORE_INVALID_CERT | FLAGS_SSL_IGNORE_INVALID_HOST. #### FLAGS_APACHE_WS This flag should be used if the server is an apache ws xmlrpc server. This will set some flags, so that the not standard conform behavior of the server will be ignored. This will enable the following flags: FLAGS_IGNORE_NAMESPACES, FLAGS_NIL, FLAGS_DEFAULT_TYPE_STRING License ======= The library is licensed under [MIT License] (http://www.opensource.org/licenses/mit-license.php). See the LICENSE file for the license text. For the uninformed reader: What does MIT mean? - You can copy this, modify it, distribute it, sell it, eat it. - You don't need to notice me about anything of the above. - If you make changes to it, it would be nice (but not obliged), if you would share it with me again. - Put the copyright notice and the LICENSE file in any copy you make. Bugs? ===== If you find a bug or wish some enhancements for the library, please fill an issue here on github or contact me otherwise (www.timroes.de). aXMLRPC-1.8.1/build.gradle000066400000000000000000000015401256106573100151540ustar00rootroot00000000000000plugins { id "com.jfrog.bintray" version "1.3.1" } apply plugin: 'java' apply plugin: 'maven' apply plugin: 'signing' version = '1.8.1' group = 'de.timroes' task sourcesJar(type: Jar, dependsOn: classes) { classifier = 'sources' from sourceSets.main.allSource } task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from javadoc.destinationDir } artifacts { archives sourcesJar archives javadocJar } repositories { mavenLocal() mavenCentral() } dependencies { testCompile "junit:junit:4.12" } bintray { user = project.properties['bintray.user'] key = project.properties['bintray.key'] configurations = ['archives'] pkg { repo = 'maven' name = 'aXMLRPC' userOrg = project.properties['bintray.user'] licenses = ['MIT'] vcsUrl = 'https://github.com/timroes/aXMLRPC.git' version { name = project.version } } } aXMLRPC-1.8.1/gradle/000077500000000000000000000000001256106573100141335ustar00rootroot00000000000000aXMLRPC-1.8.1/settings.gradle000066400000000000000000000000351256106573100157130ustar00rootroot00000000000000rootProject.name = 'aXMLRPC' aXMLRPC-1.8.1/src/000077500000000000000000000000001256106573100134645ustar00rootroot00000000000000aXMLRPC-1.8.1/src/main/000077500000000000000000000000001256106573100144105ustar00rootroot00000000000000aXMLRPC-1.8.1/src/main/java/000077500000000000000000000000001256106573100153315ustar00rootroot00000000000000aXMLRPC-1.8.1/src/main/java/de/000077500000000000000000000000001256106573100157215ustar00rootroot00000000000000aXMLRPC-1.8.1/src/main/java/de/timroes/000077500000000000000000000000001256106573100174035ustar00rootroot00000000000000aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/000077500000000000000000000000001256106573100210515ustar00rootroot00000000000000aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/AuthenticationManager.java000066400000000000000000000021751256106573100261730ustar00rootroot00000000000000package de.timroes.axmlrpc; import de.timroes.base64.Base64; import java.net.HttpURLConnection; /** * The AuthenticationManager handle basic HTTP authentication. * * @author Tim Roes */ public class AuthenticationManager { private String user; private String pass; /** * Clear the username and password. No basic HTTP authentication will be used * in the next calls. */ public void clearAuthData() { this.user = null; this.pass = null; } /** * Set the username and password that should be used to perform basic * http authentication. * * @param user Username * @param pass Password */ public void setAuthData(String user, String pass) { this.user = user; this.pass = pass; } /** * Set the authentication at the HttpURLConnection. * * @param http The HttpURLConnection to set authentication. */ public void setAuthentication(HttpURLConnection http) { if(user == null || pass == null || user.length() <= 0 || pass.length() <= 0) { return; } String base64login = Base64.encode(user + ":" + pass); http.addRequestProperty("Authorization", "Basic " + base64login); } }aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/Call.java000066400000000000000000000050431256106573100225710ustar00rootroot00000000000000package de.timroes.axmlrpc; import de.timroes.axmlrpc.serializer.SerializerHandler; import de.timroes.axmlrpc.xmlcreator.SimpleXMLCreator; import de.timroes.axmlrpc.xmlcreator.XmlElement; /** * A Call object represents a call of a remote methode. * It contains the name of the method to be called and the parameters to use * in this remote procedure call. To send it over the network the method getXML * returns an xml representation according to the XML-RPC specification as a String. * * @author Tim Roes */ public class Call { private String method; private Object[] params; /** * Create a new method call with the given name and no parameters. * @param method The method to be called. */ public Call(String method) { this(method, null); } /** * Create a new method call with the given name and parameters. * @param method The method to be called. * @param params An array of parameters for the method. */ public Call(String method, Object[] params) { this.method = method; this.params = params; } /** * Return an xml representation of the method call as specified in * http://www.xmlrpc.com/spec. If flags have been set in the XMLRPCClient * the returning xml does not comply strict to the standard. * * @return The string of the xml representing this call. * @throws XMLRPCException Will be thrown whenever the xml representation cannot * be build without errors. * @see XMLRPCClient */ public String getXML() throws XMLRPCException { SimpleXMLCreator creator = new SimpleXMLCreator(); XmlElement methodCall = new XmlElement(XMLRPCClient.METHOD_CALL); creator.setRootElement(methodCall); XmlElement methodName = new XmlElement(XMLRPCClient.METHOD_NAME); methodName.setContent(method); methodCall.addChildren(methodName); if(params != null && params.length > 0) { XmlElement params = new XmlElement(XMLRPCClient.PARAMS); methodCall.addChildren(params); for(Object o : this.params) { params.addChildren(getXMLParam(o)); } } return creator.toString(); } /** * Generates the param xml tag for a specific parameter object. * * @param o The parameter object. * @return The object serialized into an xml tag. * @throws XMLRPCException Will be thrown if the serialization failed. */ private XmlElement getXMLParam(Object o) throws XMLRPCException { XmlElement param = new XmlElement(XMLRPCClient.PARAM); XmlElement value = new XmlElement(XMLRPCClient.VALUE); param.addChildren(value); value.addChildren(SerializerHandler.getDefault().serialize(o)); return param; } } aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/CookieManager.java000066400000000000000000000044151256106573100244240ustar00rootroot00000000000000package de.timroes.axmlrpc; import java.net.HttpURLConnection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * The CookieManager handles cookies for the http requests. * If the FLAGS_ENABLE_COOKIES has been set, it will save cookies * and send it with every request. * * @author Tim Roes */ class CookieManager { private static final String SET_COOKIE = "Set-Cookie"; private static final String COOKIE = "Cookie"; private int flags; private Map cookies = new ConcurrentHashMap(); /** * Create a new CookieManager with the given flags. * * @param flags A combination of flags to be set. */ public CookieManager(int flags) { this.flags = flags; } /** * Delete all cookies. */ public void clearCookies() { cookies.clear(); } /** * Returns a {@link Map} of all cookies. * * @return All cookies */ public Map getCookies() { return cookies; } /** * Read the cookies from an http response. It will look at every Set-Cookie * header and put the cookie to the map of cookies. * * @param http A http connection. */ public void readCookies(HttpURLConnection http) { // Only save cookies if FLAGS_ENABLE_COOKIES has been set. if((flags & XMLRPCClient.FLAGS_ENABLE_COOKIES) == 0) return; String cookie, key; String[] split; // Extract every Set-Cookie field and put the cookie to the cookies map. for(int i = 0; i < http.getHeaderFields().size(); i++) { key = http.getHeaderFieldKey(i); if(key != null && SET_COOKIE.toLowerCase().equals(key.toLowerCase())) { cookie = http.getHeaderField(i).split(";")[0]; split = cookie.split("="); if(split.length >= 2) cookies.put(split[0], split[1]); } } } /** * Write the cookies to a http connection. It will set the Cookie field * to all currently set cookies in the map. * * @param http A http connection. */ public void setCookies(HttpURLConnection http) { // Only save cookies if FLAGS_ENABLE_COOKIES has been set. if((flags & XMLRPCClient.FLAGS_ENABLE_COOKIES) == 0) return; String concat = ""; for(Map.Entry cookie : cookies.entrySet()) { concat += cookie.getKey() + "=" + cookie.getValue() + "; "; } http.setRequestProperty(COOKIE, concat); } }aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/ResponseParser.java000066400000000000000000000055511256106573100246750ustar00rootroot00000000000000package de.timroes.axmlrpc; import de.timroes.axmlrpc.serializer.SerializerHandler; import java.io.InputStream; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * The ResponseParser parses the response of an XMLRPC server to an object. * * @author Tim Roes */ class ResponseParser { private static final String FAULT_CODE = "faultCode"; private static final String FAULT_STRING = "faultString"; /** * The given InputStream must contain the xml response from an xmlrpc server. * This method extract the content of it as an object. * * @param response The InputStream of the server response. * @return The returned object. * @throws XMLRPCException Will be thrown whenever something fails. * @throws XMLRPCServerException Will be thrown, if the server returns an error. */ public Object parse(InputStream response) throws XMLRPCException { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); DocumentBuilder builder = factory.newDocumentBuilder(); Document dom = builder.parse(response); Element e = dom.getDocumentElement(); // Check for root tag if(!e.getNodeName().equals(XMLRPCClient.METHOD_RESPONSE)) { throw new XMLRPCException("MethodResponse root tag is missing."); } e = XMLUtil.getOnlyChildElement(e.getChildNodes()); if(e.getNodeName().equals(XMLRPCClient.PARAMS)) { e = XMLUtil.getOnlyChildElement(e.getChildNodes()); if(!e.getNodeName().equals(XMLRPCClient.PARAM)) { throw new XMLRPCException("The params tag must contain a param tag."); } return getReturnValueFromElement(e); } else if(e.getNodeName().equals(XMLRPCClient.FAULT)) { @SuppressWarnings("unchecked") Map o = (Map)getReturnValueFromElement(e); throw new XMLRPCServerException((String)o.get(FAULT_STRING), (Integer)o.get(FAULT_CODE)); } throw new XMLRPCException("The methodResponse tag must contain a fault or params tag."); } catch (Exception ex) { if(ex instanceof XMLRPCServerException) throw (XMLRPCServerException)ex; else throw new XMLRPCException("Error getting result from server.", ex); } } /** * This method takes an element (must be a param or fault element) and * returns the deserialized object of this param tag. * * @param element An param element. * @return The deserialized object within the given param element. * @throws XMLRPCException Will be thrown when the structure of the document * doesn't match the XML-RPC specification. */ private Object getReturnValueFromElement(Element element) throws XMLRPCException { element = XMLUtil.getOnlyChildElement(element.getChildNodes()); return SerializerHandler.getDefault().deserialize(element); } }aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/XMLRPCCallback.java000066400000000000000000000021641256106573100243410ustar00rootroot00000000000000package de.timroes.axmlrpc; /** * The XMLRPCCallback interface must be implemented by a listener for an * asynchronous call to a server method. * When the server responds, the corresponding method on the listener is called. * * @author Tim Roes */ public interface XMLRPCCallback { /** * This callback is called whenever the server successfully responds. * * @param id The id as returned by the XMLRPCClient.asyncCall(..) method for this request. * @param result The Object returned from the server. */ public void onResponse(long id, Object result); /** * This callback is called whenever an error occurs during the method call. * * @param id The id as returned by the XMLRPCClient.asyncCall(..) method for this request. * @param error The error occured. */ public void onError(long id, XMLRPCException error); /** * This callback is called whenever the server returns an error. * * @param id The id as returned by the XMLRPCClient.asyncCall(..) method for this request. * @param error The error returned from the server. */ public void onServerError(long id, XMLRPCServerException error); } aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/XMLRPCClient.java000066400000000000000000000647131256106573100240730ustar00rootroot00000000000000package de.timroes.axmlrpc; import de.timroes.axmlrpc.serializer.SerializerHandler; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.net.*; import java.security.SecureRandom; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Map; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; import javax.net.ssl.*; /** * An XMLRPCClient is a client used to make XML-RPC (Extensible Markup Language * Remote Procedure Calls). * The specification of XMLRPC can be found at http://www.xmlrpc.com/spec. * You can use flags to extend the functionality of the client to some extras. * Further information on the flags can be found in the documentation of these. * For a documentation on how to use this class see also the README file delivered * with the source of this library. * * @author Tim Roes */ public class XMLRPCClient { private static final String DEFAULT_USER_AGENT = "aXMLRPC"; /** * Constants from the http protocol. */ static final String USER_AGENT = "User-Agent"; static final String CONTENT_TYPE = "Content-Type"; static final String TYPE_XML = "text/xml; charset=utf-8"; static final String HOST = "Host"; static final String CONTENT_LENGTH = "Content-Length"; static final String HTTP_POST = "POST"; /** * XML elements to be used. */ static final String METHOD_RESPONSE = "methodResponse"; static final String PARAMS = "params"; static final String PARAM = "param"; public static final String VALUE = "value"; static final String FAULT = "fault"; static final String METHOD_CALL = "methodCall"; static final String METHOD_NAME = "methodName"; static final String STRUCT_MEMBER = "member"; /** * No flags should be set. */ public static final int FLAGS_NONE = 0x0; /** * The client should parse responses strict to specification. * It will check if the given content-type is right. * The method name in a call must only contain of A-Z, a-z, 0-9, _, ., :, / * Normally this is not needed. */ public static final int FLAGS_STRICT = 0x01; /** * The client will be able to handle 8 byte integer values (longs). * The xml type tag <i8> will be used. This is not in the specification * but some libraries and servers support this behaviour. * If this isn't enabled you cannot recieve 8 byte integers and if you try to * send a long the value must be within the 4byte integer range. */ public static final int FLAGS_8BYTE_INT = 0x02; /** * With this flag, the client will be able to handle cookies, meaning saving cookies * from the server and sending it with every other request again. This is needed * for some XML-RPC interfaces that support login. */ public static final int FLAGS_ENABLE_COOKIES = 0x04; /** * The client will be able to send null values. A null value will be send * as . This extension is described under: http://ontosys.com/xml-rpc/extensions.php */ public static final int FLAGS_NIL = 0x08; /** * With this flag enabled, the XML-RPC client will ignore the HTTP status * code of the response from the server. According to specification the * status code must be 200. This flag is only needed for the use with * not standard compliant servers. */ public static final int FLAGS_IGNORE_STATUSCODE = 0x10; /** * With this flag enabled, the client will forward the request, if * the 301 or 302 HTTP status code has been received. If this flag has not * been set, the client will throw an exception on these HTTP status codes. */ public static final int FLAGS_FORWARD = 0x20; /** * With this flag enabled, the client will ignore, if the URL doesn't match * the SSL Certificate. This should be used with caution. Normally the URL * should always match the URL in the SSL certificate, even with self signed * certificates. */ public static final int FLAGS_SSL_IGNORE_INVALID_HOST = 0x40; /** * With this flag enabled, the client will ignore all unverified SSL/TLS * certificates. This must be used, if you use self-signed certificates * or certificated from unknown (or untrusted) authorities. If this flag is * used, calls to {@link #installCustomTrustManager(javax.net.ssl.TrustManager)} * won't have any effect. */ public static final int FLAGS_SSL_IGNORE_INVALID_CERT = 0x80; /** * With this flag enabled, a value with a missing type tag, will be parsed * as a string element. This is just for incoming messages. Outgoing messages * will still be generated according to specification. */ public static final int FLAGS_DEFAULT_TYPE_STRING = 0x100; /** * With this flag enabled, the {@link XMLRPCClient} ignores all namespaces * used within the response from the server. */ public static final int FLAGS_IGNORE_NAMESPACES = 0x200; /** * With this flag enabled, the {@link XMLRPCClient} will use the system http * proxy to connect to the XML-RPC server. */ public static final int FLAGS_USE_SYSTEM_PROXY = 0x400; /** * This prevents the decoding of incoming strings, meaning & and < * won't be decoded to the & sign and the "less then" sign. See * {@link #FLAGS_NO_STRING_ENCODE} for the counterpart. */ public static final int FLAGS_NO_STRING_DECODE = 0x800; /** * By default outgoing string values will be encoded according to specification. * Meaning the & sign will be encoded to & and the "less then" sign to <. * If you set this flag, the encoding won't be done for outgoing string values. * See {@link #FLAGS_NO_STRING_ENCODE} for the counterpart. */ public static final int FLAGS_NO_STRING_ENCODE = 0x1000; /** * This flag disables all SSL warnings. It is an alternative to use * FLAGS_SSL_IGNORE_INVALID_CERT | FLAGS_SSL_IGNORE_INVALID_HOST. There * is no functional difference. */ public static final int FLAGS_SSL_IGNORE_ERRORS = FLAGS_SSL_IGNORE_INVALID_CERT | FLAGS_SSL_IGNORE_INVALID_HOST; /** * This flag should be used if the server is an apache ws xmlrpc server. * This will set some flags, so that the not standard conform behavior * of the server will be ignored. * This will enable the following flags: FLAGS_IGNORE_NAMESPACES, FLAGS_NIL, * FLAGS_DEFAULT_TYPE_STRING */ public static final int FLAGS_APACHE_WS = FLAGS_IGNORE_NAMESPACES | FLAGS_NIL | FLAGS_DEFAULT_TYPE_STRING; private final int flags; private URL url; private Map httpParameters = new ConcurrentHashMap(); private Map backgroundCalls = new ConcurrentHashMap(); private ResponseParser responseParser; private CookieManager cookieManager; private AuthenticationManager authManager; private TrustManager[] trustManagers; private KeyManager[] keyManagers; private Proxy proxy; private int timeout; /** * Create a new XMLRPC client for the given URL. * * @param url The URL to send the requests to. * @param userAgent A user agent string to use in the HTTP requests. * @param flags A combination of flags to be set. */ public XMLRPCClient(URL url, String userAgent, int flags) { SerializerHandler.initialize(flags); this.url = url; this.flags = flags; // Create a parser for the http responses. responseParser = new ResponseParser(); cookieManager = new CookieManager(flags); authManager = new AuthenticationManager(); httpParameters.put(CONTENT_TYPE, TYPE_XML); httpParameters.put(USER_AGENT, userAgent); // If invalid ssl certs are ignored, instantiate an all trusting TrustManager if(isFlagSet(FLAGS_SSL_IGNORE_INVALID_CERT)) { trustManagers = new TrustManager[] { new X509TrustManager() { public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { } public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } } }; } if(isFlagSet(FLAGS_USE_SYSTEM_PROXY)) { // Read system proxy settings and generate a proxy from that Properties prop = System.getProperties(); String proxyHost = prop.getProperty("http.proxyHost"); int proxyPort = Integer.parseInt(prop.getProperty("http.proxyPort", "0")); if(proxyPort > 0 && proxyHost.length() > 0 && !proxyHost.equals("null")) { proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)); } } } /** * Create a new XMLRPC client for the given URL. * The default user agent string will be used. * * @param url The URL to send the requests to. * @param flags A combination of flags to be set. */ public XMLRPCClient(URL url, int flags) { this(url, DEFAULT_USER_AGENT, flags); } /** * Create a new XMLRPC client for the given url. * No flags will be set. * * @param url The url to send the requests to. * @param userAgent A user agent string to use in the http request. */ public XMLRPCClient(URL url, String userAgent) { this(url, userAgent, FLAGS_NONE); } /** * Create a new XMLRPC client for the given url. * No flags will be used. * The default user agent string will be used. * * @param url The url to send the requests to. */ public XMLRPCClient(URL url) { this(url, DEFAULT_USER_AGENT, FLAGS_NONE); } /** * Returns the URL this XMLRPCClient is connected to. If that URL permanently forwards * to another URL, this method will return the forwarded URL, as soon as * the first call has been made. * * @return Returns the URL for this XMLRPCClient. */ public URL getURL() { return url; } /** * Sets the time in seconds after which a call should timeout. * If {@code timeout} will be zero or less the connection will never timeout. * In case the connection times out and {@link XMLRPCTimeoutException} will * be thrown for calls made by {@link #call(java.lang.String, java.lang.Object[])}. * For calls made by {@link #callAsync(de.timroes.axmlrpc.XMLRPCCallback, java.lang.String, java.lang.Object[])} * the {@link XMLRPCCallback#onError(long, de.timroes.axmlrpc.XMLRPCException)} method * of the callback will be called. By default connections won't timeout. * * @param timeout The timeout for connections in seconds. */ public void setTimeout(int timeout) { this.timeout = timeout; } /** * Sets the user agent string. * If this method is never called the default * user agent 'aXMLRPC' will be used. * * @param userAgent The new user agent string. */ public void setUserAgentString(String userAgent) { httpParameters.put(USER_AGENT, userAgent); } /** * Sets a proxy to use for this client. If you want to use the system proxy, * use {@link #FLAGS_adbUSE_SYSTEM_PROXY} instead. If combined with * {@code FLAGS_USE_SYSTEM_PROXY}, this proxy will be used instead of the * system proxy. * * @param proxy A proxy to use for the connection. */ public void setProxy(Proxy proxy) { this.proxy = proxy; } /** * Set a HTTP header field to a custom value. * You cannot modify the Host or Content-Type field that way. * If the field already exists, the old value is overwritten. * * @param headerName The name of the header field. * @param headerValue The new value of the header field. */ public void setCustomHttpHeader(String headerName, String headerValue) { if(CONTENT_TYPE.equals(headerName) || HOST.equals(headerName) || CONTENT_LENGTH.equals(headerName)) { throw new XMLRPCRuntimeException("You cannot modify the Host, Content-Type or Content-Length header."); } httpParameters.put(headerName, headerValue); } /** * Set the username and password that should be used to perform basic * http authentication. * * @param user Username * @param pass Password */ public void setLoginData(String user, String pass) { authManager.setAuthData(user, pass); } /** * Clear the username and password. No basic HTTP authentication will be used * in the next calls. */ public void clearLoginData() { authManager.clearAuthData(); } /** * Returns a {@link Map} of all cookies. It contains each cookie key as a map * key and its value as a map value. Cookies will only be used if {@link #FLAGS_ENABLE_COOKIES} * has been set for the client. This map will also be available (and empty) * when this flag hasn't been said, but has no effect on the HTTP connection. * * @return A {@code Map} of all cookies. */ public Map getCookies() { return cookieManager.getCookies(); } /** * Delete all cookies currently used by the client. * This method has only an effect, as long as the FLAGS_ENABLE_COOKIES has * been set on this client. */ public void clearCookies() { cookieManager.clearCookies(); } /** * Installs a custom {@link TrustManager} to handle SSL/TLS certificate verification. * This will replace any previously installed {@code TrustManager}s. * If {@link #FLAGS_SSL_IGNORE_INVALID_CERT} is set, this won't do anything. * * @param trustManager {@link TrustManager} to install. * * @see #installCustomTrustManagers(javax.net.ssl.TrustManager[]) */ public void installCustomTrustManager(TrustManager trustManager) { if(!isFlagSet(FLAGS_SSL_IGNORE_INVALID_CERT)) { trustManagers = new TrustManager[] { trustManager }; } } /** * Installs custom {@link TrustManager TrustManagers} to handle SSL/TLS certificate * verification. This will replace any previously installed {@code TrustManagers}s. * If {@link #FLAGS_SSL_IGNORE_INVALID_CERT} is set, this won't do anything. * * @param trustManagers {@link TrustManager TrustManagers} to install. * * @see #installCustomTrustManager(javax.net.ssl.TrustManager) */ public void installCustomTrustManagers(TrustManager[] trustManagers) { if(!isFlagSet(FLAGS_SSL_IGNORE_INVALID_CERT)) { this.trustManagers = trustManagers.clone(); } } /** * Installs a custom {@link KeyManager} to handle SSL/TLS certificate verification. * This will replace any previously installed {@code KeyManager}s. * If {@link #FLAGS_SSL_IGNORE_INVALID_CERT} is set, this won't do anything. * * @param keyManager {@link KeyManager} to install. * * @see #installCustomKeyManagers(javax.net.ssl.KeyManager[]) */ public void installCustomKeyManager(KeyManager keyManager) { if(!isFlagSet(FLAGS_SSL_IGNORE_INVALID_CERT)) { keyManagers = new KeyManager[] { keyManager }; } } /** * Installs custom {@link KeyManager KeyManagers} to handle SSL/TLS certificate * verification. This will replace any previously installed {@code KeyManagers}s. * If {@link #FLAGS_SSL_IGNORE_INVALID_CERT} is set, this won't do anything. * * @param keyManagers {@link KeyManager KeyManagers} to install. * * @see #installCustomKeyManager(javax.net.ssl.KeyManager) */ public void installCustomKeyManagers(KeyManager[] keyManagers) { if(!isFlagSet(FLAGS_SSL_IGNORE_INVALID_CERT)) { this.keyManagers = keyManagers.clone(); } } /** * Call a remote procedure on the server. The method must be described by * a method name. If the method requires parameters, this must be set. * The type of the return object depends on the server. You should consult * the server documentation and then cast the return value according to that. * This method will block until the server returned a result (or an error occurred). * Read the README file delivered with the source code of this library for more * information. * * @param method A method name to call. * @param params An array of parameters for the method. * @return The result of the server. * @throws XMLRPCException Will be thrown if an error occurred during the call. */ public Object call(String method, Object... params) throws XMLRPCException { return new Caller().call(method, params); } /** * Asynchronously call a remote procedure on the server. The method must be * described by a method name. If the method requires parameters, this must * be set. When the server returns a response the onResponse method is called * on the listener. If the server returns an error the onServerError method * is called on the listener. The onError method is called whenever something * fails. This method returns immediately and returns an identifier for the * request. All listener methods get this id as a parameter to distinguish between * multiple requests. * * @param listener A listener, which will be notified about the server response or errors. * @param methodName A method name to call on the server. * @param params An array of parameters for the method. * @return The id of the current request. */ public long callAsync(XMLRPCCallback listener, String methodName, Object... params) { long id = System.currentTimeMillis(); new Caller(listener, id, methodName, params).start(); return id; } /** * Cancel a specific asynchronous call. * * @param id The id of the call as returned by the callAsync method. */ public void cancel(long id) { // Lookup the background call for the given id. Caller cancel = backgroundCalls.get(id); if(cancel == null) { return; } // Cancel the thread cancel.cancel(); try { // Wait for the thread cancel.join(); } catch (InterruptedException ex) { // Ignore this } } /** * Create a call object from a given method string and parameters. * * @param method The method that should be called. * @param params An array of parameters or null if no parameters needed. * @return A call object. */ private Call createCall(String method, Object[] params) { if(isFlagSet(FLAGS_STRICT) && !method.matches("^[A-Za-z0-9\\._:/]*$")) { throw new XMLRPCRuntimeException("Method name must only contain A-Z a-z . : _ / "); } return new Call(method, params); } /** * Checks whether a specific flag has been set. * * @param flag The flag to check for. * @return Whether the flag has been set. */ private boolean isFlagSet(int flag) { return (this.flags & flag) != 0; } /** * The Caller class is used to make asynchronous calls to the server. * For synchronous calls the Thread function of this class isn't used. */ private class Caller extends Thread { private XMLRPCCallback listener; private long threadId; private String methodName; private Object[] params; private volatile boolean canceled; private HttpURLConnection http; /** * Create a new Caller for asynchronous use. * * @param listener The listener to notice about the response or an error. * @param threadId An id that will be send to the listener. * @param methodName The method name to call. * @param params The parameters of the call or null. */ public Caller(XMLRPCCallback listener, long threadId, String methodName, Object[] params) { this.listener = listener; this.threadId = threadId; this.methodName = methodName; this.params = params; } /** * Create a new Caller for synchronous use. * If the caller has been created with this constructor you cannot use the * start method to start it as a thread. But you can call the call method * on it for synchronous use. */ public Caller() { } /** * The run method is invoked when the thread gets started. * This will only work, if the Caller has been created with parameters. * It execute the call method and notify the listener about the result. */ @Override public void run() { if(listener == null) return; try { backgroundCalls.put(threadId, this); Object o = this.call(methodName, params); listener.onResponse(threadId, o); } catch(CancelException ex) { // Don't notify the listener, if the call has been canceled. } catch(XMLRPCServerException ex) { listener.onServerError(threadId, ex); } catch (XMLRPCException ex) { listener.onError(threadId, ex); } finally { backgroundCalls.remove(threadId); } } /** * Cancel this call. This will abort the network communication. */ public void cancel() { // Set the flag, that this thread has been canceled canceled = true; // Disconnect the connection to the server http.disconnect(); } /** * Call a remote procedure on the server. The method must be described by * a method name. If the method requires parameters, this must be set. * The type of the return object depends on the server. You should consult * the server documentation and then cast the return value according to that. * This method will block until the server returned a result (or an error occurred). * Read the README file delivered with the source code of this library for more * information. * * @param method A method name to call. * @param params An array of parameters for the method. * @return The result of the server. * @throws XMLRPCException Will be thrown if an error occurred during the call. */ public Object call(String methodName, Object[] params) throws XMLRPCException { try { Call c = createCall(methodName, params); // If proxy is available, use it URLConnection conn; if(proxy != null) conn = url.openConnection(proxy); else conn = url.openConnection(); http = verifyConnection(conn); http.setInstanceFollowRedirects(false); http.setRequestMethod(HTTP_POST); http.setDoOutput(true); http.setDoInput(true); // Set timeout if(timeout > 0) { http.setConnectTimeout(timeout * 1000); http.setReadTimeout(timeout * 1000); } // Set the request parameters for(Map.Entry param : httpParameters.entrySet()) { http.setRequestProperty(param.getKey(), param.getValue()); } authManager.setAuthentication(http); cookieManager.setCookies(http); OutputStreamWriter stream = new OutputStreamWriter(http.getOutputStream()); stream.write(c.getXML()); stream.flush(); stream.close(); // Try to get the status code from the connection int statusCode; try { statusCode = http.getResponseCode(); } catch(IOException ex) { // Due to a bug on android, the getResponseCode()-method will // fail the first time, with a IOException, when 401 or 403 has been returned. // The second time it should success. If it fail the second time again // the normal exceptipon handling can take care of this, since // it is a real error. statusCode = http.getResponseCode(); } InputStream istream; // If status code was 401 or 403 throw exception or if appropriate // flag is set, ignore error code. if(statusCode == HttpURLConnection.HTTP_FORBIDDEN || statusCode == HttpURLConnection.HTTP_UNAUTHORIZED) { if(isFlagSet(FLAGS_IGNORE_STATUSCODE)) { // getInputStream will fail if server returned above // error code, use getErrorStream instead istream = http.getErrorStream(); } else { throw new XMLRPCException("Invalid status code '" + statusCode + "' returned from server."); } } else { istream = http.getInputStream(); } // If status code is 301 Moved Permanently or 302 Found ... if(statusCode == HttpURLConnection.HTTP_MOVED_PERM || statusCode == HttpURLConnection.HTTP_MOVED_TEMP) { // ... do either a foward if(isFlagSet(FLAGS_FORWARD)) { boolean temporaryForward = (statusCode == HttpURLConnection.HTTP_MOVED_TEMP); // Get new location from header field. String newLocation = http.getHeaderField("Location"); // Try getting header in lower case, if no header has been found if(newLocation == null || newLocation.length() <= 0) newLocation = http.getHeaderField("location"); // Set new location, disconnect current connection and request to new location. URL oldURL = url; url = new URL(newLocation); http.disconnect(); Object forwardedResult = call(methodName, params); // In case of temporary forward, restore original URL again for next call. if(temporaryForward) { url = oldURL; } return forwardedResult; } else { // ... or throw an exception throw new XMLRPCException("The server responded with a http 301 or 302 status " + "code, but forwarding has not been enabled (FLAGS_FORWARD)."); } } if(!isFlagSet(FLAGS_IGNORE_STATUSCODE) && statusCode != HttpURLConnection.HTTP_OK) { throw new XMLRPCException("The status code of the http response must be 200."); } // Check for strict parameters if(isFlagSet(FLAGS_STRICT)) { if(!http.getContentType().startsWith(TYPE_XML)) { throw new XMLRPCException("The Content-Type of the response must be text/xml."); } } cookieManager.readCookies(http); return responseParser.parse(istream); } catch(SocketTimeoutException ex) { throw new XMLRPCTimeoutException("The XMLRPC call timed out."); } catch (IOException ex) { // If the thread has been canceled this exception will be thrown. // So only throw an exception if the thread hasnt been canceled // or if the thred has not been started in background. if(!canceled || threadId <= 0) { throw new XMLRPCException(ex); } else { throw new CancelException(); } } } /** * Verifies the given URLConnection to be a valid HTTP or HTTPS connection. * If the SSL ignoring flags are set, the method will ignore SSL warnings. * * @param conn The URLConnection to validate. * @return The verified HttpURLConnection. * @throws XMLRPCException Will be thrown if an error occurred. */ private HttpURLConnection verifyConnection(URLConnection conn) throws XMLRPCException { if(!(conn instanceof HttpURLConnection)) { throw new IllegalArgumentException("The URL is not valid for a http connection."); } // Validate the connection if its an SSL connection if(conn instanceof HttpsURLConnection) { HttpsURLConnection h = (HttpsURLConnection)conn; // Don't check, that URL matches the certificate. if(isFlagSet(FLAGS_SSL_IGNORE_INVALID_HOST)) { h.setHostnameVerifier(new HostnameVerifier() { public boolean verify(String host, SSLSession ssl) { return true; } }); } // Associate the TrustManager with TLS and SSL connections, if present. if(trustManagers != null) { try { String[] sslContexts = new String[]{ "TLS", "SSL" }; for(String ctx : sslContexts) { SSLContext sc = SSLContext.getInstance(ctx); sc.init(keyManagers, trustManagers, new SecureRandom()); h.setSSLSocketFactory(sc.getSocketFactory()); } } catch(Exception ex) { throw new XMLRPCException(ex); } } return h; } return (HttpURLConnection)conn; } } private class CancelException extends RuntimeException { } } aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/XMLRPCException.java000066400000000000000000000006501256106573100246010ustar00rootroot00000000000000package de.timroes.axmlrpc; /** * The exception is thrown whenever the remote procedure call fails in some point. * * @author Tim Roes */ public class XMLRPCException extends Exception { public XMLRPCException() { super(); } public XMLRPCException(Exception ex) { super(ex); } public XMLRPCException(String ex) { super(ex); } public XMLRPCException(String msg, Exception ex) { super(msg, ex); } } aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/XMLRPCRuntimeException.java000066400000000000000000000003731256106573100261470ustar00rootroot00000000000000package de.timroes.axmlrpc; /** * * @author Tim Roes */ public class XMLRPCRuntimeException extends RuntimeException { public XMLRPCRuntimeException(String ex) { super(ex); } public XMLRPCRuntimeException(Exception ex) { super(ex); } }aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/XMLRPCServerException.java000066400000000000000000000014111256106573100257640ustar00rootroot00000000000000package de.timroes.axmlrpc; /** * This exception will be thrown if the server returns an error. It contains the * message and the error number returned from the server. * * @author Tim Roes */ public class XMLRPCServerException extends XMLRPCException { private int errornr; public XMLRPCServerException(String ex, int errnr) { super(ex); this.errornr = errnr; } /** * Returns the detail message string of this throwable. * It will have the server error number at the end. * * @return The detail message string of this error. */ @Override public String getMessage() { return super.getMessage() + " [" + errornr + "]"; } /** * Return the error number. * * @return The error number. */ public int getErrorNr() { return errornr; } } aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/XMLRPCTimeoutException.java000066400000000000000000000005011256106573100261430ustar00rootroot00000000000000package de.timroes.axmlrpc; /** * Will be thrown when a call to the server times out. The timeout can be * set via {@link XMLRPCClient#setTimeout(int)}. * * @author Tim Roes */ public class XMLRPCTimeoutException extends XMLRPCException { XMLRPCTimeoutException(String ex) { super(ex); } }aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/XMLUtil.java000066400000000000000000000061331256106573100232150ustar00rootroot00000000000000package de.timroes.axmlrpc; import de.timroes.axmlrpc.xmlcreator.XmlElement; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * This class provides some utility methods for the use with the Java DOM parser. * * @author Tim Roes */ public class XMLUtil { /** * Returns the only child element in a given NodeList. * Will throw an error if there is more then one child element or any other * child that is not an element or an empty text string (whitespace are normal). * * @param list A NodeList of children nodes. * @return The only child element in the given node list. * @throws XMLRPCException Will be thrown if there is more then one child element * except empty text nodes. */ public static Element getOnlyChildElement(NodeList list) throws XMLRPCException { Element e = null; Node n; for(int i = 0; i < list.getLength(); i++) { n = list.item(i); // Strip only whitespace text elements and comments if((n.getNodeType() == Node.TEXT_NODE && n.getNodeValue().trim().length() <= 0) || n.getNodeType() == Node.COMMENT_NODE) continue; // Check if there is anything else than an element node. if(n.getNodeType() != Node.ELEMENT_NODE) { throw new XMLRPCException("Only element nodes allowed."); } // If there was already an element, throw exception. if(e != null) { throw new XMLRPCException("Element has more than one children."); } e = (Element)n; } return e; } /** * Returns the text node from a given NodeList. If the list contains * more then just text nodes, an exception will be thrown. * * @param list The given list of nodes. * @return The text of the given node list. * @throws XMLRPCException Will be thrown if there is more than just one * text node within the list. */ public static String getOnlyTextContent(NodeList list) throws XMLRPCException { StringBuilder builder = new StringBuilder(); Node n; for(int i = 0; i < list.getLength(); i++) { n = list.item(i); // Skip comments inside text tag. if(n.getNodeType() == Node.COMMENT_NODE) { continue; } if(n.getNodeType() != Node.TEXT_NODE) { throw new XMLRPCException("Element must contain only text elements."); } builder.append(n.getNodeValue()); } return builder.toString(); } /** * Checks if the given {@link NodeList} contains a child element. * * @param list The {@link NodeList} to check. * @return Whether the {@link NodeList} contains children. */ public static boolean hasChildElement(NodeList list) { Node n; for(int i = 0; i < list.getLength(); i++) { n = list.item(i); if(n.getNodeType() == Node.ELEMENT_NODE) { return true; } } return false; } /** * Creates an xml tag with a given type and content. * * @param type The type of the xml tag. What will be filled in the <..>. * @param content The content of the tag. * @return The xml tag with its content as a string. */ public static XmlElement makeXmlTag(String type, String content) { XmlElement xml = new XmlElement(type); xml.setContent(content); return xml; } }aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/serializer/000077500000000000000000000000001256106573100232225ustar00rootroot00000000000000aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/serializer/ArraySerializer.java000066400000000000000000000040561256106573100272020ustar00rootroot00000000000000package de.timroes.axmlrpc.serializer; import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.XMLRPCRuntimeException; import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.xmlcreator.XmlElement; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * * @author Tim Roes */ public class ArraySerializer implements Serializer { private static final String ARRAY_DATA = "data"; private static final String ARRAY_VALUE = "value"; public Object deserialize(Element content) throws XMLRPCException { List list = new ArrayList(); Element data = XMLUtil.getOnlyChildElement(content.getChildNodes()); if(!ARRAY_DATA.equals(data.getNodeName())) { throw new XMLRPCException("The array must contain one data tag."); } // Deserialize every array element Node value; for(int i = 0; i < data.getChildNodes().getLength(); i++) { value = data.getChildNodes().item(i); // Strip only whitespace text elements and comments if(value == null || (value.getNodeType() == Node.TEXT_NODE && value.getNodeValue().trim().length() <= 0) || value.getNodeType() == Node.COMMENT_NODE) continue; if(value.getNodeType() != Node.ELEMENT_NODE) { throw new XMLRPCException("Wrong element inside of array."); } list.add(SerializerHandler.getDefault().deserialize((Element)value)); } return list.toArray(); } public XmlElement serialize(Object object) { Iterable iter; if ( object instanceof Iterable){ iter = (Iterable)object; } else { iter = Arrays.asList((Object[]) object); } XmlElement array = new XmlElement(SerializerHandler.TYPE_ARRAY); XmlElement data = new XmlElement(ARRAY_DATA); array.addChildren(data); try { XmlElement e; for(Object obj : iter) { e = new XmlElement(ARRAY_VALUE); e.addChildren(SerializerHandler.getDefault().serialize(obj)); data.addChildren(e); } } catch(XMLRPCException ex) { throw new XMLRPCRuntimeException(ex); } return array; } } aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/serializer/Base64Serializer.java000066400000000000000000000011431256106573100271420ustar00rootroot00000000000000package de.timroes.axmlrpc.serializer; import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.xmlcreator.XmlElement; import de.timroes.base64.Base64; import org.w3c.dom.Element; /** * * @author Tim Roes */ public class Base64Serializer implements Serializer { public Object deserialize(Element content) throws XMLRPCException { return Base64.decode(XMLUtil.getOnlyTextContent(content.getChildNodes())); } public XmlElement serialize(Object object) { return XMLUtil.makeXmlTag(SerializerHandler.TYPE_BASE64, Base64.encode((Byte[])object)); } }aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/serializer/BooleanSerializer.java000066400000000000000000000011561256106573100275010ustar00rootroot00000000000000package de.timroes.axmlrpc.serializer; import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.xmlcreator.XmlElement; import org.w3c.dom.Element; /** * * @author Tim Roes */ public class BooleanSerializer implements Serializer { public Object deserialize(Element content) throws XMLRPCException { return (XMLUtil.getOnlyTextContent(content.getChildNodes()).equals("1")) ? Boolean.TRUE : Boolean.FALSE; } public XmlElement serialize(Object object) { return XMLUtil.makeXmlTag(SerializerHandler.TYPE_BOOLEAN, ((Boolean)object == true) ? "1" : "0"); } }aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/serializer/DateTimeSerializer.java000066400000000000000000000035361256106573100276220ustar00rootroot00000000000000package de.timroes.axmlrpc.serializer; import java.text.SimpleDateFormat; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.w3c.dom.Element; import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.xmlcreator.XmlElement; /** * * @author timroes */ public class DateTimeSerializer implements Serializer { private static final String DATETIME_FORMAT = "yyyyMMdd'T'HH:mm:ss"; private static final SimpleDateFormat DATE_FORMATER = new SimpleDateFormat(DATETIME_FORMAT); private static final Pattern PATTERN_WITHOUT_COLON = Pattern.compile("(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\+\\d{2})(\\d{2})"); private static final Pattern PATTERN_LEGACY = Pattern.compile("(\\d{4})(\\d{2})(\\d{2}T\\d{2}:\\d{2}:\\d{2})"); @Override public Object deserialize(Element content) throws XMLRPCException { return deserialize(XMLUtil.getOnlyTextContent(content.getChildNodes())); } public Object deserialize(String dateStr) throws XMLRPCException { try { String value = formatStringIfNeeded(dateStr); return javax.xml.bind.DatatypeConverter.parseDateTime(value).getTime(); } catch (Exception ex) { throw new XMLRPCException("Unable to parse given date.", ex); } } private static String formatStringIfNeeded(String dateStr){ Matcher matcherWithoutColon = PATTERN_WITHOUT_COLON.matcher(dateStr); if ( matcherWithoutColon.matches() ){ return matcherWithoutColon.group(1) + ":" + matcherWithoutColon.group(2); } Matcher matcherLegacy = PATTERN_LEGACY.matcher(dateStr); if ( matcherLegacy.matches() ){ return matcherLegacy.group(1) + "-" + matcherLegacy.group(2) + "-" + matcherLegacy.group(3); } return dateStr; } @Override public XmlElement serialize(Object object) { return XMLUtil.makeXmlTag(SerializerHandler.TYPE_DATETIME, DATE_FORMATER.format(object)); } } aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/serializer/DoubleSerializer.java000066400000000000000000000014661256106573100273400ustar00rootroot00000000000000package de.timroes.axmlrpc.serializer; import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.xmlcreator.XmlElement; import java.math.BigDecimal; import org.w3c.dom.Element; /** * This serializer is responsible for floating point numbers. * * @author Tim Roes */ public class DoubleSerializer implements Serializer { public Object deserialize(Element content) throws XMLRPCException { return Double.valueOf(XMLUtil.getOnlyTextContent(content.getChildNodes())); } public XmlElement serialize(Object object) { // Turn double value of object into a BigDecimal to get the // right decimal point format. BigDecimal bd = BigDecimal.valueOf(((Number)object).doubleValue()); return XMLUtil.makeXmlTag(SerializerHandler.TYPE_DOUBLE, bd.toPlainString()); } } aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/serializer/IntSerializer.java000066400000000000000000000010631256106573100266510ustar00rootroot00000000000000package de.timroes.axmlrpc.serializer; import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.xmlcreator.XmlElement; import org.w3c.dom.Element; /** * * @author timroes */ public class IntSerializer implements Serializer { public Object deserialize(Element content) throws XMLRPCException { return Integer.parseInt(XMLUtil.getOnlyTextContent(content.getChildNodes())); } public XmlElement serialize(Object object) { return XMLUtil.makeXmlTag(SerializerHandler.TYPE_INT, object.toString()); } } aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/serializer/LongSerializer.java000066400000000000000000000010651256106573100270200ustar00rootroot00000000000000package de.timroes.axmlrpc.serializer; import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.xmlcreator.XmlElement; import org.w3c.dom.Element; /** * * @author Tim Roes */ class LongSerializer implements Serializer { public Object deserialize(Element content) throws XMLRPCException { return Long.parseLong(XMLUtil.getOnlyTextContent(content.getChildNodes())); } public XmlElement serialize(Object object) { return XMLUtil.makeXmlTag(SerializerHandler.TYPE_LONG, ((Long)object).toString()); } } aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/serializer/NullSerializer.java000066400000000000000000000006661256106573100270410ustar00rootroot00000000000000package de.timroes.axmlrpc.serializer; import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.xmlcreator.XmlElement; import org.w3c.dom.Element; /** * * @author Tim Roes */ public class NullSerializer implements Serializer { public Object deserialize(Element content) throws XMLRPCException { return null; } public XmlElement serialize(Object object) { return new XmlElement(SerializerHandler.TYPE_NULL); } }aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/serializer/Serializer.java000066400000000000000000000020371256106573100262000ustar00rootroot00000000000000package de.timroes.axmlrpc.serializer; import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.xmlcreator.XmlElement; import org.w3c.dom.Element; /** * A Serializer is responsible to serialize a specific type of data to * an xml tag and deserialize the content of this xml tag back to an object. * * @author Tim Roes */ public interface Serializer { /** * This method takes an xml type element and deserialize it to an object. * * @param content Must be an xml element of a specific type. * @return The deserialized content. * @throws XMLRPCException Will be thrown whenervt the deserialization fails. */ public Object deserialize(Element content) throws XMLRPCException; /** * This method takes an object and returns a representation as a string * containing the right xml type tag. The returning string must be useable * within a value tag. * * @param object The object that should be serialized. * @return An XmlElement representation of the object. */ public XmlElement serialize(Object object); }aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/serializer/SerializerHandler.java000066400000000000000000000164071256106573100275040ustar00rootroot00000000000000package de.timroes.axmlrpc.serializer; import de.timroes.axmlrpc.XMLRPCClient; import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.XMLRPCRuntimeException; import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.xmlcreator.XmlElement; import java.math.BigDecimal; import java.util.Calendar; import java.util.Date; import java.util.Map; import org.w3c.dom.Element; /** * The serializer handler serializes and deserialized objects. * It takes an object, determine its type and let the responsible handler serialize it. * For deserialization it looks at the xml tag around the element. * The class is designed as a kind of singleton, so it can be accessed from anywhere in * the library. * * @author Tim Roes */ public class SerializerHandler { public static final String TYPE_STRING = "string"; public static final String TYPE_BOOLEAN = "boolean"; public static final String TYPE_INT = "int"; public static final String TYPE_INT2 = "i4"; public static final String TYPE_LONG = "i8"; public static final String TYPE_DOUBLE = "double"; public static final String TYPE_DATETIME = "dateTime.iso8601"; public static final String TYPE_STRUCT = "struct"; public static final String TYPE_ARRAY = "array"; public static final String TYPE_BASE64 = "base64"; public static final String TYPE_NULL = "nil"; private static SerializerHandler instance; /** * Initialize the serialization handler. This method must be called before * the get method returns any object. * * @param flags The flags that has been set in the XMLRPCClient. * @see XMLRPCClient */ public static void initialize(int flags) { instance = new SerializerHandler(flags); } /** * Return the instance of the SerializerHandler. * It must have been initialized with initialize() before. * * @return The instance of the SerializerHandler. */ public static SerializerHandler getDefault() { if(instance == null) { throw new XMLRPCRuntimeException("The SerializerHandler has not been initialized."); } return instance; } private StringSerializer string; private BooleanSerializer bool = new BooleanSerializer(); private IntSerializer integer = new IntSerializer(); private LongSerializer long8 = new LongSerializer(); private StructSerializer struct = new StructSerializer(); private DoubleSerializer floating = new DoubleSerializer(); private DateTimeSerializer datetime = new DateTimeSerializer(); private ArraySerializer array = new ArraySerializer(); private Base64Serializer base64 = new Base64Serializer(); private NullSerializer nil = new NullSerializer(); private int flags; /** * Generates the SerializerHandler. * This method can only called from within the class (the initialize method). * * @param flags The flags to use. */ private SerializerHandler(int flags) { this.flags = flags; string = new StringSerializer( (flags & XMLRPCClient.FLAGS_NO_STRING_ENCODE) == 0, (flags & XMLRPCClient.FLAGS_NO_STRING_DECODE) == 0 ); } /** * Deserializes an incoming xml element to an java object. * The xml element must be the value element around the type element. * The type of the returning object depends on the type tag. * * @param element An type element from within a value tag. * @return The deserialized object. * @throws XMLRPCException Will be thrown whenever an error occurs. */ public Object deserialize(Element element) throws XMLRPCException { if(!XMLRPCClient.VALUE.equals(element.getNodeName())) { throw new XMLRPCException("Value tag is missing around value."); } if(!XMLUtil.hasChildElement(element.getChildNodes())) { // Value element doesn't contain a child element if((flags & XMLRPCClient.FLAGS_DEFAULT_TYPE_STRING) != 0) { return string.deserialize(element); } else { throw new XMLRPCException("Missing type element inside of value element."); } } // Grep type element from inside value element element = XMLUtil.getOnlyChildElement(element.getChildNodes()); Serializer s = null; String type; // If FLAGS_IGNORE_NAMESPACE has been set, only use local name. if((flags & XMLRPCClient.FLAGS_IGNORE_NAMESPACES) != 0) { type = element.getLocalName() == null ? element.getNodeName() : element.getLocalName(); } else { type = element.getNodeName(); } if((flags & XMLRPCClient.FLAGS_NIL) != 0 && TYPE_NULL.equals(type)) { s = nil; } else if(TYPE_STRING.equals(type)) { s = string; } else if(TYPE_BOOLEAN.equals(type)) { s = bool; } else if(TYPE_DOUBLE.equals(type)) { s = floating; } else if (TYPE_INT.equals(type) || TYPE_INT2.equals(type)) { s = integer; } else if(TYPE_DATETIME.equals(type)) { s = datetime; } else if (TYPE_LONG.equals(type)) { if((flags & XMLRPCClient.FLAGS_8BYTE_INT) != 0) { s = long8; } else { throw new XMLRPCException("8 byte integer is not in the specification. " + "You must use FLAGS_8BYTE_INT to enable the i8 tag."); } } else if(TYPE_STRUCT.equals(type)) { s = struct; } else if(TYPE_ARRAY.equals(type)) { s = array; } else if(TYPE_BASE64.equals(type)) { s = base64; } else { throw new XMLRPCException("No deserializer found for type '" + type + "'."); } return s.deserialize(element); } /** * Serialize an object to its representation as an xml element. * The xml element will be the type element for the use within a value tag. * * @param object The object that should be serialized. * @return The xml representation of this object. * @throws XMLRPCException Will be thrown, if an error occurs (e.g. the object * cannot be serialized to an xml element. */ public XmlElement serialize(Object object) throws XMLRPCException { Serializer s = null; if((flags & XMLRPCClient.FLAGS_NIL) != 0 && object == null) { s = nil; } else if(object instanceof String) { s = string; } else if(object instanceof Boolean) { s = bool; } else if(object instanceof Double || object instanceof Float || object instanceof BigDecimal) { s = floating; } else if (object instanceof Integer || object instanceof Short || object instanceof Byte) { s = integer; } else if(object instanceof Long) { // Check whether the 8 byte integer flag was set. if((flags & XMLRPCClient.FLAGS_8BYTE_INT) != 0) { s = long8; } else { // Allow long values as long as their fit within the 4 byte integer range. long l = (Long)object; if(l > Integer.MAX_VALUE || l < Integer.MIN_VALUE) { throw new XMLRPCException("FLAGS_8BYTE_INT must be set, if values " + "outside the 4 byte integer range should be transfered."); } else { s = integer; } } } else if(object instanceof Date) { s = datetime; } else if(object instanceof Calendar) { object = ((Calendar)object).getTime(); s = datetime; } else if (object instanceof Map) { s = struct; } else if(object instanceof byte[]) { byte[] old = (byte[])object; Byte[] boxed = new Byte[old.length]; for(int i = 0; i < boxed.length; i++) { boxed[i] = new Byte(old[i]); } object = boxed; s = base64; } else if(object instanceof Byte[]) { s = base64; } else if(object instanceof Iterable || object instanceof Object[]) { s = array; } else { throw new XMLRPCException("No serializer found for type '" + object.getClass().getName() + "'."); } return s.serialize(object); } } aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/serializer/StringSerializer.java000066400000000000000000000017531256106573100273730ustar00rootroot00000000000000package de.timroes.axmlrpc.serializer; import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.xmlcreator.XmlElement; import org.w3c.dom.Element; /** * * @author Tim Roes */ public class StringSerializer implements Serializer { private boolean decodeStrings; private boolean encodeStrings; public StringSerializer(boolean encodeStrings, boolean decodeStrings) { this.decodeStrings = decodeStrings; this.encodeStrings = encodeStrings; } public Object deserialize(Element content) throws XMLRPCException { String text = XMLUtil.getOnlyTextContent(content.getChildNodes()); if(decodeStrings) { text = text.replaceAll("<", "<").replaceAll("&", "&"); } return text; } public XmlElement serialize(Object object) { String content = object.toString(); if(encodeStrings) { content = content.replaceAll("&", "&").replaceAll("<", "<"); } return XMLUtil.makeXmlTag(SerializerHandler.TYPE_STRING, content); } }aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/serializer/StructSerializer.java000066400000000000000000000060711256106573100274070ustar00rootroot00000000000000package de.timroes.axmlrpc.serializer; import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.XMLRPCRuntimeException; import de.timroes.axmlrpc.XMLUtil; import de.timroes.axmlrpc.xmlcreator.XmlElement; import java.util.HashMap; import java.util.Map; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * * @author Tim Roes */ public class StructSerializer implements Serializer { private static final String STRUCT_MEMBER = "member"; private static final String STRUCT_NAME = "name"; private static final String STRUCT_VALUE = "value"; public Object deserialize(Element content) throws XMLRPCException { Map map = new HashMap(); Node n, m; String s; Object o; for(int i = 0; i < content.getChildNodes().getLength(); i++) { n = content.getChildNodes().item(i); // Strip only whitespace text elements and comments if((n.getNodeType() == Node.TEXT_NODE && n.getNodeValue().trim().length() <= 0) || n.getNodeType() == Node.COMMENT_NODE) continue; if(n.getNodeType() != Node.ELEMENT_NODE || !STRUCT_MEMBER.equals(n.getNodeName())) { throw new XMLRPCException("Only struct members allowed within a struct."); } // Grep name and value from member s = null; o = null; for(int j = 0; j < n.getChildNodes().getLength(); j++) { m = n.getChildNodes().item(j); // Strip only whitespace text elements and comments if((m.getNodeType() == Node.TEXT_NODE && m.getNodeValue().trim().length() <= 0) || m.getNodeType() == Node.COMMENT_NODE) continue; if(STRUCT_NAME.equals(m.getNodeName())) { if(s != null) { throw new XMLRPCException("Name of a struct member cannot be set twice."); } else { s = XMLUtil.getOnlyTextContent(m.getChildNodes()); } } else if(m.getNodeType() == Node.ELEMENT_NODE && STRUCT_VALUE.equals(m.getNodeName())) { if(o != null) { throw new XMLRPCException("Value of a struct member cannot be set twice."); } else { o = SerializerHandler.getDefault().deserialize((Element)m); } } else { throw new XMLRPCException("A struct member must only contain one name and one value."); } } map.put(s, o); } return map; } public XmlElement serialize(Object object) { XmlElement struct = new XmlElement(SerializerHandler.TYPE_STRUCT); try { XmlElement entry, name, value; // We can safely cast here, this Serializer should only be called when // the parameter is a map. @SuppressWarnings("unchecked") Map map = (Map)object; for(Map.Entry member : map.entrySet()) { entry = new XmlElement(STRUCT_MEMBER); name = new XmlElement(STRUCT_NAME); value = new XmlElement(STRUCT_VALUE); name.setContent(member.getKey()); value.addChildren(SerializerHandler.getDefault().serialize(member.getValue())); entry.addChildren(name); entry.addChildren(value); struct.addChildren(entry); } } catch(XMLRPCException ex) { throw new XMLRPCRuntimeException(ex); } return struct; } }aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/xmlcreator/000077500000000000000000000000001256106573100232315ustar00rootroot00000000000000aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/xmlcreator/SimpleXMLCreator.java000066400000000000000000000012761256106573100272340ustar00rootroot00000000000000package de.timroes.axmlrpc.xmlcreator; /** * This is a very simple xml creator. It allows creating an xml document * containing multiple xml tags. No attributes are supported. * * @author Tim Roes */ public class SimpleXMLCreator { private XmlElement root; /** * Set the root element of the xml tree. * * @param element The element to use as root element in this tree. */ public void setRootElement(XmlElement element) { this.root = element; } /** * Return the string representation of the xml tree. * @return String representation of the xml tree. */ @Override public String toString() { return "\n" + root.toString(); } } aXMLRPC-1.8.1/src/main/java/de/timroes/axmlrpc/xmlcreator/XmlElement.java000066400000000000000000000033401256106573100261460ustar00rootroot00000000000000package de.timroes.axmlrpc.xmlcreator; import java.util.ArrayList; import java.util.List; /** * An xml element within an xml tree. * In this case an xml element can have a text content OR a multiple amount * of children. The xml element itself has a name. * * @author Tim Roes */ public class XmlElement { private List children = new ArrayList(); private String name; private String content; /** * Create a new xml element with the given name. * * @param name The name of the xml element. */ public XmlElement(String name) { this.name = name; } /** * Add a child to this xml element. * * @param element The child to add. */ public void addChildren(XmlElement element) { children.add(element); } /** * Set the content of this xml tag. If the content is set the children * won't be used in a string representation. * * @param content Content of the xml element. */ public void setContent(String content) { this.content = content; } /** * Return a string representation of this xml element. * * @return String representation of xml element. */ @Override public String toString() { StringBuilder builder = new StringBuilder(); if(content != null && content.length() > 0) { builder.append("\n<").append(name).append(">") .append(content) .append("\n"); return builder.toString(); } else if(children.size() > 0) { builder.append("\n<").append(name).append(">"); for(XmlElement x : children) { builder.append(x.toString()); } builder.append("\n"); return builder.toString(); } else { builder.append("\n<").append(name).append("/>\n"); return builder.toString(); } } } aXMLRPC-1.8.1/src/main/java/de/timroes/base64/000077500000000000000000000000001256106573100204675ustar00rootroot00000000000000aXMLRPC-1.8.1/src/main/java/de/timroes/base64/Base64.java000066400000000000000000000103241256106573100223560ustar00rootroot00000000000000package de.timroes.base64; import java.util.HashMap; /** * A Base64 en/decoder. You can use it to encode and decode strings and byte arrays. * * @author Tim Roes */ public class Base64 { private static final char[] code = ("=ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz0123456789+/").toCharArray(); private static final HashMap map = new HashMap(); static { for(int i = 0; i < code.length; i++) { map.put(code[i], (byte)i); } } /** * Decode a base64 encoded string to a byte array. * * @param in A string representing a base64 encoding. * @return The decoded byte array. */ public static byte[] decode(String in) { in = in.replaceAll("\\r|\\n",""); if(in.length() % 4 != 0) { throw new IllegalArgumentException("The length of the input string must be a multiple of four."); } if(!in.matches("^[A-Za-z0-9+/]*[=]{0,3}$")) { throw new IllegalArgumentException("The argument contains illegal characters."); } byte[] out = new byte[(in.length()*3)/4]; char[] input = in.toCharArray(); int outi = 0; int b1, b2, b3, b4; for(int i = 0; i < input.length; i+=4) { b1 = (map.get(input[i]) - 1); b2 = (map.get(input[i+1]) - 1); b3 = (map.get(input[i+2]) - 1); b4 = (map.get(input[i+3]) - 1); out[outi++] = (byte)(b1 << 2 | b2 >>> 4); out[outi++] = (byte)((b2 & 0x0F) << 4 | b3 >>> 2); out[outi++] = (byte)((b3 & 0x03) << 6 | (b4 & 0x3F)); } if(in.endsWith("=")) { byte[] trimmed = new byte[out.length - (in.length() - in.indexOf("="))]; System.arraycopy(out, 0, trimmed, 0, trimmed.length); return trimmed; } return out; } /** * Decode a base64 encoded string to a string. * * @param in The string representation of the base64 encoding. * @return The decoded string in the default charset of the JVM. */ public static String decodeAsString(String in) { return new String(decode(in)); } /** * Encode a String and return the encoded string. * * @param in A string to encode. * @return The encoded byte array. */ public static String encode(String in) { return encode(in.getBytes()); } /** * Encode a Byte array and return the encoded string. * * @param in A string to encode. * @return The encoded byte array. */ public static String encode(Byte[] in) { byte[] tmp = new byte[in.length]; for(int i = 0; i < tmp.length; i++) { tmp[i] = in[i]; } return encode(tmp); } /** * Encode a byte array and return the encoded string. * * @param in A string to encode. * @return The encoded byte array. */ public static String encode(byte[] in) { StringBuilder builder = new StringBuilder(4 * ((in.length+2)/3)); byte[] encoded = encodeAsBytes(in); for(int i = 0; i < encoded.length; i++) { builder.append(code[encoded[i]+1]); if(i % 72 == 71) builder.append("\n"); } return builder.toString(); } /** * Encode a String and return the encoded byte array. Bytes that has been * appended to pad the string to a multiple of four are set to -1 in the array. * * @param in A string to encode. * @return The encoded byte array. */ public static byte[] encodeAsBytes(String in) { return encodeAsBytes(in.getBytes()); } /** * Encode a byte array and return the encoded byte array. Bytes that has been * appended to pad the string to a multiple of four are set to -1 in the array. * * @param inArray A string to encode. * @return The encoded byte array. */ public static byte[] encodeAsBytes(byte[] inArray) { // Output string must be 4 * floor((n+2)/3) large byte[] out = new byte[4 * ((inArray.length+2)/3)]; // Create padded input array with the next largest length that is a multiple of 3 byte[] in = new byte[(inArray.length+2)/3*3]; // Copy content form unpadded to padded array System.arraycopy(inArray, 0, in, 0, inArray.length); int outi = 0; for(int i = 0; i < in.length; i+=3) { out[outi++] = (byte)((in[i] & 0xFF) >>> 2); out[outi++] = (byte)(((in[i] & 0x03) << 4) | ((in[i+1] & 0xFF) >>> 4)); out[outi++] = (byte)(((in[i+1] & 0x0F) << 2) | ((in[i+2] & 0xFF) >>> 6)); out[outi++] = (byte)(in[i+2] & 0x3F); } for(int i = in.length - inArray.length; i > 0; i--) { out[out.length - i] = -1; } return out; } }aXMLRPC-1.8.1/src/test/000077500000000000000000000000001256106573100144435ustar00rootroot00000000000000aXMLRPC-1.8.1/src/test/java/000077500000000000000000000000001256106573100153645ustar00rootroot00000000000000aXMLRPC-1.8.1/src/test/java/de/000077500000000000000000000000001256106573100157545ustar00rootroot00000000000000aXMLRPC-1.8.1/src/test/java/de/timroes/000077500000000000000000000000001256106573100174365ustar00rootroot00000000000000aXMLRPC-1.8.1/src/test/java/de/timroes/axmlrpc/000077500000000000000000000000001256106573100211045ustar00rootroot00000000000000aXMLRPC-1.8.1/src/test/java/de/timroes/axmlrpc/serializer/000077500000000000000000000000001256106573100232555ustar00rootroot00000000000000aXMLRPC-1.8.1/src/test/java/de/timroes/axmlrpc/serializer/TestDateTimeSerializer.java000066400000000000000000000021401256106573100305030ustar00rootroot00000000000000package de.timroes.axmlrpc.serializer; import static org.junit.Assert.*; import java.util.Date; public class TestDateTimeSerializer { private static final Date EXPECTED_DATE = new Date(85, 2, 4, 12, 13, 14); @org.junit.Test public void canParseLegacyDates() throws Exception { Date date = (Date) new DateTimeSerializer().deserialize("19850304T12:13:14"); assertDatesCloseEnough(EXPECTED_DATE, date); } @org.junit.Test public void canParseDateWithoutSemiColon() throws Exception { Date date = (Date) new DateTimeSerializer().deserialize("1985-03-04T12:13:14+0100"); assertDatesCloseEnough(EXPECTED_DATE, date); } @org.junit.Test public void canParseDateWithSemiColon() throws Exception { Date date = (Date) new DateTimeSerializer().deserialize("1985-03-04T12:13:14+01:00"); assertDatesCloseEnough(EXPECTED_DATE, date); } //Because I don't want the tests to fail if the user isn't in my timezone private void assertDatesCloseEnough(Date expected, Date actual){ long differenceInMs = Math.abs(expected.getTime() - actual.getTime()); assertTrue(differenceInMs < 24 * 60 * 60 * 1000); } }