/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-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.http;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URI;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.XMLConstants;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.TransformerConfigurationException;
import org.apache.log4j.Logger;
import org.exist.EXistException;
import org.exist.Namespaces;
import org.exist.collections.Collection;
import org.exist.collections.IndexInfo;
import org.exist.collections.triggers.TriggerException;
import org.exist.debuggee.Debuggee;
import org.exist.dom.BinaryDocument;
import org.exist.dom.DefaultDocumentSet;
import org.exist.dom.DocumentImpl;
import org.exist.dom.DocumentMetadata;
import org.exist.dom.MutableDocumentSet;
import org.exist.dom.QName;
import org.exist.dom.XMLUtil;
import org.exist.http.servlets.HttpRequestWrapper;
import org.exist.http.servlets.HttpResponseWrapper;
import org.exist.http.servlets.ResponseWrapper;
import org.exist.memtree.ElementImpl;
import org.exist.memtree.NodeImpl;
import org.exist.memtree.SAXAdapter;
import org.exist.security.Permission;
import org.exist.security.PermissionDeniedException;
import org.exist.security.xacml.AccessContext;
import org.exist.source.DBSource;
import org.exist.source.Source;
import org.exist.source.StringSource;
import org.exist.source.URLSource;
import org.exist.storage.BrokerPool;
import org.exist.storage.DBBroker;
import org.exist.storage.XQueryPool;
import org.exist.storage.lock.Lock;
import org.exist.storage.serializers.EXistOutputKeys;
import org.exist.storage.serializers.Serializer;
import org.exist.storage.serializers.Serializer.HttpContext;
import org.exist.storage.txn.TransactionException;
import org.exist.storage.txn.TransactionManager;
import org.exist.storage.txn.Txn;
import org.exist.util.LockException;
import org.exist.util.MimeTable;
import org.exist.util.MimeType;
import org.exist.util.serializer.SAXSerializer;
import org.exist.util.serializer.SerializerPool;
import org.exist.xmldb.XmldbURI;
import org.exist.xqj.Marshaller;
import org.exist.xquery.CompiledXQuery;
import org.exist.xquery.Constants;
import org.exist.xquery.NameTest;
import org.exist.xquery.XPathException;
import org.exist.xquery.XQuery;
import org.exist.xquery.XQueryContext;
import org.exist.xquery.functions.request.RequestModule;
import org.exist.xquery.functions.response.ResponseModule;
import org.exist.xquery.functions.session.SessionModule;
import org.exist.xquery.value.AnyURIValue;
import org.exist.xquery.value.DateTimeValue;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.SequenceIterator;
import org.exist.xquery.value.Type;
import org.exist.xquery.value.ValueSequence;
import org.exist.xupdate.Modification;
import org.exist.xupdate.XUpdateProcessor;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.XMLFilterImpl;
/**
*
* @author wolf
* @author ljo
* @author adam
* @author gev
*
*/
public class RESTServer {
protected final static Logger LOG = Logger.getLogger(RESTServer.class);
// Should we not obey the instance's defaults? /ljo
protected final static Properties defaultProperties = new Properties();
static {
defaultProperties.setProperty(OutputKeys.INDENT, "yes");
defaultProperties.setProperty(OutputKeys.ENCODING, "UTF-8");
defaultProperties.setProperty(OutputKeys.MEDIA_TYPE, MimeType.XML_TYPE
.getName());
defaultProperties.setProperty(EXistOutputKeys.EXPAND_XINCLUDES, "yes");
defaultProperties.setProperty(EXistOutputKeys.HIGHLIGHT_MATCHES,
"elements");
defaultProperties.setProperty(EXistOutputKeys.PROCESS_XSL_PI, "yes");
}
protected final static Properties defaultOutputKeysProperties = new Properties();
static {
defaultOutputKeysProperties.setProperty(OutputKeys.INDENT, "yes");
defaultOutputKeysProperties.setProperty(OutputKeys.ENCODING, "UTF-8");
defaultOutputKeysProperties.setProperty(OutputKeys.MEDIA_TYPE,
MimeType.XML_TYPE.getName());
}
private final static String QUERY_ERROR_HEAD = "<html>" + "<head>"
+ "<title>Query Error</title>" + "<style type=\"text/css\">"
+ ".errmsg {" + " border: 1px solid black;" + " padding: 15px;"
+ " margin-left: 20px;" + " margin-right: 20px;" + "}"
+ "h1 { color: #C0C0C0; }" + ".path {" + " padding-bottom: 10px;"
+ "}" + ".high { " + " color: #666699; " + " font-weight: bold;"
+ "}" + "</style>" + "</head>" + "<body>" + "<h1>XQuery Error</h1>";
private String formEncoding; // TODO: we may be able to remove this
// eventually, in favour of
// HttpServletRequestWrapper being setup in
// EXistServlet, currently used for doPost()
// but perhaps could be used for other
// Request Methods? - deliriumsky
private String containerEncoding;
private boolean useDynamicContentType;
private boolean saveMode = false;
private SessionManager sessionManager;
// Constructor
public RESTServer(BrokerPool pool, String formEncoding,
String containerEncoding, boolean useDynamicContentType, boolean saveMode) {
this.formEncoding = formEncoding;
this.containerEncoding = containerEncoding;
this.useDynamicContentType = useDynamicContentType;
this.saveMode = saveMode;
this.sessionManager = new SessionManager(pool);
}
/**
* Handle GET request. In the simplest case just returns the document or
* binary resource specified in the path. If the path leads to a collection,
* a listing of the collection contents is returned. If it resolves to a
* binary resource with mime-type "application/xquery", this resource will
* be loaded and executed by the XQuery engine.
*
* The method also recognizes a number of predefined parameters:
*
* <ul>
* <li>_xpath or _query: if specified, the given query is executed on the
* current resource or collection.</li>
*
* <li>_howmany: defines how many items from the query result will be
* returned.</li>
*
* <li>_start: a start offset into the result set.</li>
*
* <li>_wrap: if set to "yes", the query results will be wrapped into a
* exist:result element.</li>
*
* <li>_indent: if set to "yes", the returned XML will be pretty-printed.
* </li>
*
* <li>_source: if set to "yes" and a resource with mime-type
* "application/xquery" is requested then the xquery will not be executed,
* instead the source of the document will be returned. Must be enabled in
* descriptor.xml with the following syntax <xquery-app><allow-source><xquery
* path="/db/mycollection/myquery.xql"/></allow-source></xquery-app> </li>
*
* <li>_xsl: an URI pointing to an XSL stylesheet that will be applied to
* the returned XML.</li>
*
* @param broker
* @param request
* @param response
* @param path
* @throws BadRequestException
* @throws PermissionDeniedException
* @throws NotFoundException
*/
public void doGet(DBBroker broker, HttpServletRequest request,
HttpServletResponse response, String path)
throws BadRequestException, PermissionDeniedException,
NotFoundException, IOException {
// if required, set character encoding
if (request.getCharacterEncoding() == null)
request.setCharacterEncoding(formEncoding);
String option;
if ((option = request.getParameter("_release")) != null) {
int sessionId = Integer.parseInt(option);
sessionManager.release(sessionId);
if (LOG.isDebugEnabled())
LOG.debug("Released session " + sessionId);
response.setStatus(HttpServletResponse.SC_OK);
return;
}
// Process special parameters
int howmany = 10;
int start = 1;
boolean wrap = true;
boolean source = false;
boolean cache = false;
Properties outputProperties = new Properties(
defaultOutputKeysProperties);
String query = null;
if (!saveMode) {
query = request.getParameter("_xpath");
if (query == null)
query = request.getParameter("_query");
}
String _var = request.getParameter("_variables");
List /*<Namespace>*/ namespaces = null;
ElementImpl variables = null;
try {
if (_var != null)
{
NamespaceExtractor nsExtractor = new NamespaceExtractor();
variables = parseXML(_var, nsExtractor);
namespaces = nsExtractor.getNamespaces();
}
} catch (SAXException e) {
XPathException x = new XPathException(e.toString());
writeXPathException(response, HttpServletResponse.SC_BAD_REQUEST, "UTF-8", query, path, x);
} catch (ParserConfigurationException e) {
XPathException x = new XPathException(e.toString());
writeXPathException(response, HttpServletResponse.SC_BAD_REQUEST, "UTF-8", query, path, x);
}
if ((option = request.getParameter("_howmany")) != null) {
try {
howmany = Integer.parseInt(option);
} catch (NumberFormatException nfe) {
throw new BadRequestException(
"Parameter _howmany should be an int");
}
}
if ((option = request.getParameter("_start")) != null) {
try {
start = Integer.parseInt(option);
} catch (NumberFormatException nfe) {
throw new BadRequestException(
"Parameter _start should be an int");
}
}
if ((option = request.getParameter("_wrap")) != null) {
wrap = option.equals("yes");
outputProperties.setProperty("_wrap", option);
}
if ((option = request.getParameter("_cache")) != null) {
cache = option.equals("yes");
}
if ((option = request.getParameter("_indent")) != null) {
outputProperties.setProperty(OutputKeys.INDENT, option);
}
if ((option = request.getParameter("_source")) != null && !saveMode) {
source = option.equals("yes");
}
if ((option = request.getParameter("_session")) != null) {
outputProperties
.setProperty(Serializer.PROPERTY_SESSION_ID, option);
}
String stylesheet;
if ((stylesheet = request.getParameter("_xsl")) != null) {
if (stylesheet.equals("no")) {
outputProperties.setProperty(EXistOutputKeys.PROCESS_XSL_PI,
"no");
outputProperties.remove(EXistOutputKeys.STYLESHEET);
stylesheet = null;
} else {
outputProperties.setProperty(EXistOutputKeys.STYLESHEET,
stylesheet);
}
} else {
outputProperties.setProperty(EXistOutputKeys.PROCESS_XSL_PI, "yes");
}
LOG.debug("stylesheet = " + stylesheet);
LOG.debug("query = " + query);
String encoding;
if ((encoding = request.getParameter("_encoding")) != null)
outputProperties.setProperty(OutputKeys.ENCODING, encoding);
else
encoding = "UTF-8";
String mimeType = outputProperties.getProperty(OutputKeys.MEDIA_TYPE);
if (query != null) {
// query parameter specified, search method does all the rest of the
// work
try {
search(broker, query, path, null, variables, howmany, start, outputProperties,
wrap, cache, request, response);
} catch (XPathException e) {
if (MimeType.XML_TYPE.getName().equals(mimeType)) {
writeXPathException(response, HttpServletResponse.SC_BAD_REQUEST, encoding, query, path, e);
} else {
writeXPathExceptionHtml(response, HttpServletResponse.SC_BAD_REQUEST, encoding, query, path, e);
}
}
return;
}
// Process the request
DocumentImpl resource = null;
XmldbURI pathUri = XmldbURI.create( URLDecoder.decode( path , "UTF-8" ) );
try {
// check if path leads to an XQuery resource
String xquery_mime_type = MimeType.XQUERY_TYPE.getName();
String xproc_mime_type = MimeType.XPROC_TYPE.getName();
resource = broker.getXMLResource(pathUri, Lock.READ_LOCK);
if (null != resource && !isExecutableType(resource)) {
// return regular resource that is not an xquery and not is xproc
writeResourceAs(resource, broker, stylesheet, encoding, null,
outputProperties, request, response);
return;
}
if (resource == null) { // could be request for a Collection
// no document: check if path points to a collection
Collection collection = broker.getCollection(pathUri);
if (collection != null) {
if (saveMode || !collection.getPermissions().validate(broker.getUser(),
Permission.READ))
throw new PermissionDeniedException(
"Not allowed to read collection");
// return a listing of the collection contents
writeCollection(response, encoding, broker, collection);
return;
} else if (source) {
// didn't find regular resource, or user wants source
// on a possible xquery resource that was not found
throw new NotFoundException("Document " + path
+ " not found");
}
}
XmldbURI servletPath = pathUri;
// if resource is still null, work up the url path to find an
// xquery or xproc resource
while (null == resource) {
// traverse up the path looking for xquery objects
servletPath = servletPath.removeLastSegment();
if (servletPath == XmldbURI.EMPTY_URI)
break;
resource = broker.getXMLResource(servletPath, Lock.READ_LOCK);
if (null != resource && isExecutableType(resource)) {
break;
} else if (null != resource) {
// not an xquery resource. This means we have a path
// that cannot contain an xquery object even if we keep
// moving up the path, so bail out now
throw new NotFoundException("Document " + path
+ " not found");
}
}
if (null == resource) { // path search failed
throw new NotFoundException("Document " + path + " not found");
}
// found an XQuery or XProc resource, fixup request values
String pathInfo = pathUri.trimFromBeginning(servletPath).toString();
// Should we display the source of the XQuery or XProc or execute it
Descriptor descriptor = Descriptor.getDescriptorSingleton();
if (source) {
// show the source
// check are we allowed to show the xquery source -
// descriptor.xml
if ((null != descriptor) && descriptor.allowSource(path)) {
// TODO: change writeResourceAs to use a serializer
// that will serialize xquery to syntax coloured
// xhtml, replace the asMimeType parameter with a
// method for specifying the serializer, or split
// the code into two methods. - deliriumsky
if (xquery_mime_type.equals(resource.getMetadata().getMimeType())) {
// Show the source of the XQuery
writeResourceAs(resource, broker, stylesheet, encoding,
MimeType.TEXT_TYPE.getName(), outputProperties,
request, response);
} else if (xproc_mime_type.equals(resource.getMetadata().getMimeType())) {
// Show the source of the XProc
writeResourceAs(resource, broker, stylesheet, encoding,
MimeType.XML_TYPE.getName(), outputProperties,
request, response);
}
} else {
// we are not allowed to show the source - query not
// allowed in descriptor.xml
// or descriptor not found, so assume source view not
// allowed
response
.sendError(
HttpServletResponse.SC_FORBIDDEN,
"Permission to view XQuery source for: "
+ path
+ " denied. Must be explicitly defined in descriptor.xml");
return;
}
} else {
try {
if (xquery_mime_type.equals(resource.getMetadata().getMimeType())){
// Execute the XQuery
executeXQuery(broker, resource, request, response,
outputProperties, servletPath.toString(), pathInfo);
} else if (xproc_mime_type.equals(resource.getMetadata().getMimeType())) {
// Execute the XProc
executeXProc(broker, resource, request, response,
outputProperties, servletPath.toString(), pathInfo);
}
} catch (XPathException e) {
if (LOG.isDebugEnabled())
LOG.debug(e.getMessage(), e);
if (MimeType.XML_TYPE.getName().equals(mimeType)) {
writeXPathException(response, HttpServletResponse.SC_BAD_REQUEST, encoding, query, path, e);
} else {
writeXPathExceptionHtml(response, HttpServletResponse.SC_BAD_REQUEST, encoding, query,
path, e);
}
}
}
} finally {
if (resource != null)
resource.getUpdateLock().release(Lock.READ_LOCK);
}
}
public void doHead(DBBroker broker, HttpServletRequest request, HttpServletResponse response, String path) throws BadRequestException, PermissionDeniedException, NotFoundException, IOException
{
Properties outputProperties = new Properties(defaultOutputKeysProperties);
String mimeType = outputProperties.getProperty(OutputKeys.MEDIA_TYPE);
String encoding;
if ((encoding = request.getParameter("_encoding")) != null)
outputProperties.setProperty(OutputKeys.ENCODING, encoding);
else
encoding = "UTF-8";
DocumentImpl resource = null;
XmldbURI pathUri = XmldbURI.create(path);
try
{
resource = broker.getXMLResource(pathUri, Lock.READ_LOCK);
if(resource != null)
{
if (!resource.getPermissions().validate(broker.getUser(),
Permission.READ)) {
throw new PermissionDeniedException(
"Permission to read resource " + path + " denied");
}
DocumentMetadata metadata = resource.getMetadata();
response.setContentType(metadata.getMimeType());
response.setContentLength(resource.getContentLength());
response.addDateHeader("Last-Modified", metadata.getLastModified());
response.addDateHeader("Created", metadata.getCreated());
}
else
{
Collection col = broker.getCollection(pathUri);
//no resource or collection
if(col == null)
{
response.sendError(HttpServletResponse.SC_NOT_FOUND, "No resource at location: " + path);
return;
}
if(!col.getPermissions().validate(broker.getUser(), Permission.READ))
{
throw new PermissionDeniedException(
"Permission to read resource " + path + " denied");
}
response.setContentType(MimeType.XML_TYPE.getName() + "; charset=" + encoding);
response.addDateHeader("Last-Modified", col.getCreationTime());
response.addDateHeader("Created", col.getCreationTime());
}
}
finally
{
if (resource != null)
resource.getUpdateLock().release(Lock.READ_LOCK);
}
}
/**
* Handles POST requests. If the path leads to a binary resource with
* mime-type "application/xquery", that resource will be read and executed
* by the XQuery engine. Otherwise, the request content is loaded and parsed
* as XML. It may either contain an XUpdate or a query request.
*
* @param broker
* @param request
* @param response
* @param path
* @throws BadRequestException
* @throws PermissionDeniedException
*/
public void doPost(DBBroker broker, HttpServletRequest request,
HttpServletResponse response, String path)
throws BadRequestException, PermissionDeniedException, IOException {
// if required, set character encoding
if (request.getCharacterEncoding() == null)
request.setCharacterEncoding(formEncoding);
Properties outputProperties = new Properties(
defaultOutputKeysProperties);
XmldbURI pathUri = XmldbURI.create(path);
DocumentImpl resource = null;
String encoding = outputProperties.getProperty(OutputKeys.ENCODING);
String mimeType = outputProperties.getProperty(OutputKeys.MEDIA_TYPE);
try {
// check if path leads to an XQuery resource.
// if yes, the resource is loaded and the XQuery executed.
String xquery_mime_type = MimeType.XQUERY_TYPE.getName();
String xproc_mime_type = MimeType.XPROC_TYPE.getName();
resource = broker.getXMLResource(pathUri, Lock.READ_LOCK);
XmldbURI servletPath = pathUri;
// if resource is still null, work up the url path to find an
// xquery resource
while (null == resource) {
// traverse up the path looking for xquery objects
servletPath = servletPath.removeLastSegment();
if (servletPath == XmldbURI.EMPTY_URI)
break;
resource = broker.getXMLResource(servletPath, Lock.READ_LOCK);
if (null != resource
&& (resource.getResourceType() == DocumentImpl.BINARY_FILE
&& xquery_mime_type.equals(resource.getMetadata().getMimeType()) ||
resource.getResourceType() == DocumentImpl.XML_FILE
&& xproc_mime_type.equals(resource.getMetadata().getMimeType()))
) {
break; // found a binary file with mime-type xquery or XML file with mime-type xproc
} else if (null != resource) {
// not an xquery or xproc resource. This means we have a path
// that cannot contain an xquery or xproc object even if we keep
// moving up the path, so bail out now
resource.getUpdateLock().release(Lock.READ_LOCK);
resource = null;
break;
}
}
if (resource != null) {
if (resource.getResourceType() == DocumentImpl.BINARY_FILE
&& xquery_mime_type.equals(resource.getMetadata().getMimeType()) ||
resource.getResourceType() == DocumentImpl.XML_FILE
&& xproc_mime_type.equals(resource.getMetadata().getMimeType())
) {
// found an XQuery resource, fixup request values
String pathInfo = pathUri.trimFromBeginning(servletPath).toString();
try {
if (xquery_mime_type.equals(resource.getMetadata().getMimeType())){
// Execute the XQuery
executeXQuery(broker, resource, request, response,
outputProperties, servletPath.toString(), pathInfo);
} else {
// Execute the XProc
executeXProc(broker, resource, request, response,
outputProperties, servletPath.toString(), pathInfo);
}
} catch (XPathException e) {
if (MimeType.XML_TYPE.getName().equals(mimeType)) {
writeXPathException(response, HttpServletResponse.SC_BAD_REQUEST, encoding, null, path,
e);
} else {
writeXPathExceptionHtml(response, HttpServletResponse.SC_BAD_REQUEST, encoding, null,
path, e);
}
}
return;
}
}
} finally {
if (resource != null)
resource.getUpdateLock().release(Lock.READ_LOCK);
}
// third, normal POST: read the request content and check if
// it is an XUpdate or a query request.
int howmany = 10;
int start = 1;
ElementImpl variables = null;
boolean enclose = true;
boolean cache = false;
String mime = MimeType.XML_TYPE.getName();
String query = null;
TransactionManager transact = broker.getBrokerPool()
.getTransactionManager();
Txn transaction = transact.beginTransaction();
try {
String content = getRequestContent(request);
NamespaceExtractor nsExtractor = new NamespaceExtractor();
Element root = parseXML(content, nsExtractor);
String rootNS = root.getNamespaceURI();
if (rootNS != null && rootNS.equals(Namespaces.EXIST_NS)) {
if (root.getLocalName().equals("query")) {
// process <query>xpathQuery</query>
String option = root.getAttribute("start");
if (option != null)
try {
start = Integer.parseInt(option);
} catch (NumberFormatException e) {
}
option = root.getAttribute("max");
if (option != null)
try {
howmany = Integer.parseInt(option);
} catch (NumberFormatException e) {
}
option = root.getAttribute("enclose");
if (option != null) {
if (option.equals("no"))
enclose = false;
}
option = root.getAttribute("mime");
mime = MimeType.XML_TYPE.getName();
if ((option != null) && (!option.equals(""))) {
mime = option;
}
if ((option = root.getAttribute("cache")) != null) {
cache = option.equals("yes");
}
if ((option = root.getAttribute("session")) != null
&& option.length() > 0) {
outputProperties.setProperty(
Serializer.PROPERTY_SESSION_ID, option);
}
NodeList children = root.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE
&& child.getNamespaceURI().equals(
Namespaces.EXIST_NS)) {
if (child.getLocalName().equals("text")) {
StringBuilder buf = new StringBuilder();
Node next = child.getFirstChild();
while (next != null) {
if (next.getNodeType() == Node.TEXT_NODE
|| next.getNodeType() == Node.CDATA_SECTION_NODE)
buf.append(next.getNodeValue());
next = next.getNextSibling();
}
query = buf.toString();
} else if (child.getLocalName().equals("variables")) {
variables = (ElementImpl) child;
} else if (child.getLocalName().equals("properties")) {
Node node = child.getFirstChild();
while (node != null) {
if (node.getNodeType() == Node.ELEMENT_NODE
&& node.getNamespaceURI().equals(
Namespaces.EXIST_NS)
&& node.getLocalName().equals(
"property")) {
Element property = (Element) node;
String key = property
.getAttribute("name");
String value = property
.getAttribute("value");
LOG.debug(key + " = " + value);
if (key != null && value != null)
outputProperties.setProperty(key,
value);
}
node = node.getNextSibling();
}
}
}
}
}
// execute query
if (query != null) {
String result;
try {
search(broker, query, path, nsExtractor.getNamespaces(), variables, howmany, start,
outputProperties, enclose, cache, request,
response);
transact.commit(transaction);
} catch (XPathException e) {
result = e.getMessage();
if (MimeType.XML_TYPE.getName().equals(mimeType)) {
writeXPathException(response, HttpServletResponse.SC_ACCEPTED, encoding, null, path,
e);
} else {
writeXPathExceptionHtml(response, HttpServletResponse.SC_ACCEPTED, encoding, null,
path, e);
}
}
} else {
transact.abort(transaction);
throw new BadRequestException("No query specified");
}
} else if (rootNS != null
&& rootNS.equals(XUpdateProcessor.XUPDATE_NS)) {
LOG.debug("Got xupdate request: " + content);
MutableDocumentSet docs = new DefaultDocumentSet();
Collection collection = broker.getCollection(pathUri);
if (collection != null) {
collection.allDocs(broker, docs, true, true);
} else {
DocumentImpl xupdateDoc = (DocumentImpl) broker
.getXMLResource(pathUri);
if (xupdateDoc != null) {
if (!xupdateDoc.getPermissions().validate(
broker.getUser(), Permission.READ)) {
transact.abort(transaction);
throw new PermissionDeniedException(
"Not allowed to read collection");
}
docs.add(xupdateDoc);
} else
broker.getAllXMLResources(docs);
}
XUpdateProcessor processor = new XUpdateProcessor(broker, docs,
AccessContext.REST);
Modification modifications[] = processor.parse(new InputSource(
new StringReader(content)));
long mods = 0;
for (int i = 0; i < modifications.length; i++) {
mods += modifications[i].process(transaction);
broker.flush();
}
transact.commit(transaction);
// FD : Returns an XML doc
writeXUpdateResult(response, encoding, mods);
// END FD
} else {
transact.abort(transaction);
throw new BadRequestException("Unknown XML root element: "
+ root.getNodeName());
}
} catch (SAXException e) {
transact.abort(transaction);
Exception cause = e;
if (e.getException() != null)
cause = e.getException();
LOG.debug("SAX exception while parsing request: "
+ cause.getMessage(), cause);
throw new BadRequestException(
"SAX exception while parsing request: "
+ cause.getMessage());
} catch (ParserConfigurationException e) {
transact.abort(transaction);
throw new BadRequestException(
"Parser exception while parsing request: " + e.getMessage());
} catch (XPathException e) {
transact.abort(transaction);
throw new BadRequestException(
"Query exception while parsing request: " + e.getMessage());
} catch (IOException e) {
transact.abort(transaction);
throw new BadRequestException(
"IO exception while parsing request: " + e.getMessage());
} catch (EXistException e) {
transact.abort(transaction);
throw new BadRequestException(e.getMessage());
} catch (LockException e) {
transact.abort(transaction);
throw new PermissionDeniedException(e.getMessage());
}
}
private ElementImpl parseXML(String content, NamespaceExtractor nsExtractor) throws ParserConfigurationException, SAXException, IOException
{
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
InputSource src = new InputSource(new StringReader(content));
SAXParser parser = factory.newSAXParser();
XMLReader reader = parser.getXMLReader();
SAXAdapter adapter = new SAXAdapter();
//reader.setContentHandler(adapter);
//reader.parse(src);
nsExtractor.setContentHandler(adapter);
nsExtractor.setParent(reader);
nsExtractor.parse(src);
Document doc = adapter.getDocument();
return (ElementImpl) doc.getDocumentElement();
}
private class NamespaceExtractor extends XMLFilterImpl
{
List namespaces = new ArrayList(); //<Namespace>
public void startPrefixMapping(String prefix, String uri) throws SAXException
{
if (!Namespaces.EXIST_NS.equals(uri)) {
Namespace ns = new Namespace(prefix, uri);
namespaces.add(ns);
}
super.startPrefixMapping(prefix, uri);
}
public List getNamespaces()
{
return namespaces;
}
}
private class Namespace
{
private String prefix = null;
private String uri = null;
public Namespace(String prefix, String uri)
{
this.prefix = prefix;
this.uri = uri;
}
public String getPrefix() {
return prefix;
}
public String getUri() {
return uri;
}
}
/**
* Creates an input source from a URL location with an optional known
* charset.
*/
private InputSource createInputSource(String charset, URI location)
throws java.io.IOException {
if (charset == null) {
return new InputSource(location.toASCIIString());
} else {
InputSource source = new InputSource(new InputStreamReader(location
.toURL().openStream(), charset));
source.setSystemId(location.toASCIIString());
return source;
}
}
/**
* Handles PUT requests. The request content is stored as a new resource at
* the specified location. If the resource already exists, it is overwritten
* if the user has write permissions.
*
* The resource type depends on the content type specified in the HTTP
* header. The content type will be looked up in the global mime table. If
* the corresponding mime type is not a know XML mime type, the resource
* will be stored as a binary resource.
*
* @param broker
* @param tempFile
* The temp file from which the PUT will get its content
* @param path
* The path to which the file should be stored
* @param request
* @param response
* @throws BadRequestException
* @throws PermissionDeniedException
*/
public void doPut(DBBroker broker, File tempFile, XmldbURI path,
HttpServletRequest request, HttpServletResponse response)
throws BadRequestException, PermissionDeniedException, IOException {
if (tempFile == null)
throw new BadRequestException("No request content found for PUT");
TransactionManager transact = broker.getBrokerPool()
.getTransactionManager();
Txn transaction = transact.beginTransaction();
try {
XmldbURI docUri = path.lastSegment();
XmldbURI collUri = path.removeLastSegment();
if (docUri == null || collUri == null) {
transact.abort(transaction);
throw new BadRequestException("Bad path: " + path);
}
// TODO : use getOrCreateCollection() right now ?
Collection collection = broker.getCollection(collUri);
if (collection == null) {
LOG.debug("creating collection " + collUri);
collection = broker.getOrCreateCollection(transaction, collUri);
broker.saveCollection(transaction, collection);
}
MimeType mime;
String contentType = request.getContentType();
String charset = null;
if (contentType != null) {
int semicolon = contentType.indexOf(';');
if (semicolon > 0) {
contentType = contentType.substring(0, semicolon).trim();
int equals = contentType.indexOf('=', semicolon);
if (equals > 0) {
String param = contentType.substring(semicolon + 1,
equals).trim();
if (param.compareToIgnoreCase("charset=") == 0) {
charset = param.substring(equals + 1).trim();
}
}
}
mime = MimeTable.getInstance().getContentType(contentType);
} else {
mime = MimeTable.getInstance().getContentTypeFor(docUri);
if (mime != null)
contentType = mime.getName();
}
if (mime == null)
mime = MimeType.BINARY_TYPE;
if (mime.isXMLType()) {
URI url = tempFile.toURI();
IndexInfo info = collection.validateXMLResource(transaction,
broker, docUri, createInputSource(charset, url));
info.getDocument().getMetadata().setMimeType(contentType);
collection.store(transaction, broker, info, createInputSource(
charset, url), false);
response.setStatus(HttpServletResponse.SC_CREATED);
} else {
FileInputStream is = new FileInputStream(tempFile);
collection.addBinaryResource(transaction, broker, docUri, is,
contentType, (int) tempFile.length());
is.close();
response.setStatus(HttpServletResponse.SC_CREATED);
}
transact.commit(transaction);
} catch (SAXParseException e) {
transact.abort(transaction);
throw new BadRequestException("Parsing exception at "
+ e.getLineNumber() + "/" + e.getColumnNumber() + ": "
+ e.toString());
} catch (TriggerException e) {
transact.abort(transaction);
throw new PermissionDeniedException(e.getMessage());
} catch (SAXException e) {
transact.abort(transaction);
Exception o = e.getException();
if (o == null)
o = e;
throw new BadRequestException("Parsing exception: "
+ o.getMessage());
} catch (EXistException e) {
transact.abort(transaction);
throw new BadRequestException("Internal error: " + e.getMessage());
} catch (LockException e) {
transact.abort(transaction);
throw new PermissionDeniedException(e.getMessage());
}
return;
}
public void doDelete(DBBroker broker, XmldbURI path,
HttpServletResponse response) throws PermissionDeniedException,
NotFoundException, IOException {
TransactionManager transact = broker.getBrokerPool()
.getTransactionManager();
Txn txn = transact.beginTransaction();
try {
Collection collection = broker.getCollection(path);
if (collection != null) {
// remove the collection
LOG.debug("removing collection " + path);
broker.removeCollection(txn, collection);
response.setStatus(HttpServletResponse.SC_OK);
} else {
DocumentImpl doc = (DocumentImpl) broker.getXMLResource(path);
if (doc == null) {
transact.abort(txn);
throw new NotFoundException(
"No document or collection found " + "for path: "
+ path);
} else {
// remove the document
LOG.debug("removing document " + path);
if (doc.getResourceType() == DocumentImpl.BINARY_FILE)
doc.getCollection().removeBinaryResource(txn, broker,
path.lastSegment());
else
doc.getCollection().removeXMLResource(txn, broker,
path.lastSegment());
response.setStatus(HttpServletResponse.SC_OK);
}
}
transact.commit(txn);
} catch (TriggerException e) {
transact.abort(txn);
throw new PermissionDeniedException("Trigger failed: "
+ e.getMessage());
} catch (LockException e) {
transact.abort(txn);
throw new PermissionDeniedException("Could not acquire lock: "
+ e.getMessage());
} catch (TransactionException e) {
transact.abort(txn);
LOG.warn("Transaction aborted: " + e.getMessage(), e);
}
}
private String getRequestContent(HttpServletRequest request)
throws IOException {
String encoding = request.getCharacterEncoding();
if (encoding == null)
encoding = "UTF-8";
InputStream is = request.getInputStream();
Reader reader = new InputStreamReader(is, encoding);
StringWriter content = new StringWriter();
char ch[] = new char[4096];
int len = 0;
while ((len = reader.read(ch)) > -1)
content.write(ch, 0, len);
String xml = content.toString();
return xml;
}
/**
* TODO: pass request and response objects to XQuery.
*
* @throws XPathException
*/
protected void search(DBBroker broker, String query, String path,
List/*<Namespace>*/ namespaces, ElementImpl variables, int howmany, int start, Properties outputProperties, boolean wrap,
boolean cache, HttpServletRequest request,
HttpServletResponse response) throws BadRequestException,
PermissionDeniedException, XPathException {
String sessionIdParam = outputProperties
.getProperty(Serializer.PROPERTY_SESSION_ID);
if (sessionIdParam != null) {
try {
int sessionId = Integer.parseInt(sessionIdParam);
if (sessionId > -1) {
Sequence cached = sessionManager.get(query, sessionId);
if (cached != null) {
LOG.debug("Returning cached query result");
writeResults(response, broker, cached, howmany, start,
outputProperties, wrap);
} else {
LOG
.debug("Cached query result not found. Probably timed out. Repeating query.");
}
}
} catch (NumberFormatException e) {
throw new BadRequestException(
"Invalid session id passed in query request: "
+ sessionIdParam);
}
}
XmldbURI pathUri = XmldbURI.create(path);
try {
Source source = new StringSource(query);
XQuery xquery = broker.getXQueryService();
XQueryPool pool = xquery.getXQueryPool();
CompiledXQuery compiled = pool.borrowCompiledXQuery(broker, source);
XQueryContext context;
if (compiled == null)
context = xquery.newContext(AccessContext.REST);
else
context = compiled.getContext();
context.setStaticallyKnownDocuments(new XmldbURI[] { pathUri });
context.setBaseURI(new AnyURIValue(pathUri.toString()));
declareNamespaces(context, namespaces);
declareVariables(context, variables, request, response);
if (compiled == null)
compiled = xquery.compile(context, source);
else {
compiled.getContext().updateContext(context);
context.getWatchDog().reset();
}
try {
long startTime = System.currentTimeMillis();
Sequence resultSequence = xquery.execute(compiled, null, outputProperties);
long queryTime = System.currentTimeMillis() - startTime;
if (LOG.isDebugEnabled())
LOG.debug("Found " + resultSequence.getItemCount() + " in "
+ queryTime + "ms.");
if (cache) {
int sessionId = sessionManager.add(query, resultSequence);
outputProperties.setProperty(
Serializer.PROPERTY_SESSION_ID, Integer
.toString(sessionId));
if (!response.isCommitted())
response.setIntHeader("X-Session-Id", sessionId);
}
writeResults(response, broker, resultSequence, howmany, start,
outputProperties, wrap);
} finally {
pool.returnCompiledXQuery(source, compiled);
}
} catch (IOException e) {
throw new BadRequestException(e.getMessage(), e);
}
}
private void declareNamespaces(XQueryContext context, List/*<Namespace>*/ namespaces) throws XPathException
{
if(namespaces == null)
return;
for(int i = 0; i < namespaces.size(); i++)
{
Namespace ns = (Namespace)namespaces.get(i);
context.declareNamespace(ns.getPrefix(), ns.getUri());
}
}
/**
* Pass the request, response and session objects to the XQuery context.
*
* @param context
* @param request
* @param response
* @throws XPathException
*/
private HttpRequestWrapper declareVariables(XQueryContext context, ElementImpl variables,
HttpServletRequest request, HttpServletResponse response)
throws XPathException {
HttpRequestWrapper reqw = new HttpRequestWrapper(request, formEncoding,
containerEncoding);
ResponseWrapper respw = new HttpResponseWrapper(response);
// context.declareNamespace(RequestModule.PREFIX,
// RequestModule.NAMESPACE_URI);
context.declareVariable(RequestModule.PREFIX + ":request", reqw);
context.declareVariable(ResponseModule.PREFIX + ":response", respw);
context.declareVariable(SessionModule.PREFIX + ":session", reqw.getSession( false ));
if(variables != null){
declareExternalAndXQJVariables(context, variables);
}
return reqw;
}
private void declareExternalAndXQJVariables(XQueryContext context, ElementImpl variables) throws XPathException {
ValueSequence varSeq = new ValueSequence();
variables.selectChildren(new NameTest(Type.ELEMENT, new QName("variable", Namespaces.EXIST_NS)), varSeq);
for (SequenceIterator i = varSeq.iterate(); i.hasNext(); ) {
ElementImpl variable = (ElementImpl) i.nextItem();
// get the QName of the variable
ElementImpl qname = (ElementImpl) variable.getFirstChild(new NameTest(Type.ELEMENT, new QName("qname", Namespaces.EXIST_NS)));
String localname = null, prefix = null, uri = null;
NodeImpl child = (NodeImpl) qname.getFirstChild();
while (child != null) {
if (child.getLocalName().equals("localname")) {
localname = child.getStringValue();
} else if (child.getLocalName().equals("namespace")) {
uri = child.getStringValue();
} else if (child.getLocalName().equals("prefix")) {
prefix = child.getStringValue();
}
child = (NodeImpl) child.getNextSibling();
}
if (uri != null && prefix != null)
context.declareNamespace(prefix, uri);
if (localname == null)
continue;
QName q;
if(prefix != null && localname != null) {
q = new QName(localname, uri, prefix);
} else {
q = new QName(localname, uri, XMLConstants.DEFAULT_NS_PREFIX);
}
// get serialized sequence
NodeImpl value = variable.getFirstChild(new NameTest(Type.ELEMENT, Marshaller.ROOT_ELEMENT_QNAME));
Sequence sequence;
try {
sequence = value == null ? Sequence.EMPTY_SEQUENCE : Marshaller.demarshall(value);
} catch (XMLStreamException xe) {
throw new XPathException(xe.toString());
}
// now declare variable
if(prefix != null) {
context.declareVariable(q.getPrefix() + ":" + q.getLocalName(), sequence);
} else {
context.declareVariable(q.getLocalName(), sequence);
}
}
}
/**
* Directly execute an XQuery stored as a binary document in the database.
*/
private void executeXQuery(DBBroker broker, DocumentImpl resource,
HttpServletRequest request, HttpServletResponse response,
Properties outputProperties, String servletPath, String pathInfo)
throws XPathException, BadRequestException {
Source source = new DBSource(broker, (BinaryDocument) resource, true);
XQuery xquery = broker.getXQueryService();
XQueryPool pool = xquery.getXQueryPool();
XQueryContext context;
CompiledXQuery compiled = pool.borrowCompiledXQuery(broker, source);
if (compiled == null) {
// special header to indicate that the query is not returned from
// cache
response.setHeader("X-XQuery-Cached", "false");
context = xquery.newContext(AccessContext.REST);
} else {
response.setHeader("X-XQuery-Cached", "true");
context = compiled.getContext();
}
// TODO: don't hardcode this?
context.setModuleLoadPath(XmldbURI.EMBEDDED_SERVER_URI.append(
resource.getCollection().getURI()).toString());
context.setStaticallyKnownDocuments(new XmldbURI[] { resource
.getCollection().getURI() });
HttpRequestWrapper reqw = declareVariables(context, null, request, response);
reqw.setServletPath(servletPath);
reqw.setPathInfo(pathInfo);
if (compiled == null) {
try {
compiled = xquery.compile(context, source);
} catch (IOException e) {
throw new BadRequestException("Failed to read query from "
+ resource.getURI(), e);
}
}
String xdebug = request.getParameter("XDEBUG_SESSION_START");
if (xdebug != null)
compiled.getContext().setDebugMode(true);
else {
//if have session
xdebug = request.getParameter("XDEBUG_SESSION");
if (xdebug != null) {
compiled.getContext().setDebugMode(true);
} else {
//looking for session in cookies (FF XDebug Helper add-ons)
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
if (cookies[i].getName().equals("XDEBUG_SESSION")) {
//TODO: check for value?? ("eXistDB_XDebug" ? or leave "default") -shabanovd
compiled.getContext().setDebugMode(true);
break;
}
}
}
}
}
boolean wrap = outputProperties.getProperty("_wrap") != null &&
outputProperties.getProperty("_wrap").equals("yes");
try {
Sequence result = xquery.execute(compiled, null, outputProperties);
writeResults(response, broker, result, -1, 1, outputProperties, wrap);
} finally {
pool.returnCompiledXQuery(source, compiled);
}
}
/**
* Directly execute an XProc stored as a XML document in the database.
*/
private void executeXProc(DBBroker broker, DocumentImpl resource,
HttpServletRequest request, HttpServletResponse response,
Properties outputProperties, String servletPath, String pathInfo)
throws XPathException, BadRequestException {
URLSource source = new URLSource(this.getClass().getResource("run-xproc.xq"));
XQuery xquery = broker.getXQueryService();
XQueryPool pool = xquery.getXQueryPool();
XQueryContext context;
CompiledXQuery compiled = pool.borrowCompiledXQuery(broker, source);
if (compiled == null) {
context = xquery.newContext(AccessContext.REST);
} else {
context = compiled.getContext();
}
context.declareVariable("pipeline", resource.getURI().toString());
String stdin = request.getParameter("stdin");
context.declareVariable("stdin", stdin == null ? "" : stdin);
String debug = request.getParameter("debug");
context.declareVariable("debug", debug == null ? "0" : debug);
// TODO: don't hardcode this?
context.setModuleLoadPath(XmldbURI.EMBEDDED_SERVER_URI.append(
resource.getCollection().getURI()).toString());
context.setStaticallyKnownDocuments(new XmldbURI[] { resource
.getCollection().getURI() });
HttpRequestWrapper reqw = declareVariables(context, null, request, response);
reqw.setServletPath(servletPath);
reqw.setPathInfo(pathInfo);
if (compiled == null) {
try {
compiled = xquery.compile(context, source);
} catch (IOException e) {
throw new BadRequestException("Failed to read query from "
+ source.getURL(), e);
}
}
try {
Sequence result = xquery.execute(compiled, null, outputProperties);
writeResults(response, broker, result, -1, 1, outputProperties, false);
} finally {
pool.returnCompiledXQuery(source, compiled);
}
}
// writes out a resource, uses asMimeType as the specified mime-type or if
// null uses the type of the resource
private void writeResourceAs(DocumentImpl resource, DBBroker broker,
String stylesheet, String encoding, String asMimeType,
Properties outputProperties, HttpServletRequest request, HttpServletResponse response)
throws BadRequestException, PermissionDeniedException, IOException {
// Do we have permission to read the resource
if (!resource.getPermissions().validate(broker.getUser(),
Permission.READ)) {
throw new PermissionDeniedException("Not allowed to read resource");
}
DocumentMetadata metadata = resource.getMetadata();
response.addDateHeader("Last-Modified", metadata.getLastModified());
response.addDateHeader("Created", metadata.getCreated());
if (resource.getResourceType() == DocumentImpl.BINARY_FILE) {
// binary resource
if (asMimeType == null) { // wasn't a mime-type specified?
asMimeType = resource.getMetadata().getMimeType();
}
if (asMimeType.startsWith("text/")){
response.setContentType(asMimeType + "; charset="
+ encoding);
} else {
response.setContentType(asMimeType);
}
OutputStream os = response.getOutputStream();
broker.readBinaryResource((BinaryDocument) resource, os);
os.flush();
} else {
// xml resource
SAXSerializer sax = null;
Serializer serializer = broker.getSerializer();
serializer.reset();
//setup the http context
HttpContext httpContext = serializer.new HttpContext();
HttpRequestWrapper reqw = new HttpRequestWrapper(request, formEncoding, containerEncoding);
httpContext.setRequest(reqw);
httpContext.setSession(reqw.getSession(false));
serializer.setHttpContext(httpContext);
// Serialize the document
try {
sax = (SAXSerializer) SerializerPool.getInstance()
.borrowObject(SAXSerializer.class);
// use a stylesheet if specified in query parameters
if (stylesheet != null) {
serializer.setStylesheet(resource, stylesheet);
}
serializer.setProperties(outputProperties);
serializer.prepareStylesheets(resource);
if (asMimeType != null) { // was a mime-type specified?
response.setContentType(asMimeType + "; charset="
+ encoding);
} else {
if (serializer.isStylesheetApplied()
|| serializer.hasXSLPi(resource) != null) {
asMimeType = serializer.getStylesheetProperty(OutputKeys.MEDIA_TYPE);
if (!useDynamicContentType || asMimeType == null)
asMimeType = MimeType.HTML_TYPE.getName();
LOG.debug("media-type: " + asMimeType);
response.setContentType(asMimeType + "; charset="
+ encoding);
} else {
asMimeType = resource.getMetadata().getMimeType();
response.setContentType(asMimeType + "; charset="
+ encoding);
}
}
if (asMimeType.equals(MimeType.HTML_TYPE.getName())) {
outputProperties.setProperty("method", "xhtml");
outputProperties.setProperty("media-type", "text/html; charset="
+ encoding);
outputProperties.setProperty("ident", "yes");
outputProperties.setProperty("omit-xml-declaration", "no");
}
OutputStreamWriter writer = new OutputStreamWriter(response
.getOutputStream(), encoding);
sax.setOutput(writer, outputProperties);
serializer.setSAXHandlers(sax, sax);
serializer.toSAX(resource);
writer.flush();
writer.close();
} catch (SAXException saxe) {
LOG.warn(saxe);
throw new BadRequestException("Error while serializing XML: "
+ saxe.getMessage());
} catch (TransformerConfigurationException e) {
LOG.warn(e);
throw new BadRequestException(e.getMessageAndLocation());
} finally {
if (sax != null) {
SerializerPool.getInstance().returnObject(sax);
}
}
}
}
/**
* @param response
* @param encoding
* @param query
* @param path
* @param e
*
*/
private void writeXPathExceptionHtml(HttpServletResponse response, int httpStatusCode,
String encoding, String query, String path, XPathException e)
throws IOException {
if( !response.isCommitted() ) {
response.reset();
}
response.setStatus(httpStatusCode);
response.setContentType(MimeType.HTML_TYPE.getName() + "; charset="
+ encoding);
OutputStreamWriter writer = new OutputStreamWriter(response
.getOutputStream(), encoding);
writer.write(QUERY_ERROR_HEAD);
writer.write("<p class=\"path\"><span class=\"high\">Path</span>: ");
writer.write("<a href=\"");
writer.write(path);
writer.write("\">");
writer.write(path);
writer.write("</a></p>");
writer.write("<p class=\"errmsg\">");
String message = e.getMessage() == null ? e.toString() : e.getMessage();
writer.write(XMLUtil.encodeAttrMarkup(message));
writer.write("</p>");
if (query != null) {
writer.write("<p><span class=\"high\">Query</span>:</p><pre>");
writer.write(XMLUtil.encodeAttrMarkup(query));
writer.write("</pre>");
}
writer.write("</body></html>");
writer.flush();
writer.close();
}
/**
* @param response
* @param encoding
* @param query
* @param path
* @param e
*/
private void writeXPathException(HttpServletResponse response, int httpStatusCode, String encoding, String query, String path, XPathException e)
throws IOException {
if( !response.isCommitted() ) {
response.reset();
}
response.setStatus(httpStatusCode);
response.setContentType(MimeType.XML_TYPE.getName() + "; charset="
+ encoding);
OutputStreamWriter writer = new OutputStreamWriter(response
.getOutputStream(), encoding);
writer.write("<?xml version=\"1.0\" ?>");
writer.write("<exception><path>");
writer.write(path);
writer.write("</path>");
writer.write("<message>");
String message = e.getMessage() == null ? e.toString() : e.getMessage();
writer.write(XMLUtil.encodeAttrMarkup(message));
writer.write("</message>");
if (query != null) {
writer.write("<query>");
writer.write(XMLUtil.encodeAttrMarkup(query));
writer.write("</query>");
}
writer.write("</exception>");
writer.flush();
writer.close();
}
/**
* @param response
* @param encoding
* @param updateCount
*/
private void writeXUpdateResult(HttpServletResponse response,
String encoding, long updateCount) throws IOException {
response.setContentType(MimeType.XML_TYPE.getName() + "; charset="
+ encoding);
OutputStreamWriter writer = new OutputStreamWriter(response
.getOutputStream(), encoding);
writer.write("<?xml version=\"1.0\" ?>");
writer.write("<exist:modifications xmlns:exist=\""
+ Namespaces.EXIST_NS + "\" count=\"" + updateCount + "\">");
writer.write(updateCount + " modifications processed.");
writer.write("</exist:modifications>");
writer.flush();
writer.close();
}
/**
* @param response
* @param encoding
* @param broker
* @param collection
*/
protected void writeCollection(HttpServletResponse response,
String encoding, DBBroker broker, Collection collection)
throws IOException {
response.setContentType(MimeType.XML_TYPE.getName() + "; charset="
+ encoding);
response.addDateHeader("Last-Modified", collection.getCreationTime());
response.addDateHeader("Created", collection.getCreationTime());
OutputStreamWriter writer = new OutputStreamWriter(response
.getOutputStream(), encoding);
SAXSerializer serializer = null;
try {
serializer = (SAXSerializer) SerializerPool.getInstance()
.borrowObject(SAXSerializer.class);
serializer.setOutput(writer, defaultProperties);
AttributesImpl attrs = new AttributesImpl();
serializer.startDocument();
serializer.startPrefixMapping("exist", Namespaces.EXIST_NS);
serializer.startElement(Namespaces.EXIST_NS, "result",
"exist:result", attrs);
attrs.addAttribute("", "name", "name", "CDATA", collection.getURI()
.toString());
// add an attribute for the creation date as an xs:dateTime
try {
DateTimeValue dtCreated = new DateTimeValue(new Date(collection
.getCreationTime()));
attrs.addAttribute("", "created", "created", "CDATA", dtCreated
.getStringValue());
} catch (XPathException e) {
// fallback to long value
attrs.addAttribute("", "created", "created", "CDATA", String
.valueOf(collection.getCreationTime()));
}
addPermissionAttributes(attrs, collection.getPermissions());
serializer.startElement(Namespaces.EXIST_NS, "collection",
"exist:collection", attrs);
for (Iterator i = collection.collectionIterator(); i.hasNext();) {
XmldbURI child = (XmldbURI) i.next();
Collection childCollection = broker.getCollection(collection
.getURI().append(child));
if (childCollection != null
&& childCollection.getPermissions().validate(
broker.getUser(), Permission.READ)) {
attrs.clear();
attrs.addAttribute("", "name", "name", "CDATA", child
.toString());
// add an attribute for the creation date as an xs:dateTime
try {
DateTimeValue dtCreated = new DateTimeValue(new Date(
childCollection.getCreationTime()));
attrs.addAttribute("", "created", "created", "CDATA",
dtCreated.getStringValue());
} catch (XPathException e) {
// fallback to long value
attrs.addAttribute("", "created", "created", "CDATA",
String.valueOf(childCollection
.getCreationTime()));
}
addPermissionAttributes(attrs, childCollection
.getPermissions());
serializer.startElement(Namespaces.EXIST_NS, "collection",
"exist:collection", attrs);
serializer.endElement(Namespaces.EXIST_NS, "collection",
"exist:collection");
}
}
for (Iterator i = collection.iterator(broker); i.hasNext();) {
DocumentImpl doc = (DocumentImpl) i.next();
if (doc.getPermissions().validate(broker.getUser(),
Permission.READ)) {
XmldbURI resource = doc.getFileURI();
DocumentMetadata metadata = doc.getMetadata();
attrs.clear();
attrs.addAttribute("", "name", "name", "CDATA", resource
.toString());
// add an attribute for the creation date as an xs:dateTime
try {
DateTimeValue dtCreated = new DateTimeValue(new Date(
metadata.getCreated()));
attrs.addAttribute("", "created", "created", "CDATA",
dtCreated.getStringValue());
} catch (XPathException e) {
// fallback to long value
attrs.addAttribute("", "created", "created", "CDATA",
String.valueOf(metadata.getCreated()));
}
// add an attribute for the last modified date as an
// xs:dateTime
try {
DateTimeValue dtLastModified = new DateTimeValue(
new Date(metadata.getLastModified()));
attrs.addAttribute("", "last-mofified",
"last-modified", "CDATA", dtLastModified
.getStringValue());
} catch (XPathException e) {
// fallback to long value
attrs.addAttribute("", "last-modified",
"last-modified", "CDATA", String
.valueOf(metadata.getLastModified()));
}
addPermissionAttributes(attrs, doc.getPermissions());
serializer.startElement(Namespaces.EXIST_NS, "resource",
"exist:resource", attrs);
serializer.endElement(Namespaces.EXIST_NS, "resource",
"exist:resource");
}
}
serializer.endElement(Namespaces.EXIST_NS, "collection",
"exist:collection");
serializer
.endElement(Namespaces.EXIST_NS, "result", "exist:result");
serializer.endDocument();
writer.flush();
writer.close();
} catch (SAXException e) {
// should never happen
LOG.warn("Error while serializing collection contents: "
+ e.getMessage(), e);
} finally {
if (serializer != null) {
SerializerPool.getInstance().returnObject(serializer);
}
}
}
protected void addPermissionAttributes(AttributesImpl attrs, Permission perm) {
attrs.addAttribute("", "owner", "owner", "CDATA", perm.getOwner());
attrs.addAttribute("", "group", "group", "CDATA", perm.getOwnerGroup());
attrs.addAttribute("", "permissions", "permissions", "CDATA", perm
.toString());
}
protected void writeResults(HttpServletResponse response, DBBroker broker,
Sequence results, int howmany, int start,
Properties outputProperties, boolean wrap)
throws BadRequestException {
// some xquery functions can write directly to the output stream
// (response:stream-binary() etc...)
// so if output is already written then dont overwrite here
if (response.isCommitted())
return;
// calculate number of results to return
if (!results.isEmpty()) {
int rlen = results.getItemCount();
if ((start < 1) || (start > rlen))
throw new BadRequestException("Start parameter out of range");
// FD : correct bound evaluation
if (((howmany + start) > rlen) || (howmany <= 0))
howmany = rlen - start + 1;
} else {
howmany = 0;
}
// serialize the results to the response output stream
Serializer serializer = broker.getSerializer();
serializer.reset();
outputProperties.setProperty(Serializer.GENERATE_DOC_EVENTS, "false");
SAXSerializer sax = null;
try {
sax = (SAXSerializer) SerializerPool.getInstance().borrowObject(
SAXSerializer.class);
// set output headers
String encoding = outputProperties.getProperty(OutputKeys.ENCODING);
String mimeType = outputProperties
.getProperty(OutputKeys.MEDIA_TYPE);
if (mimeType != null) {
int semicolon = mimeType.indexOf(';');
if (semicolon != Constants.STRING_NOT_FOUND) {
mimeType = mimeType.substring(0, semicolon);
}
if (wrap) {
mimeType = "text/xml";
}
response.setContentType(mimeType + "; charset=" + encoding);
}
if (wrap)
outputProperties.setProperty("method", "xml");
Writer writer = new OutputStreamWriter(response.getOutputStream(),
encoding);
sax.setOutput(writer, outputProperties);
serializer.setProperties(outputProperties);
serializer.setSAXHandlers(sax, sax);
serializer.toSAX(results, start, howmany, wrap);
writer.flush();
writer.close();
} catch (SAXException e) {
LOG.warn(e);
throw new BadRequestException("Error while serializing xml: "
+ e.toString(), e);
} catch (Exception e) {
LOG.warn(e.getMessage(), e);
throw new BadRequestException("Error while serializing xml: "
+ e.toString(), e);
} finally {
if (sax != null) {
SerializerPool.getInstance().returnObject(sax);
}
}
}
private boolean isExecutableType(DocumentImpl resource) {
if (resource != null
&& (MimeType.XQUERY_TYPE.getName().equals(resource.getMetadata().getMimeType()) // a xquery
|| MimeType.XPROC_TYPE.getName().equals(resource.getMetadata().getMimeType()))//a xproc
)
return true;
else
return false;
}
}