use of org.exquery.restxq.RestXqServiceException in project exist by eXist-db.
the class ResourceFunctionExecutorImpl method execute.
@Override
public Sequence execute(final ResourceFunction resourceFunction, final Iterable<TypedArgumentValue> arguments, final HttpRequest request) throws RestXqServiceException {
final RestXqServiceCompiledXQueryCache cache = RestXqServiceCompiledXQueryCacheImpl.getInstance();
CompiledXQuery xquery = null;
ProcessMonitor processMonitor = null;
try (final DBBroker broker = getBrokerPool().getBroker()) {
// ensure we can execute the function before going any further
checkSecurity(broker, resourceFunction.getXQueryLocation());
// get a compiled query service from the cache
xquery = cache.getCompiledQuery(broker, resourceFunction.getXQueryLocation());
// find the function that we will execute
final UserDefinedFunction fn = findFunction(xquery, resourceFunction.getFunctionSignature());
final XQueryContext xqueryContext = xquery.getContext();
// set the request object - can later be used by the EXQuery Request Module
xqueryContext.setAttribute(EXQ_REQUEST_ATTR, request);
// TODO this is a workaround?
declareVariables(xqueryContext);
// START workaround: evaluate global variables in modules, as they are reset by XQueryContext.reset()
final Expression rootExpr = xqueryContext.getRootExpression();
for (int i = 0; i < rootExpr.getSubExpressionCount(); i++) {
final Expression subExpr = rootExpr.getSubExpression(i);
if (subExpr instanceof VariableDeclaration) {
subExpr.eval(null);
}
}
// END workaround
// setup monitoring
processMonitor = broker.getBrokerPool().getProcessMonitor();
xqueryContext.getProfiler().traceQueryStart();
processMonitor.queryStarted(xqueryContext.getWatchDog());
// create a function call
try (final FunctionReference fnRef = new FunctionReference(new FunctionCall(xqueryContext, fn))) {
// convert the arguments
final org.exist.xquery.value.Sequence[] fnArgs = convertToExistFunctionArguments(xqueryContext, fn, arguments);
// execute the function call
fnRef.analyze(new AnalyzeContextInfo());
// if setUid/setGid, determine the effectiveSubject to use for execution
final Optional<EffectiveSubject> effectiveSubject = getEffectiveSubject(xquery);
try {
// switch to effective user if setUid/setGid
effectiveSubject.ifPresent(broker::pushSubject);
final org.exist.xquery.value.Sequence result = fnRef.evalFunction(null, null, fnArgs);
// copy for closure
final CompiledXQuery xquery1 = xquery;
// return a sequence adapter which returns the query when it is finished with the results
return new SequenceAdapter(result, () -> {
if (xquery1 != null) {
// return the compiled query to the pool
cache.returnCompiledQuery(resourceFunction.getXQueryLocation(), xquery1);
}
});
} finally {
// switch back from effective user if setUid/setGid
if (effectiveSubject.isPresent()) {
broker.popSubject();
}
}
}
} catch (final URISyntaxException | EXistException | XPathException | PermissionDeniedException use) {
// if an error occurred we should return the compiled query
if (xquery != null) {
// return the compiled query to the pool
cache.returnCompiledQuery(resourceFunction.getXQueryLocation(), xquery);
}
throw new RestXqServiceException(use.getMessage(), use);
} finally {
// clear down monitoring
if (processMonitor != null) {
xquery.getContext().getProfiler().traceQueryEnd(xquery.getContext());
processMonitor.queryCompleted(xquery.getContext().getWatchDog());
}
}
}
use of org.exquery.restxq.RestXqServiceException in project exist by eXist-db.
the class ResourceFunctionExecutorImpl method convertToType.
// TODO this needs to be abstracted into EXQuery library / or not, see the TODOs below
private <X> TypedValue<X> convertToType(final XQueryContext xqueryContext, final String argumentName, final TypedValue typedValue, final org.exquery.xquery.Type destinationType, final Class<X> underlyingDestinationClass) throws RestXqServiceException {
// TODO consider changing Types that can be used as <T> to TypedValue to a set of interfaces for XDM types that
// require absolute minimal implementation, and we provide some default or abstract implementations if possible
final Item convertedValue;
try {
final int existDestinationType = TypeAdapter.toExistType(destinationType);
final Item value;
// Consider a factory or java.util.ServiceLoader pattern
if (typedValue instanceof org.exquery.xdm.type.StringTypedValue) {
value = new StringValue(((org.exquery.xdm.type.StringTypedValue) typedValue).getValue());
} else if (typedValue instanceof org.exquery.xdm.type.Base64BinaryTypedValue) {
value = BinaryValueFromInputStream.getInstance(xqueryContext, new Base64BinaryValueType(), ((org.exquery.xdm.type.Base64BinaryTypedValue) typedValue).getValue());
} else {
value = (Item) typedValue.getValue();
}
if (existDestinationType == value.getType()) {
convertedValue = value;
} else if (value instanceof AtomicValue) {
convertedValue = value.convertTo(existDestinationType);
} else {
LOG.warn("Could not convert parameter '{}' from '{}' to '{}'.", argumentName, typedValue.getType().name(), destinationType.name());
convertedValue = value;
}
} catch (final XPathException xpe) {
// TODO define an ErrorCode
throw new RestXqServiceException("TODO need to implement error code for problem with parameter conversion!: " + xpe.getMessage(), xpe);
}
return new TypedValue<X>() {
@Override
public org.exquery.xquery.Type getType() {
// return destinationType;
return TypeAdapter.toExQueryType(convertedValue.getType());
}
@Override
public X getValue() {
return (X) convertedValue;
}
};
}
use of org.exquery.restxq.RestXqServiceException in project exist by eXist-db.
the class RestXqServiceSerializerImpl method serializeNodeBody.
@Override
protected void serializeNodeBody(final Sequence result, final HttpResponse response, final Map<SerializationProperty, String> serializationProperties) throws RestXqServiceException {
try (final DBBroker broker = getBrokerPool().getBroker();
final Writer writer = new OutputStreamWriter(response.getOutputStream(), serializationProperties.get(SerializationProperty.ENCODING))) {
final Properties outputProperties = serializationPropertiesToProperties(serializationProperties);
final XQuerySerializer xqSerializer = new XQuerySerializer(broker, outputProperties, writer);
xqSerializer.serialize(((SequenceAdapter) result).getExistSequence());
writer.flush();
} catch (IOException | XPathException | SAXException | EXistException ioe) {
throw new RestXqServiceException("Error while serializing xml: " + ioe.toString(), ioe);
}
}
use of org.exquery.restxq.RestXqServiceException in project exist by eXist-db.
the class RestXqServlet method service.
@Override
protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
// authenticate
final Subject user = authenticate(request, response);
if (user == null) {
// "Permission denied: unknown user or password");
return;
}
try (final DBBroker broker = getPool().get(Optional.of(user))) {
final Configuration configuration = broker.getConfiguration();
final HttpRequest requestAdapter = new HttpServletRequestAdapter(request, () -> (String) configuration.getProperty(Configuration.BINARY_CACHE_CLASS_PROPERTY));
final RestXqService service = getRegistry().findService(requestAdapter);
if (service != null) {
if (log.isTraceEnabled()) {
log.trace("Received {} request for \"{}\" and found Resource Function \"{}\" in module \"{}\"", requestAdapter.getMethod().name(), requestAdapter.getPath(), service.getResourceFunction().getFunctionSignature(), service.getResourceFunction().getXQueryLocation());
}
service.service(requestAdapter, new HttpServletResponseAdapter(response), new ResourceFunctionExecutorImpl(getPool(), request.getContextPath() + request.getServletPath(), request.getRequestURI()), new RestXqServiceSerializerImpl(getPool()));
} else {
if (log.isTraceEnabled()) {
log.trace("Received {} request for \"{}\" but no suitable Resource Function found!", requestAdapter.getMethod().name(), requestAdapter.getPath());
}
super.service(request, response);
}
} catch (final EXistException e) {
getLog().error(e.getMessage(), e);
throw new ServletException(e.getMessage(), e);
} catch (final RestXqServiceException e) {
if (e.getCause() instanceof PermissionDeniedException) {
getAuthenticator().sendChallenge(request, response);
} else {
// TODO should probably be caught higher up and returned as a HTTP Response? maybe need two different types of exception to differentiate critical vs processing exception
getLog().error(e.getMessage(), e);
throw new ServletException(e.getMessage(), e);
}
}
}
use of org.exquery.restxq.RestXqServiceException in project exist by eXist-db.
the class RestXqServiceCompiledXQueryCacheImpl method getCompiledQuery.
@Override
public CompiledXQuery getCompiledQuery(final DBBroker broker, final URI xqueryLocation) throws RestXqServiceException {
final Queue<CompiledXQuery> queue = cache.get(xqueryLocation, key -> new MpmcAtomicArrayQueue<>(DEFAULT_MAX_QUERY_STACK_SIZE));
CompiledXQuery xquery = queue.poll();
if (xquery == null) {
xquery = XQueryCompiler.compile(broker, xqueryLocation);
} else {
// prepare the context for re-use
try {
xquery.getContext().prepareForReuse();
} catch (final XPathException e) {
throw new RestXqServiceException("Unable to prepare compiled XQuery for reuse", e);
}
}
xquery.getContext().prepareForExecution();
return xquery;
}
Aggregations