/*
* eXist Open Source Native XML Database
* Copyright (C) 2004-2009 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.
*
* $Id$
*/
package org.exist.xmlrpc;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
import org.custommonkey.xmlunit.XMLAssert;
import org.exist.jetty.JettyStart;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.exist.security.MessageDigester;
import org.exist.storage.serializers.EXistOutputKeys;
import org.exist.test.TestConstants;
import org.exist.util.MimeType;
import org.exist.xmldb.XmldbURI;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import javax.xml.transform.OutputKeys;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import org.exist.security.Permission;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* JUnit test for XMLRPC interface methods.
* @author wolf
* @author Pierrick Brihaye <pierrick.brihaye@free.fr>
* @author ljo
*/
public class XmlRpcTest {
private static JettyStart server = null;
// jetty.port.standalone
private final static String URI = "http://localhost:" + System.getProperty("jetty.port", "8088") + "/xmlrpc";
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)";
public XmlRpcTest() {
}
@BeforeClass
public static void setUp() {
System.out.println("==================================");
//Don't worry about closing the server : the shutdownDB hook will do the job
initServer();
}
@AfterClass
public static void stopServer() {
server.shutdown();
server = null;
System.gc();
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
protected void tearDown() {
XmlRpcClient xmlrpc = getClient();
try {
List<String> params = new ArrayList<String>(1);
params.add(TARGET_COLLECTION.toString());
@SuppressWarnings("unused")
Boolean b = (Boolean) xmlrpc.execute("removeCollection", params);
} catch (XmlRpcException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
private static void initServer() {
try {
if (server == null) {
server = new JettyStart();
System.out.println("Starting standalone server...");
server.run();
}
} catch (Exception e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
@Test
public void testStoreAndRetrieve() {
System.out.println("---testStoreAndRetrieve");
try {
System.out.println("Creating collection " + TARGET_COLLECTION);
XmlRpcClient xmlrpc = getClient();
Vector<Object> params = new Vector<Object>();
params.addElement(TARGET_COLLECTION.toString());
Boolean result = (Boolean)xmlrpc.execute("createCollection", params);
Assert.assertTrue(result.booleanValue());
System.out.println("Storing document " + XML_DATA);
params.clear();
params.addElement(XML_DATA);
params.addElement(TARGET_RESOURCE.toString());
params.addElement(new Integer(1));
result = (Boolean)xmlrpc.execute("parse", params);
Assert.assertTrue(result.booleanValue());
System.out.println("Documents stored.");
HashMap<String, String> options = new HashMap<String, String>();
params.clear();
params.addElement(TARGET_RESOURCE.toString());
params.addElement(options);
byte[] data = (byte[]) xmlrpc.execute( "getDocument", params );
System.out.println( new String(data, "UTF-8") );
Assert.assertNotNull(data);
params.clear();
params.addElement(TARGET_RESOURCE.toString());
params.addElement("UTF-8");
params.addElement(0);
data = (byte[]) xmlrpc.execute( "getDocument", params );
System.out.println( new String(data, "UTF-8") );
Assert.assertNotNull(data);
params.clear();
params.addElement(TARGET_RESOURCE.toString());
params.addElement(0);
String sdata = (String) xmlrpc.execute( "getDocumentAsString", params );
System.out.println(sdata);
Assert.assertNotNull(data);
params.clear();
params.addElement(TARGET_RESOURCE.toString());
params.addElement(options);
HashMap<?,?> table = (HashMap<?,?>) xmlrpc.execute("getDocumentData", params);
ByteArrayOutputStream os = new ByteArrayOutputStream();
int offset = ((Integer)table.get("offset")).intValue();
data = (byte[])table.get("data");
os.write(data);
while(offset > 0) {
params.clear();
params.add(table.get("handle"));
params.add(Integer.valueOf(offset));
table = (HashMap<?,?>) xmlrpc.execute("getNextChunk", params);
offset = ((Integer)table.get("offset")).intValue();
data = (byte[])table.get("data");
os.write(data);
}
data = os.toByteArray();
Assert.assertTrue(data.length > 0);
System.out.println(new String(data, "UTF-8"));
params.clear();
params.addElement(TARGET_RESOURCE.toString());
Boolean b = (Boolean) xmlrpc.execute( "hasDocument", params );
Assert.assertTrue(b.booleanValue());
} catch (Exception e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
private void storeData() {
System.out.println("---storeData");
try {
System.out.println("Creating collection " + TARGET_COLLECTION);
XmlRpcClient xmlrpc = getClient();
Vector<Object> params = new Vector<Object>();
params.addElement(TARGET_COLLECTION.toString());
Boolean result = (Boolean)xmlrpc.execute("createCollection", params);
Assert.assertTrue(result.booleanValue());
System.out.println("Storing document " + XML_DATA);
params.clear();
params.addElement(XML_DATA);
params.addElement(TARGET_RESOURCE.toString());
params.addElement(Integer.valueOf(1));
result = (Boolean)xmlrpc.execute("parse", params);
Assert.assertTrue(result.booleanValue());
System.out.println("Storing resource " + XSL_DATA);
params.setElementAt(XSL_DATA, 0);
params.setElementAt(TARGET_COLLECTION.append("test.xsl").toString(), 1);
result = (Boolean)xmlrpc.execute("parse", params);
Assert.assertTrue(result.booleanValue());
System.out.println("Storing resource " + MODULE_DATA);
params.setElementAt(MODULE_DATA.getBytes(UTF_8), 0);
params.setElementAt(MODULE_RESOURCE.toString(), 1);
params.setElementAt(MimeType.XQUERY_TYPE.getName(), 2);
params.addElement(Boolean.TRUE);
result = (Boolean)xmlrpc.execute("storeBinary", params);
Assert.assertTrue(result.booleanValue());
System.out.println("Documents stored.");
} catch (Exception e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
@Test
public void testRemoveCollection() {
System.out.println("---testRemoveCollection");
storeData();
XmlRpcClient xmlrpc = getClient();
try {
List<Object> params = new ArrayList<Object>(1);
params.add(TARGET_COLLECTION.toString());
Boolean b = (Boolean) xmlrpc.execute("hasCollection", params);
Assert.assertTrue(b.booleanValue());
b = (Boolean) xmlrpc.execute("removeCollection", params);
Assert.assertTrue(b.booleanValue());
b = (Boolean) xmlrpc.execute("hasCollection", params);
Assert.assertFalse(b.booleanValue());
} catch (XmlRpcException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
@Test
public void testRemoveDoc() {
System.out.println("---testRemoveDoc");
storeData();
XmlRpcClient xmlrpc = getClient();
try {
List<Object> params = new ArrayList<Object>(1);
params.add(TARGET_RESOURCE.toString());
Boolean b = (Boolean) xmlrpc.execute("hasDocument", params);
Assert.assertTrue(b.booleanValue());
b = (Boolean) xmlrpc.execute("remove", params);
Assert.assertTrue(b.booleanValue());
b = (Boolean) xmlrpc.execute("hasDocument", params);
Assert.assertFalse(b.booleanValue());
} catch (XmlRpcException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
@Test
public void testRetrieveDoc() {
System.out.println("---testRetrieveDoc");
storeData();
System.out.println("Retrieving document " + TARGET_RESOURCE);
Hashtable<String, String> options = new Hashtable<String, String>();
options.put("indent", "yes");
options.put("encoding", "UTF-8");
options.put("expand-xincludes", "yes");
options.put("process-xsl-pi", "no");
Vector<Object> params = new Vector<Object>();
params.addElement( TARGET_RESOURCE.toString() );
params.addElement( options );
try {
// execute the call
XmlRpcClient xmlrpc = getClient();
byte[] data = (byte[]) xmlrpc.execute( "getDocument", params );
System.out.println( new String(data, "UTF-8") );
System.out.println("Retrieving document with stylesheet applied");
options.put("stylesheet", "test.xsl");
data = (byte[]) xmlrpc.execute( "getDocument", params );
System.out.println( new String(data, "UTF-8") );
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
@Test
public void testCharEncoding() {
System.out.println("---testCharEncoding");
storeData();
try {
System.out.println("Testing charsets returned by query");
Vector<Object> params = new Vector<Object>();
String query = "distinct-values(//para)";
params.addElement(query.getBytes(UTF_8));
params.addElement(new Hashtable<Object, Object>());
XmlRpcClient xmlrpc = getClient();
HashMap<?,?> result = (HashMap<?,?>) xmlrpc.execute( "queryP", params );
Object[] resources = (Object[]) result.get("results");
//TODO : check the number of resources before !
Assert.assertEquals(resources.length, 2);
String value = (String)resources[0];
Assert.assertEquals(value, "\u00E4\u00E4\u00F6\u00F6\u00FC\u00FC\u00C4\u00C4\u00D6\u00D6\u00DC\u00DC\u00DF\u00DF");
System.out.println("Result1: " + value);
value = (String)resources[1];
Assert.assertEquals(value, "\uC5F4\uB2E8\uACC4");
System.out.println("Result2: " + value);
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
@Test
public void testQuery() {
System.out.println("---testQuery");
storeData();
try {
Vector<Object> params = new Vector<Object>();
String query =
"(::pragma exist:serialize indent=no::) //para";
params.addElement(query.getBytes(UTF_8));
params.addElement(Integer.valueOf(10));
params.addElement(Integer.valueOf(1));
params.addElement(new Hashtable());
XmlRpcClient xmlrpc = getClient();
byte[] result = (byte[]) xmlrpc.execute( "query", params );
Assert.assertNotNull(result);
Assert.assertTrue(result.length > 0);
System.out.println(new String(result, "UTF-8"));
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
@Test
public void testQueryWithStylesheet() {
System.out.println("---testQueryWithStylesheet");
storeData();
try {
HashMap<String, String> options = new HashMap<String, String>();
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 !
Vector<Object> params = new Vector<Object>();
String query = "//para[1]";
params.addElement(query.getBytes(UTF_8));
params.addElement(options);
XmlRpcClient xmlrpc = getClient();
Integer handle = (Integer) xmlrpc.execute( "executeQuery", params );
Assert.assertNotNull(handle);
params.clear();
params.addElement(handle);
params.addElement(Integer.valueOf(0));
params.addElement(options);
byte[] item = (byte[]) xmlrpc.execute( "retrieve", params );
Assert.assertNotNull(item);
Assert.assertTrue(item.length > 0);
String out = new String(item, UTF_8);
System.out.println("Received: " + out);
XMLAssert.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);
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
@Test
public void testCompile() {
System.out.println("---testCompile");
storeData();
try {
Vector<Object> params = new Vector<Object>();
String query = "<a>Invalid<a>";
params.addElement(query.getBytes(UTF_8));
params.addElement(new Hashtable<Object, Object>());
XmlRpcClient xmlrpc = getClient();
System.out.println("Executing query: " + query);
Map stats = (Map) xmlrpc.execute( "compile", params );
Assert.assertNotNull(stats);
Assert.assertNotNull(stats.get("error"));
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
@Test
public void testAddAccount() {
System.out.println("---testAddAccount");
try {
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<Object>(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<String, String>());
XmlRpcClient xmlrpc = getClient();
xmlrpc.execute("addAccount", params);
XmlRpcClientConfigImpl config = (XmlRpcClientConfigImpl) xmlrpc.getClientConfig();
config.setBasicUserName("admin");
config.setBasicPassword("");
xmlrpc.execute("sync", new ArrayList());
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
@Test
public void testExecuteQuery() {
System.out.println("---testExecuteQuery");
storeData();
try {
Vector<Object> params = new Vector<Object>();
String query = "distinct-values(//para)";
params.addElement(query.getBytes(UTF_8));
params.addElement(new Hashtable<Object, Object>());
XmlRpcClient xmlrpc = getClient();
System.out.println("Executing query: " + query);
Integer handle = (Integer) xmlrpc.execute( "executeQuery", params );
Assert.assertNotNull(handle);
params.clear();
params.addElement(handle);
Integer hits = (Integer) xmlrpc.execute( "getHits", params );
Assert.assertNotNull(hits);
System.out.println("Found: " + hits.intValue());
Assert.assertEquals(hits.intValue(), 2);
params.addElement(new Integer(0));
params.addElement(new Hashtable<Object, Object>());
byte[] item = (byte[]) xmlrpc.execute( "retrieve", params );
System.out.println(new String(item, "UTF-8"));
params.clear();
params.addElement(handle);
params.addElement(Integer.valueOf(1));
params.addElement(new Hashtable<Object, Object>());
item = (byte[]) xmlrpc.execute( "retrieve", params );
System.out.println(new String(item, "UTF-8"));
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
@Test
public void testQueryModuleExternalVar() {
System.out.println("---testQueryModuleExternalVar");
storeData();
try {
System.out.println("Quering with external variable definied in module ...");
Vector<Object> params = new Vector<Object>();
params.addElement(QUERY_MODULE_DATA.getBytes(UTF_8));
Hashtable<String, Object> qp = new Hashtable<String, Object>();
HashMap<String, Object> namespaceDecls = new HashMap<String, Object>();
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);
HashMap<String, Object> variableDecls = new HashMap<String, Object>();
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.addElement(qp);
XmlRpcClient xmlrpc = getClient();
HashMap<String, Object[]> result = (HashMap) xmlrpc.execute("queryP", params);
Object[] resources = (Object[]) result.get("results");
Assert.assertEquals(resources.length, 2);
String value = (String) resources[0];
Assert.assertEquals(value, "imported-string-value");
System.out.println("Imported external: " + value);
value = (String) resources[1];
Assert.assertEquals(value, "local-string-value");
System.out.println("Local external: " + value);
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
@Test
public void testCollectionWithAccentsAndSpaces() {
System.out.println("---testCollectionWithAccentsAndSpaces");
storeData();
try {
System.out.println("Creating collection with accents and spaces in name ...");
Vector<Object> params = new Vector<Object>();
params.addElement(SPECIAL_COLLECTION.toString());
XmlRpcClient xmlrpc = getClient();
xmlrpc.execute( "createCollection", params );
System.out.println("Storing document " + XML_DATA);
params.clear();
params.addElement(XML_DATA);
params.addElement(SPECIAL_RESOURCE.toString());
params.addElement(Integer.valueOf(1));
Boolean result = (Boolean)xmlrpc.execute("parse", params);
Assert.assertTrue(result.booleanValue());
params.clear();
params.addElement(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];
System.out.println("Child collection: " + childName);
if(childName.equals(targetCollectionName)) {
foundMatch=true;
break;
}
}
Assert.assertTrue("added collection not found", foundMatch);
System.out.println("Retrieving document '" + SPECIAL_RESOURCE.toString() + "'");
HashMap<String, String> options = new HashMap<String, String>();
options.put("indent", "yes");
options.put("encoding", "UTF-8");
options.put("expand-xincludes", "yes");
options.put("process-xsl-pi", "no");
params.clear();
params.addElement( SPECIAL_RESOURCE.toString() );
params.addElement( options );
// execute the call
byte[] data = (byte[]) xmlrpc.execute( "getDocument", params );
System.out.println( new String(data, "UTF-8") );
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
protected XmlRpcClient getClient() {
try {
XmlRpcClient client = new XmlRpcClient();
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
config.setEnabledForExtensions(true);
config.setServerURL(new URL(URI));
config.setBasicUserName("admin");
config.setBasicPassword("");
client.setConfig(config);
return client;
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
org.junit.runner.JUnitCore.main(XmlRpcTest.class.getName());
//Explicit shutdownDB for the shutdownDB hook
System.exit(0);
}
}