@Test public void testCompute() { JavaScriptPostAggregator javaScriptPostAggregator; Map<String, Object> metricValues = new HashMap<>(); metricValues.put("delta", -10.0); metricValues.put("total", 100.0); String absPercentFunction = "function(delta, total) { return 100 * Math.abs(delta) / total; }"; javaScriptPostAggregator = new JavaScriptPostAggregator( "absPercent", Lists.newArrayList("delta", "total"), absPercentFunction, JavaScriptConfig.getEnabledInstance() ); Assert.assertEquals(10.0, javaScriptPostAggregator.compute(metricValues)); }
/** * {@link #compute} can be called by multiple threads, so this function should be thread-safe to avoid extra * script compilation. */ @EnsuresNonNull("fn") private Function getCompiledScript() { // JavaScript configuration should be checked when it's actually used because someone might still want Druid // nodes to be able to deserialize JavaScript-based objects even though JavaScript is disabled. Preconditions.checkState(config.isEnabled(), "JavaScript is disabled"); Function syncedFn = fn; if (syncedFn == null) { synchronized (config) { syncedFn = fn; if (syncedFn == null) { syncedFn = compile(function); fn = syncedFn; } } } return syncedFn; }
@Override public Object compute(Map<String, Object> combinedAggregators) { Function fn = getCompiledScript(); final Object[] args = new Object[fieldNames.size()]; int i = 0; for (String field : fieldNames) { args[i++] = combinedAggregators.get(field); } return fn.apply(args); }
@Override public Object compute(Map<String, Object> combinedAggregators) { checkAndCompileScript(); final Object[] args = new Object[fieldNames.size()]; int i = 0; for (String field : fieldNames) { args[i++] = combinedAggregators.get(field); } return fn.apply(args); }
@Test public void testComputeJavaScriptNotAllowed() { String absPercentFunction = "function(delta, total) { return 100 * Math.abs(delta) / total; }"; JavaScriptPostAggregator aggregator = new JavaScriptPostAggregator( "absPercent", Lists.newArrayList("delta", "total"), absPercentFunction, new JavaScriptConfig(false) ); expectedException.expect(IllegalStateException.class); expectedException.expectMessage("JavaScript is disabled"); aggregator.compute(new HashMap<>()); } }
/** * {@link #compute} can be called by multiple threads, so this function should be thread-safe to avoid extra * script compilation. */ private void checkAndCompileScript() { if (fn == null) { // JavaScript configuration should be checked when it's actually used because someone might still want Druid // nodes to be able to deserialize JavaScript-based objects even though JavaScript is disabled. Preconditions.checkState(config.isEnabled(), "JavaScript is disabled"); // Synchronizing here can degrade the performance significantly because this method is called per input row. // However, early compilation of JavaScript functions can occur some memory issues due to unnecessary compilation // involving Java class generation each time, and thus this will be better. synchronized (config) { if (fn == null) { fn = compile(function); } } } }