use of org.opennms.netmgt.measurements.api.exceptions.ExpressionException in project opennms by OpenNMS.
the class JEXLExpressionEngine method applyExpressions.
/**
* {@inheritDoc}
*/
@Override
public void applyExpressions(final QueryRequest request, final FetchResults results) throws ExpressionException {
Preconditions.checkNotNull(request, "request argument");
Preconditions.checkNotNull(results, "results argument");
final int numExpressions = request.getExpressions().size();
// Don't do anything if there are no expressions
if (numExpressions < 1) {
return;
}
// Use to keep track of transient expression so that we don't
// allocate memory to store their results
int numNonTransientExpression = 0;
boolean[] transientFlags = new boolean[numExpressions];
// Compile the expressions
int j, k = 0;
final LinkedHashMap<String, org.apache.commons.jexl2.Expression> expressions = Maps.newLinkedHashMap();
for (final Expression e : request.getExpressions()) {
// Populate the transientFlags array
transientFlags[k] = e.getTransient();
if (!transientFlags[k]) {
numNonTransientExpression++;
}
k++;
try {
expressions.put(e.getLabel(), jexl.createExpression(e.getExpression()));
} catch (JexlException ex) {
throw new ExpressionException(ex, "Failed to parse expression label '{}'.", e.getLabel());
}
}
// Prepare the JEXL context
final Map<String, Object> jexlValues = Maps.newHashMap();
final JexlContext context = new MapContext(jexlValues);
// Add constants (i.e. values from strings.properties) retrieved by the fetch operation
jexlValues.putAll(results.getConstants());
LOG.debug("JEXL context constants: {}", jexlValues);
// Add some additional constants for ease of use
jexlValues.put("__inf", Double.POSITIVE_INFINITY);
jexlValues.put("__neg_inf", Double.NEGATIVE_INFINITY);
jexlValues.put("NaN", Double.NaN);
jexlValues.put("__E", java.lang.Math.E);
jexlValues.put("__PI", java.lang.Math.PI);
// Add JexlEvaluateFunctions with current context and jexl engine to allow string constants to be evaluated.
JexlEvaluateFunctions jexlEvaluateFunctions = new JexlEvaluateFunctions(context, jexl);
jexl.getFunctions().put("jexl", jexlEvaluateFunctions);
final long[] timestamps = results.getTimestamps();
final Map<String, double[]> columns = results.getColumns();
final int numRows = timestamps.length;
// Calculate the time span
jexlValues.put("__diff_time", numRows < 1 ? 0d : timestamps[numRows - 1] - timestamps[0]);
final double[][] expressionValues = new double[numNonTransientExpression][numRows];
// Iterate through all of the rows, apply the expressions
for (int i = 0; i < numRows; i++) {
// Evaluate every expression, in the same order as which they appeared in the query
j = k = 0;
for (final Map.Entry<String, org.apache.commons.jexl2.Expression> expressionEntry : expressions.entrySet()) {
// Update the timestamp
jexlValues.put("timestamp", timestamps[i]);
// add index as a referenced variable in context
jexlValues.put("__i", Integer.valueOf(i));
// overwriting values from the last loop
for (final String sourceLabel : columns.keySet()) {
jexlValues.put(sourceLabel, columns.get(sourceLabel)[i]);
// add reference to complete array for each column to allow backwards referencing of samples
jexlValues.put("__" + sourceLabel, columns.get(sourceLabel));
}
// Evaluate the expression
try {
Object derived = expressionEntry.getValue().evaluate(context);
double derivedAsDouble = Utils.toDouble(derived);
// Only store the values for non-transient expressions
if (!transientFlags[j++]) {
expressionValues[k++][i] = derivedAsDouble;
}
// Store the result back in the context, so that it can be referenced
// by subsequent expression in the row
jexlValues.put(expressionEntry.getKey(), derivedAsDouble);
} catch (NullPointerException | NumberFormatException e) {
throw new ExpressionException(e, "The return value from expression with label '" + expressionEntry.getKey() + "' could not be cast to a Double.");
} catch (JexlException e) {
throw new ExpressionException(e, "Failed to evaluate expression with label '" + expressionEntry.getKey() + "'.");
}
}
}
// Store the results
j = k = 0;
for (final String expressionLabel : expressions.keySet()) {
if (!transientFlags[j++]) {
columns.put(expressionLabel, expressionValues[k++]);
}
}
}
use of org.opennms.netmgt.measurements.api.exceptions.ExpressionException in project opennms by OpenNMS.
the class MeasurementsRestService method query.
/**
* Retrieves the measurements of many resources and performs
* arbitrary calculations on these.
*
* This a read-only query, however we use a POST instead of GET
* since the request parameters are difficult to express in a query string.
*/
@POST
@Path("/")
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_ATOM_XML })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_ATOM_XML })
@Transactional(readOnly = true)
public QueryResponse query(final QueryRequest request) {
Preconditions.checkState(service != null);
LOG.debug("Executing query with {}", request);
QueryResponse response = null;
try {
response = service.query(request);
} catch (ExpressionException e) {
throw getException(Status.BAD_REQUEST, e, "An error occurred while evaluating an expression: {}", e.getMessage());
} catch (FilterException | ValidationException e) {
throw getException(Status.BAD_REQUEST, e, e.getMessage());
} catch (ResourceNotFoundException e) {
throw getException(Status.NOT_FOUND, e, e.getMessage());
} catch (FetchException e) {
throw getException(Status.INTERNAL_SERVER_ERROR, e, e.getMessage());
} catch (Exception e) {
throw getException(Status.INTERNAL_SERVER_ERROR, e, "Query failed: {}", e.getMessage());
}
// Return a 204 if there are no columns
if (response.getColumns().length == 0) {
throw getException(Status.NO_CONTENT, "No content.");
}
return response;
}
Aggregations