blob: ad1d38e4ddc6a513bf6866fac02ce9fefe8ee845 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.solr.util;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.XML;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
abstract public class BaseTestHarness {
private static final ThreadLocal<DocumentBuilder> builderTL = new ThreadLocal<>();
private static final ThreadLocal<XPath> xpathTL = new ThreadLocal<>();
public static DocumentBuilder getXmlDocumentBuilder() {
try {
DocumentBuilder builder = builderTL.get();
if (builder == null) {
builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
builderTL.set(builder);
}
return builder;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static XPath getXpath() {
try {
XPath xpath = xpathTL.get();
if (xpath == null) {
xpath = XPathFactory.newInstance().newXPath();
xpathTL.set(xpath);
}
return xpath;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* A helper method which validates a String against an array of XPath test
* strings.
*
* @param xml The xml String to validate
* @param tests Array of XPath strings to test (in boolean mode) on the xml
* @return null if all good, otherwise the first test that fails.
*/
public static String validateXPath(String xml, String... tests)
throws XPathExpressionException, SAXException {
if (tests==null || tests.length == 0) return null;
Document document = null;
try {
document = getXmlDocumentBuilder().parse(new ByteArrayInputStream
(xml.getBytes(StandardCharsets.UTF_8)));
} catch (UnsupportedEncodingException e1) {
throw new RuntimeException("Totally weird UTF-8 exception", e1);
} catch (IOException e2) {
throw new RuntimeException("Totally weird io exception", e2);
}
for (String xp : tests) {
xp=xp.trim();
Boolean bool = (Boolean) getXpath().evaluate(xp, document, XPathConstants.BOOLEAN);
if (!bool) {
return xp;
}
}
return null;
}
public static Object evaluateXPath(String xml, String xpath, QName returnType)
throws XPathExpressionException, SAXException {
if (null == xpath) return null;
Document document = null;
try {
document = getXmlDocumentBuilder().parse(new ByteArrayInputStream
(xml.getBytes(StandardCharsets.UTF_8)));
} catch (UnsupportedEncodingException e1) {
throw new RuntimeException("Totally weird UTF-8 exception", e1);
} catch (IOException e2) {
throw new RuntimeException("Totally weird io exception", e2);
}
xpath = xpath.trim();
return getXpath().evaluate(xpath.trim(), document, returnType);
}
/**
* A helper that creates an xml &lt;doc&gt; containing all of the
* fields and values specified
*
* @param fieldsAndValues 0 and Even numbered args are fields names odds are field values.
*/
public static String makeSimpleDoc(String... fieldsAndValues) {
try {
StringWriter w = new StringWriter();
w.append("<doc>");
for (int i = 0; i < fieldsAndValues.length; i+=2) {
XML.writeXML(w, "field", fieldsAndValues[i + 1], "name",
fieldsAndValues[i]);
}
w.append("</doc>");
return w.toString();
} catch (IOException e) {
throw new RuntimeException
("this should never happen with a StringWriter", e);
}
}
/**
* Generates a delete by query xml string
* @param q Query that has not already been xml escaped
* @param args The attributes of the delete tag
*/
public static String deleteByQuery(String q, String... args) {
try {
StringWriter r = new StringWriter();
XML.writeXML(r, "query", q);
return delete(r.getBuffer().toString(), args);
} catch(IOException e) {
throw new RuntimeException
("this should never happen with a StringWriter", e);
}
}
/**
* Generates a delete by id xml string
* @param id ID that has not already been xml escaped
* @param args The attributes of the delete tag
*/
public static String deleteById(String id, String... args) {
try {
StringWriter r = new StringWriter();
XML.writeXML(r, "id", id);
return delete(r.getBuffer().toString(), args);
} catch(IOException e) {
throw new RuntimeException
("this should never happen with a StringWriter", e);
}
}
/**
* Generates a delete xml string
* @param val text that has not already been xml escaped
* @param args 0 and Even numbered args are params, Odd numbered args are XML escaped values.
*/
private static String delete(String val, String... args) {
try {
StringWriter r = new StringWriter();
XML.writeUnescapedXML(r, "delete", val, (Object[]) args);
return r.getBuffer().toString();
} catch(IOException e) {
throw new RuntimeException
("this should never happen with a StringWriter", e);
}
}
/**
* Helper that returns an &lt;optimize&gt; String with
* optional key/val pairs.
*
* @param args 0 and Even numbered args are params, Odd numbered args are values.
*/
public static String optimize(String... args) {
return simpleTag("optimize", args);
}
public static String simpleTag(String tag, String... args) {
try {
StringWriter writer = new StringWriter();
XML.writeXML(writer, tag, (String) null, (Object[])args);
return writer.getBuffer().toString();
} catch (IOException e) {
throw new RuntimeException
("this should never happen with a StringWriter", e);
}
}
/**
* Helper that returns an &lt;commit&gt; String with
* optional key/val pairs.
*
* @param args 0 and Even numbered args are params, Odd numbered args are values.
*/
public static String commit(String... args) {
return simpleTag("commit", args);
}
/** Reloads the core */
abstract public void reload() throws Exception;
/**
* Processes an "update" (add, commit or optimize) and
* returns the response as a String.
*
* This method does NOT commit after the request.
*
* @param xml The XML of the update
* @return The XML response to the update
*/
abstract public String update(String xml);
/**
* Validates that an "update" (add, commit or optimize) results in success.
*
* :TODO: currently only deals with one add/doc at a time, this will need changed if/when SOLR-2 is resolved
*
* @param xml The XML of the update
* @return null if successful, otherwise the XML response to the update
*/
public String validateUpdate(String xml) throws SAXException {
return checkUpdateStatus(xml, "0");
}
/**
* Validates that an "update" (add, commit or optimize) results in success.
*
* :TODO: currently only deals with one add/doc at a time, this will need changed if/when SOLR-2 is resolved
*
* @param xml The XML of the update
* @return null if successful, otherwise the XML response to the update
*/
public String validateErrorUpdate(String xml) throws SAXException {
try {
return checkUpdateStatus(xml, "1");
} catch (SolrException e) {
// return ((SolrException)e).getMessage();
return null; // success
}
}
/**
* Validates that an "update" (add, commit or optimize) results in success.
*
* :TODO: currently only deals with one add/doc at a time, this will need changed if/when SOLR-2 is resolved
*
* @param xml The XML of the update
* @return null if successful, otherwise the XML response to the update
*/
public String checkUpdateStatus(String xml, String code) throws SAXException {
try {
String res = update(xml);
String valid = validateXPath(res, "//int[@name='status']="+code );
return (null == valid) ? null : res;
} catch (XPathExpressionException e) {
throw new RuntimeException
("?!? static xpath has bug?", e);
}
}
}