/*
* This file is part of a module with proprietary Enterprise Features.
*
* Licensed to Crate.io Inc. ("Crate.io") under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*
* Unauthorized copying of this file, via any medium is strictly prohibited.
*
* To use this file, Crate.io must have given you permission to enable and
* use such Enterprise Features and you must have a valid Enterprise or
* Subscription Agreement with Crate.io. If you enable or use the Enterprise
* Features, you represent and warrant that you have a valid Enterprise or
* Subscription Agreement with Crate.io. Your use of the Enterprise Features
* if governed by the terms and conditions of your Enterprise or Subscription
* Agreement with Crate.io.
*/
package io.crate.operation.language;
import io.crate.metadata.FunctionIdent;
import io.crate.metadata.FunctionInfo;
import io.crate.metadata.Scalar;
import io.crate.operation.udf.UDFLanguage;
import io.crate.operation.udf.UserDefinedFunctionMetaData;
import io.crate.operation.udf.UserDefinedFunctionService;
import io.crate.types.DataType;
import jdk.nashorn.api.scripting.NashornScriptEngine;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import org.elasticsearch.common.inject.Inject;
import javax.annotation.Nullable;
import javax.script.*;
import java.util.Locale;
import java.util.stream.Collectors;
public class JavaScriptLanguage implements UDFLanguage {
static final String NAME = "javascript";
private static final NashornScriptEngine ENGINE = (NashornScriptEngine) new NashornScriptEngineFactory()
.getScriptEngine("--no-java", "--no-syntax-extensions");
@Inject
public JavaScriptLanguage(UserDefinedFunctionService udfService) {
udfService.registerLanguage(this);
}
public Scalar createFunctionImplementation(UserDefinedFunctionMetaData meta) throws ScriptException {
FunctionInfo info = new FunctionInfo(
new FunctionIdent(meta.schema(), meta.name(), meta.argumentTypes()),
meta.returnType()
);
return new JavaScriptUserDefinedFunction(info, meta.definition());
}
@Nullable
public String validate(UserDefinedFunctionMetaData meta) {
try {
bindScript(meta.definition());
} catch (ScriptException e) {
return String.format(Locale.ENGLISH, "Invalid JavaScript in function '%s.%s(%s)': %s",
meta.schema(),
meta.name(),
meta.argumentTypes().stream().map(DataType::getName).collect(Collectors.joining(", ")),
e.getMessage()
);
}
return null;
}
static Bindings bindScript(String source) throws ScriptException {
Bindings bindings = ENGINE.createBindings();
CompiledScript compiledScript = ENGINE.compile(source);
compiledScript.eval(bindings);
return bindings;
}
public String name() {
return NAME;
}
}