/* * eXist Open Source Native XML Database * Copyright (C) 2001-2007 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.xquery; import antlr.RecognitionException; import antlr.TokenStreamException; import antlr.collections.AST; import com.sun.xacml.ctx.RequestCtx; import org.apache.log4j.Logger; import org.exist.debuggee.Debuggee; import org.exist.debuggee.DebuggeeJoint; import org.exist.security.PermissionDeniedException; import org.exist.security.xacml.AccessContext; import org.exist.security.xacml.ExistPDP; import org.exist.security.xacml.XACMLSource; import org.exist.source.Source; import org.exist.source.StringSource; import org.exist.storage.DBBroker; import org.exist.storage.XQueryPool; import org.exist.xquery.parser.XQueryLexer; import org.exist.xquery.parser.XQueryParser; import org.exist.xquery.parser.XQueryTreeParser; import org.exist.xquery.util.ExpressionDumper; import org.exist.xquery.util.HTTPUtils; import org.exist.xquery.value.Sequence; import java.io.IOException; import java.io.Reader; import java.text.NumberFormat; import java.util.Properties; /** * @author wolf */ public class XQuery { private final static Logger LOG = Logger.getLogger(XQuery.class); private DBBroker broker; /** * */ public XQuery(DBBroker broker) { this.broker = broker; } public XQueryContext newContext(AccessContext accessCtx) { return new XQueryContext(broker, accessCtx); } public XQueryPool getXQueryPool() { return broker.getBrokerPool().getXQueryPool(); } public CompiledXQuery compile(XQueryContext context, String expression) throws XPathException { Source source = new StringSource(expression); try { return compile(context, source); } catch(IOException ioe) { //should not happen because expression is a String throw new XPathException(ioe.getMessage(), ioe); } } public CompiledXQuery compile(XQueryContext context, Source source) throws XPathException, IOException { return compile(context, source, false); } public CompiledXQuery compile(XQueryContext context, Source source, boolean xpointer) throws XPathException, IOException { context.setSource(XACMLSource.getInstance(source)); Reader reader = source.getReader(); try { CompiledXQuery compiled = compile(context, reader, xpointer); return compiled; } finally { if (reader != null) reader.close(); } } private CompiledXQuery compile(XQueryContext context, Reader reader, boolean xpointer) throws XPathException { //TODO: move XQueryContext.getUserFromHttpSession() here, have to check if servlet.jar is in the classpath //before compiling/executing that code though to avoid a dependency on servlet.jar - reflection? - deliriumsky // how about - if(XQuery.class.getResource("servlet.jar") != null) do load my class with dependency and call method? /* <|wolf77|> I think last time I checked, I already had problems with the call to <|wolf77|> HTTPUtils.addLastModifiedHeader( result, context ); <|wolf77|> in line 184 of XQuery.java, because it introduces another dependency on HTTP. */ long start = System.currentTimeMillis(); XQueryLexer lexer = new XQueryLexer(context, reader); XQueryParser parser = new XQueryParser(lexer); XQueryTreeParser treeParser = new XQueryTreeParser(context); try { if (xpointer) parser.xpointer(); else parser.xpath(); if (parser.foundErrors()) { LOG.debug(parser.getErrorMessage()); throw new StaticXQueryException( parser.getErrorMessage()); } AST ast = parser.getAST(); if (ast == null) throw new XPathException("Unknown XQuery parser error: the parser returned an empty syntax tree."); // LOG.debug("Generated AST: " + ast.toStringTree()); PathExpr expr = new PathExpr(context); if (xpointer) treeParser.xpointer(ast, expr); else treeParser.xpath(ast, expr); if (treeParser.foundErrors()) { throw new StaticXQueryException( treeParser.getErrorMessage(), treeParser.getLastException()); } context.analyzeAndOptimizeIfModulesChanged(expr); // Log the query if it is not too large, but avoid // dumping huge queries to the log if (context.getExpressionCount() < 150) { LOG.debug("Query diagnostics:\n" + ExpressionDumper.dump(expr)); } else { LOG.debug("Query diagnostics:\n" + "[skipped: more than 150 expressions]"); } if (LOG.isDebugEnabled()) { NumberFormat nf = NumberFormat.getNumberInstance(); LOG.debug("Compilation took " + nf.format(System.currentTimeMillis() - start) + " ms"); } return expr; } catch (RecognitionException e) { LOG.debug("Error compiling query: " + e.getMessage(), e); String msg = e.getMessage(); if (msg.endsWith(", found 'null'")) msg = msg.substring(0, msg.length() - ", found 'null'".length()); throw new StaticXQueryException(e.getLine(), e.getColumn(), msg); } catch (TokenStreamException e) { LOG.debug("Error compiling query: " + e.getMessage(), e); throw new StaticXQueryException(e.getMessage(), e); } } public Sequence execute(CompiledXQuery expression, Sequence contextSequence) throws XPathException { return execute(expression, contextSequence, null); } public Sequence execute(CompiledXQuery expression, Sequence contextSequence, Properties outputProperties) throws XPathException { XQueryContext context = expression.getContext(); Sequence result = execute(expression, contextSequence, outputProperties, true); //TODO : move this elsewhere ! HTTPUtils.addLastModifiedHeader( result, context ); return result; } public Sequence execute(CompiledXQuery expression, Sequence contextSequence, boolean resetContext) throws XPathException { return execute(expression, contextSequence, null, resetContext); } public Sequence execute(CompiledXQuery expression, Sequence contextSequence, Properties outputProperties, boolean resetContext) throws XPathException { long start = System.currentTimeMillis(); XQueryContext context = expression.getContext(); //check access to the query XACMLSource source = expression.getSource(); try { ExistPDP pdp = context.getPDP(); if(pdp != null) { RequestCtx request = pdp.getRequestHelper().createQueryRequest(context, source); pdp.evaluate(request); } } catch (PermissionDeniedException pde) { throw new XPathException("Permission to execute query: " + source.createId() + " denied.", pde); } expression.reset(); if (resetContext) { context.setBroker(broker); context.getWatchDog().reset(); } if (context.isDebugMode()) { Debuggee debuggee = broker.getBrokerPool().getDebuggee(); if (debuggee != null) { debuggee.joint(expression); } } //do any preparation before execution context.prepare(); context.getProfiler().traceQueryStart(); broker.getBrokerPool().getProcessMonitor().queryStarted(context.getWatchDog()); try { Sequence result = expression.eval(contextSequence); if (LOG.isDebugEnabled()) { NumberFormat nf = NumberFormat.getNumberInstance(); LOG.debug("Execution took " + nf.format(System.currentTimeMillis() - start) + " ms"); } if(outputProperties != null) context.checkOptions(outputProperties); //must be done before context.reset! return result; } finally { context.getProfiler().traceQueryEnd(context); expression.reset(); if (resetContext) context.reset(); broker.getBrokerPool().getProcessMonitor().queryCompleted(context.getWatchDog()); } } public Sequence execute(String expression, Sequence contextSequence, AccessContext accessCtx) throws XPathException { XQueryContext context = new XQueryContext(broker, accessCtx); CompiledXQuery compiled = compile(context, expression); return execute(compiled, null); } }