/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-2015 The eXist Project
* http://exist-db.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.exist.xmlrpc;
import java.io.IOException;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.exist.security.MessageDigester;
import org.exist.storage.serializers.EXistOutputKeys;
import org.exist.test.ExistWebServer;
import org.exist.test.TestConstants;
import org.exist.util.MimeType;
import org.exist.xmldb.XmldbURI;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.ClassRule;
import org.junit.Test;
import javax.xml.transform.OutputKeys;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import org.exist.security.Permission;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.util.*;
import org.junit.After;
import org.xml.sax.SAXException;
/**
* JUnit test for XMLRPC interface methods.
*
* @author wolf
* @author Pierrick Brihaye <pierrick.brihaye@free.fr>
* @author ljo
*/
public class XmlRpcTest {
@ClassRule
public final static ExistWebServer existWebServer = new ExistWebServer(true, false, true);
private final static XmldbURI TARGET_COLLECTION = XmldbURI.ROOT_COLLECTION_URI.append("xmlrpc");
private final static XmldbURI TARGET_RESOURCE = TARGET_COLLECTION.append(TestConstants.TEST_XML_URI);
public final static XmldbURI MODULE_RESOURCE = TARGET_COLLECTION.append(TestConstants.TEST_MODULE_URI);
private final static XmldbURI SPECIAL_COLLECTION = TARGET_COLLECTION.append(TestConstants.SPECIAL_NAME);
private final static XmldbURI SPECIAL_RESOURCE = SPECIAL_COLLECTION.append(TestConstants.SPECIAL_XML_URI);
private final static String XML_DATA
= "<test>"
+ "<para>\u00E4\u00E4\u00F6\u00F6\u00FC\u00FC\u00C4\u00C4\u00D6\u00D6\u00DC\u00DC\u00DF\u00DF</para>"
+ "<para>\uC5F4\uB2E8\uACC4</para>"
+ "</test>";
private final static String XSL_DATA
= "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" "
+ "version=\"1.0\">"
+ "<xsl:param name=\"testparam\"/>"
+ "<xsl:template match=\"test\"><test><xsl:apply-templates/></test></xsl:template>"
+ "<xsl:template match=\"para\">"
+ "<p><xsl:value-of select=\"$testparam\"/>: <xsl:apply-templates/></p></xsl:template>"
+ "</xsl:stylesheet>";
public final static String MODULE_DATA
= "module namespace tm = \"http://exist-db.org/test/module\"; "
+ "declare variable $tm:imported-external-string as xs:string external;";
public final static String QUERY_MODULE_DATA
= "xquery version \"1.0\";"
+ "declare namespace tm-query = \"http://exist-db.org/test/module/query\";"
+ "import module namespace tm = \"http://exist-db.org/test/module\" "
+ "at \"xmldb:exist://" + MODULE_RESOURCE.toString() + "\"; "
+ "declare variable $tm-query:local-external-string as xs:string external;"
+ "($tm:imported-external-string, $tm-query:local-external-string)";
private static String getUri() {
return "http://localhost:" + existWebServer.getPort() + "/xmlrpc";
}
@After
public void tearDown() throws XmlRpcException, MalformedURLException {
XmlRpcClient xmlrpc = getClient();
List<String> params = new ArrayList<>(1);
params.add(TARGET_COLLECTION.toString());
@SuppressWarnings("unused")
Boolean b = (Boolean) xmlrpc.execute("removeCollection", params);
}
@Test
public void testStoreAndRetrieve() throws XmlRpcException, IOException {
XmlRpcClient xmlrpc = getClient();
List<Object> params = new ArrayList<>();
params.add(TARGET_COLLECTION.toString());
Boolean result = (Boolean) xmlrpc.execute("createCollection", params);
assertTrue(result);
params.clear();
params.add(XML_DATA);
params.add(TARGET_RESOURCE.toString());
params.add(1);
result = (Boolean) xmlrpc.execute("parse", params);
assertTrue(result);
Map<String, String> options = new HashMap<>();
params.clear();
params.add(TARGET_RESOURCE.toString());
params.add(options);
byte[] data = (byte[]) xmlrpc.execute("getDocument", params);
assertNotNull(data);
params.clear();
params.add(TARGET_RESOURCE.toString());
params.add(StandardCharsets.UTF_8.name());
params.add(0);
data = (byte[]) xmlrpc.execute("getDocument", params);
assertNotNull(data);
params.clear();
params.add(TARGET_RESOURCE.toString());
params.add(0);
String sdata = (String) xmlrpc.execute("getDocumentAsString", params);
assertNotNull(data);
params.clear();
params.add(TARGET_RESOURCE.toString());
params.add(options);
Map table = (Map) xmlrpc.execute("getDocumentData", params);
try (final ByteArrayOutputStream os = new ByteArrayOutputStream()) {
int offset = (int) table.get("offset");
data = (byte[]) table.get("data");
os.write(data);
while (offset > 0) {
params.clear();
params.add(table.get("handle"));
params.add(offset);
table = (Map<?, ?>) xmlrpc.execute("getNextChunk", params);
offset = (int) table.get("offset");
data = (byte[]) table.get("data");
os.write(data);
}
data = os.toByteArray();
assertTrue(data.length > 0);
}
params.clear();
params.add(TARGET_RESOURCE.toString());
Boolean b = (Boolean) xmlrpc.execute("hasDocument", params);
assertTrue(b);
}
private void storeData() throws XmlRpcException, MalformedURLException {
XmlRpcClient xmlrpc = getClient();
List<Object> params = new ArrayList<>();
params.add(TARGET_COLLECTION.toString());
Boolean result = (Boolean) xmlrpc.execute("createCollection", params);
assertTrue(result);
params.clear();
params.add(XML_DATA);
params.add(TARGET_RESOURCE.toString());
params.add(1);
result = (Boolean) xmlrpc.execute("parse", params);
assertTrue(result);
params.set(0, XSL_DATA);
params.set(1, TARGET_COLLECTION.append("test.xsl").toString());
result = (Boolean) xmlrpc.execute("parse", params);
assertTrue(result);
params.set(0, MODULE_DATA.getBytes(UTF_8));
params.set(1, MODULE_RESOURCE.toString());
params.set(2, MimeType.XQUERY_TYPE.getName());
params.add(Boolean.TRUE);
result = (Boolean) xmlrpc.execute("storeBinary", params);
assertTrue(result);
}
@Test
public void testRemoveCollection() throws XmlRpcException, MalformedURLException {
storeData();
XmlRpcClient xmlrpc = getClient();
List params = new ArrayList(1);
params.add(TARGET_COLLECTION.toString());
Boolean b = (Boolean) xmlrpc.execute("hasCollection", params);
assertTrue(b);
b = (Boolean) xmlrpc.execute("removeCollection", params);
assertTrue(b);
b = (Boolean) xmlrpc.execute("hasCollection", params);
assertFalse(b);
}
@Test
public void testRemoveDoc() throws XmlRpcException, MalformedURLException {
storeData();
XmlRpcClient xmlrpc = getClient();
List params = new ArrayList(1);
params.add(TARGET_RESOURCE.toString());
Boolean b = (Boolean) xmlrpc.execute("hasDocument", params);
assertTrue(b);
b = (Boolean) xmlrpc.execute("remove", params);
assertTrue(b);
b = (Boolean) xmlrpc.execute("hasDocument", params);
assertFalse(b);
}
@Test
public void testRetrieveDoc() throws XmlRpcException, MalformedURLException {
storeData();
Map<String, String> options = new HashMap<>();
options.put("indent", "yes");
options.put("encoding", StandardCharsets.UTF_8.name());
options.put("expand-xincludes", "yes");
options.put("process-xsl-pi", "no");
List<Object> params = new ArrayList<>();
params.add(TARGET_RESOURCE.toString());
params.add(options);
// execute the call
XmlRpcClient xmlrpc = getClient();
byte[] data = (byte[]) xmlrpc.execute("getDocument", params);
options.put("stylesheet", "test.xsl");
data = (byte[]) xmlrpc.execute("getDocument", params);
}
@Test
public void testCharEncoding() throws XmlRpcException, MalformedURLException {
storeData();
List<Object> params = new ArrayList<>();
String query = "distinct-values(//para)";
params.add(query.getBytes(UTF_8));
params.add(new HashMap<>());
XmlRpcClient xmlrpc = getClient();
HashMap<?, ?> result = (HashMap<?, ?>) xmlrpc.execute("queryP", params);
Object[] resources = (Object[]) result.get("results");
//TODO : check the number of resources before !
assertEquals(2, resources.length);
String value = (String) resources[0];
assertEquals("\u00E4\u00E4\u00F6\u00F6\u00FC\u00FC\u00C4\u00C4\u00D6\u00D6\u00DC\u00DC\u00DF\u00DF", value);
value = (String) resources[1];
assertEquals("\uC5F4\uB2E8\uACC4", value);
}
@Test
public void testQuery() throws XmlRpcException, MalformedURLException {
storeData();
List<Object> params = new ArrayList<>();
String query
= "(::pragma exist:serialize indent=no::) //para";
params.add(query.getBytes(UTF_8));
params.add(10);
params.add(1);
params.add(new HashMap());
XmlRpcClient xmlrpc = getClient();
byte[] result = (byte[]) xmlrpc.execute("query", params);
assertNotNull(result);
assertTrue(result.length > 0);
}
@Test
public void testQueryWithStylesheet() throws XmlRpcException, MalformedURLException, SAXException, IOException {
storeData();
Map<String, String> options = new HashMap<>();
options.put(EXistOutputKeys.STYLESHEET, "test.xsl");
options.put(EXistOutputKeys.STYLESHEET_PARAM + ".testparam", "Test");
options.put(OutputKeys.OMIT_XML_DECLARATION, "yes");
//TODO : check the number of resources before !
List<Object> params = new ArrayList<>();
String query = "//para[1]";
params.add(query.getBytes(UTF_8));
params.add(options);
XmlRpcClient xmlrpc = getClient();
Integer handle = (Integer) xmlrpc.execute("executeQuery", params);
assertNotNull(handle);
params.clear();
params.add(handle);
params.add(0);
params.add(options);
byte[] item = (byte[]) xmlrpc.execute("retrieve", params);
assertNotNull(item);
assertTrue(item.length > 0);
String out = new String(item, UTF_8);
assertXMLEqual("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<p>Test: \u00E4\u00E4\u00F6\u00F6\u00FC\u00FC\u00C4\u00C4\u00D6\u00D6\u00DC\u00DC\u00DF\u00DF</p>", out);
}
@Test
public void testCompile() throws XmlRpcException, MalformedURLException {
storeData();
List<Object> params = new ArrayList<>();
String query = "<a>Invalid<a>";
params.add(query.getBytes(UTF_8));
params.add(new HashMap<>());
XmlRpcClient xmlrpc = getClient();
Map stats = (Map) xmlrpc.execute("compile", params);
assertNotNull(stats);
assertNotNull(stats.get("error"));
}
@Test
public void testAddAccount() throws MalformedURLException, XmlRpcException {
String user = "rudi";
String passwd = "pass";
String simpleMd5 = MessageDigester.md5(passwd, true);
String digest = MessageDigester.md5(user + ":exist:" + passwd, false);
List<Object> params = new ArrayList<>(12);
params.add("rudi");
params.add(simpleMd5);
params.add(digest);
params.add(new String[]{"guest"});
params.add(true);
params.add(Permission.DEFAULT_UMASK);
params.add(new HashMap<>());
XmlRpcClient xmlrpc = getClient();
xmlrpc.execute("addAccount", params);
XmlRpcClientConfigImpl config = (XmlRpcClientConfigImpl) xmlrpc.getClientConfig();
config.setBasicUserName("admin");
config.setBasicPassword("");
xmlrpc.execute("sync", Collections.EMPTY_LIST);
}
@Test
public void testExecuteQuery() throws XmlRpcException, MalformedURLException {
storeData();
List<Object> params = new ArrayList<>();
String query = "distinct-values(//para)";
params.add(query.getBytes(UTF_8));
params.add(new HashMap<>());
XmlRpcClient xmlrpc = getClient();
Integer handle = (Integer) xmlrpc.execute("executeQuery", params);
assertNotNull(handle);
params.clear();
params.add(handle);
Integer hits = (Integer) xmlrpc.execute("getHits", params);
assertNotNull(hits);
assertEquals(2, hits.intValue());
params.add(0);
params.add(new HashMap());
byte[] item = (byte[]) xmlrpc.execute("retrieve", params);
params.clear();
params.add(handle);
params.add(1);
params.add(new HashMap());
item = (byte[]) xmlrpc.execute("retrieve", params);
}
@Test
public void testQueryModuleExternalVar() throws XmlRpcException, MalformedURLException {
storeData();
List<Object> params = new ArrayList<>();
params.add(QUERY_MODULE_DATA.getBytes(UTF_8));
Map<String, Object> qp = new HashMap<>();
Map<String, Object> namespaceDecls = new HashMap<>();
namespaceDecls.put("tm", "http://exist-db.org/test/module");
namespaceDecls.put("tm-query", "http://exist-db.org/test/module/query");
qp.put(RpcAPI.NAMESPACES, namespaceDecls);
Map<String, Object> variableDecls = new HashMap<>();
variableDecls.put("tm:imported-external-string", "imported-string-value");
variableDecls.put("tm-query:local-external-string", "local-string-value");
qp.put(RpcAPI.VARIABLES, variableDecls);
params.add(qp);
XmlRpcClient xmlrpc = getClient();
Map<String, Object[]> result = (Map<String, Object[]>) xmlrpc.execute("queryP", params);
Object[] resources = (Object[]) result.get("results");
assertEquals(2, resources.length);
String value = (String) resources[0];
assertEquals("imported-string-value", value);
value = (String) resources[1];
assertEquals("local-string-value", value);
}
@Test
public void testCollectionWithAccentsAndSpaces() throws XmlRpcException, MalformedURLException {
storeData();
List<Object> params = new ArrayList<>();
params.add(SPECIAL_COLLECTION.toString());
XmlRpcClient xmlrpc = getClient();
xmlrpc.execute("createCollection", params);
params.clear();
params.add(XML_DATA);
params.add(SPECIAL_RESOURCE.toString());
params.add(1);
Boolean result = (Boolean) xmlrpc.execute("parse", params);
assertTrue(result);
params.clear();
params.add(SPECIAL_COLLECTION.removeLastSegment().toString());
HashMap collection = (HashMap) xmlrpc.execute("describeCollection", params);
Object[] collections = (Object[]) collection.get("collections");
boolean foundMatch = false;
String targetCollectionName = SPECIAL_COLLECTION.lastSegment().toString();
for (int i = 0; i < collections.length; i++) {
String childName = (String) collections[i];
if (childName.equals(targetCollectionName)) {
foundMatch = true;
break;
}
}
assertTrue("added collection not found", foundMatch);
Map<String, String> options = new HashMap<>();
options.put("indent", "yes");
options.put("encoding", StandardCharsets.UTF_8.name());
options.put("expand-xincludes", "yes");
options.put("process-xsl-pi", "no");
params.clear();
params.add(SPECIAL_RESOURCE.toString());
params.add(options);
// execute the call
byte[] data = (byte[]) xmlrpc.execute("getDocument", params);
}
protected XmlRpcClient getClient() throws MalformedURLException {
XmlRpcClient client = new XmlRpcClient();
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
config.setEnabledForExtensions(true);
config.setServerURL(new URL(getUri()));
config.setBasicUserName("admin");
config.setBasicPassword("");
client.setConfig(config);
return client;
}
public static void main(String[] args) {
org.junit.runner.JUnitCore.main(XmlRpcTest.class.getName());
//Explicit shutdownDB for the shutdownDB hook
System.exit(0);
}
}