summaryrefslogtreecommitdiff
path: root/core/src/main/java/org/elasticsearch/script
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/java/org/elasticsearch/script')
-rw-r--r--core/src/main/java/org/elasticsearch/script/ScriptContext.java100
-rw-r--r--core/src/main/java/org/elasticsearch/script/ScriptContextRegistry.java93
-rw-r--r--core/src/main/java/org/elasticsearch/script/ScriptEngine.java9
-rw-r--r--core/src/main/java/org/elasticsearch/script/ScriptModule.java63
-rw-r--r--core/src/main/java/org/elasticsearch/script/ScriptService.java29
5 files changed, 63 insertions, 231 deletions
diff --git a/core/src/main/java/org/elasticsearch/script/ScriptContext.java b/core/src/main/java/org/elasticsearch/script/ScriptContext.java
index cc2408786e..cd3bff3379 100644
--- a/core/src/main/java/org/elasticsearch/script/ScriptContext.java
+++ b/core/src/main/java/org/elasticsearch/script/ScriptContext.java
@@ -19,87 +19,35 @@
package org.elasticsearch.script;
-import org.elasticsearch.common.Strings;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
/**
- * Context of an operation that uses scripts as part of its execution.
+ * A holder for information about a context in which a script is compiled and run.
*/
-public interface ScriptContext {
-
- /**
- * @return the name of the operation
- */
- String getKey();
-
- /**
- * Standard operations that make use of scripts as part of their execution.
- * Note that the suggest api is considered part of search for simplicity, as well as the percolate api.
- */
- enum Standard implements ScriptContext {
-
- AGGS("aggs"), SEARCH("search"), UPDATE("update"), INGEST("ingest");
-
- private final String key;
-
- Standard(String key) {
- this.key = key;
- }
-
- @Override
- public String getKey() {
- return key;
- }
-
- @Override
- public String toString() {
- return getKey();
- }
+public final class ScriptContext {
+
+ public static final ScriptContext AGGS = new ScriptContext("aggs");
+ public static final ScriptContext SEARCH = new ScriptContext("search");
+ public static final ScriptContext UPDATE = new ScriptContext("update");
+ public static final ScriptContext INGEST = new ScriptContext("ingest");
+
+ public static final Map<String, ScriptContext> BUILTINS;
+ static {
+ Map<String, ScriptContext> builtins = new HashMap<>();
+ builtins.put(AGGS.name, AGGS);
+ builtins.put(SEARCH.name, SEARCH);
+ builtins.put(UPDATE.name, UPDATE);
+ builtins.put(INGEST.name, INGEST);
+ BUILTINS = Collections.unmodifiableMap(builtins);
}
- /**
- * Custom operation exposed via plugin, which makes use of scripts as part of its execution
- */
- final class Plugin implements ScriptContext {
-
- private final String pluginName;
- private final String operation;
- private final String key;
-
- /**
- * Creates a new custom scripts based operation exposed via plugin.
- * The name of the plugin combined with the operation name can be used to enable/disable scripts via fine-grained settings.
- *
- * @param pluginName the name of the plugin
- * @param operation the name of the operation
- */
- public Plugin(String pluginName, String operation) {
- if (Strings.hasLength(pluginName) == false) {
- throw new IllegalArgumentException("plugin name cannot be empty when registering a custom script context");
- }
- if (Strings.hasLength(operation) == false) {
- throw new IllegalArgumentException("operation name cannot be empty when registering a custom script context");
- }
- this.pluginName = pluginName;
- this.operation = operation;
- this.key = pluginName + "_" + operation;
- }
-
- public String getPluginName() {
- return pluginName;
- }
-
- public String getOperation() {
- return operation;
- }
-
- @Override
- public String getKey() {
- return key;
- }
+ /** A unique identifier for this context. */
+ public final String name;
- @Override
- public String toString() {
- return getKey();
- }
+ // pkg private ctor, only created by script module
+ public ScriptContext(String name) {
+ this.name = name;
}
}
diff --git a/core/src/main/java/org/elasticsearch/script/ScriptContextRegistry.java b/core/src/main/java/org/elasticsearch/script/ScriptContextRegistry.java
deleted file mode 100644
index 765be1d437..0000000000
--- a/core/src/main/java/org/elasticsearch/script/ScriptContextRegistry.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Licensed to Elasticsearch under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.elasticsearch.script;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-import static java.util.Collections.unmodifiableMap;
-import static java.util.Collections.unmodifiableSet;
-
-/**
- * Registry for operations that use scripts as part of their execution. Can be standard operations of custom defined ones (via plugin).
- * Allows plugins to register custom operations that they use scripts for,
- * via {@link org.elasticsearch.plugins.ScriptPlugin}
- * Scripts can be enabled/disabled via fine-grained settings for each single registered operation.
- */
-public final class ScriptContextRegistry {
- static final Set<String> RESERVED_SCRIPT_CONTEXTS = reservedScriptContexts();
-
- private final Map<String, ScriptContext> scriptContexts;
-
- public ScriptContextRegistry(Collection<ScriptContext.Plugin> customScriptContexts) {
- Map<String, ScriptContext> scriptContexts = new HashMap<>();
- for (ScriptContext.Standard scriptContext : ScriptContext.Standard.values()) {
- scriptContexts.put(scriptContext.getKey(), scriptContext);
- }
- for (ScriptContext.Plugin customScriptContext : customScriptContexts) {
- validateScriptContext(customScriptContext);
- ScriptContext previousContext = scriptContexts.put(customScriptContext.getKey(), customScriptContext);
- if (previousContext != null) {
- throw new IllegalArgumentException("script context [" + customScriptContext.getKey() + "] cannot be registered twice");
- }
- }
- this.scriptContexts = unmodifiableMap(scriptContexts);
- }
-
- /**
- * @return a list that contains all the supported {@link ScriptContext}s, both standard ones and registered via plugins
- */
- Collection<ScriptContext> scriptContexts() {
- return scriptContexts.values();
- }
-
- /**
- * @return <tt>true</tt> if the provided {@link ScriptContext} is supported, <tt>false</tt> otherwise
- */
- boolean isSupportedContext(String scriptContext) {
- return scriptContexts.containsKey(scriptContext);
- }
-
- //script contexts can be used in fine-grained settings, we need to be careful with what we allow here
- private void validateScriptContext(ScriptContext.Plugin scriptContext) {
- if (RESERVED_SCRIPT_CONTEXTS.contains(scriptContext.getPluginName())) {
- throw new IllegalArgumentException("[" + scriptContext.getPluginName() + "] is a reserved name, it cannot be registered as a custom script context");
- }
- if (RESERVED_SCRIPT_CONTEXTS.contains(scriptContext.getOperation())) {
- throw new IllegalArgumentException("[" + scriptContext.getOperation() + "] is a reserved name, it cannot be registered as a custom script context");
- }
- }
-
- private static Set<String> reservedScriptContexts() {
- Set<String> reserved = new HashSet<>(ScriptType.values().length + ScriptContext.Standard.values().length);
- for (ScriptType scriptType : ScriptType.values()) {
- reserved.add(scriptType.toString());
- }
- for (ScriptContext.Standard scriptContext : ScriptContext.Standard.values()) {
- reserved.add(scriptContext.getKey());
- }
- reserved.add("script");
- reserved.add("engine");
- return unmodifiableSet(reserved);
- }
-}
diff --git a/core/src/main/java/org/elasticsearch/script/ScriptEngine.java b/core/src/main/java/org/elasticsearch/script/ScriptEngine.java
index caec832008..9572e891b3 100644
--- a/core/src/main/java/org/elasticsearch/script/ScriptEngine.java
+++ b/core/src/main/java/org/elasticsearch/script/ScriptEngine.java
@@ -37,13 +37,12 @@ public interface ScriptEngine extends Closeable {
/**
* Compiles a script.
- * @param scriptName name of the script. {@code null} if it is anonymous (inline).
- * For a file script, its the file name (with extension).
- * For a stored script, its the identifier.
- * @param scriptSource actual source of the script
+ * @param name the name of the script. {@code null} if it is anonymous (inline). For a stored script, its the identifier.
+ * @param code actual source of the script
* @param params compile-time parameters (such as flags to the compiler)
+ * @return an opaque compiled script which may be cached and later passed to
*/
- Object compile(String scriptName, String scriptSource, Map<String, String> params);
+ Object compile(String name, String code, Map<String, String> params);
ExecutableScript executable(CompiledScript compiledScript, @Nullable Map<String, Object> vars);
diff --git a/core/src/main/java/org/elasticsearch/script/ScriptModule.java b/core/src/main/java/org/elasticsearch/script/ScriptModule.java
index 7668b38c4c..b29a199c3a 100644
--- a/core/src/main/java/org/elasticsearch/script/ScriptModule.java
+++ b/core/src/main/java/org/elasticsearch/script/ScriptModule.java
@@ -19,17 +19,14 @@
package org.elasticsearch.script;
-import org.elasticsearch.common.settings.ClusterSettings;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.plugins.ScriptPlugin;
-
-import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
-import java.util.stream.Collectors;
+
+import org.elasticsearch.common.settings.ClusterSettings;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.plugins.ScriptPlugin;
/**
* Manages building {@link ScriptService}.
@@ -37,42 +34,26 @@ import java.util.stream.Collectors;
public class ScriptModule {
private final ScriptService scriptService;
- /**
- * Build from {@linkplain ScriptPlugin}s. Convenient for normal use but not great for tests. See
- * {@link ScriptModule#ScriptModule(Settings, List, List)} for easier use in tests.
- */
- public static ScriptModule create(Settings settings, List<ScriptPlugin> scriptPlugins) {
- List<ScriptEngine> scriptEngines = scriptPlugins.stream().map(x -> x.getScriptEngine(settings))
- .filter(Objects::nonNull).collect(Collectors.toList());
- List<ScriptContext.Plugin> plugins = scriptPlugins.stream().map(x -> x.getCustomScriptContexts()).filter(Objects::nonNull)
- .collect(Collectors.toList());
- return new ScriptModule(settings, scriptEngines, plugins);
- }
-
- /**
- * Build {@linkplain ScriptEngine} and {@linkplain ScriptContext.Plugin}.
- */
- public ScriptModule(Settings settings, List<ScriptEngine> scriptEngines,
- List<ScriptContext.Plugin> customScriptContexts) {
- ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(customScriptContexts);
- Map<String, ScriptEngine> enginesByName = getEnginesByName(scriptEngines);
- try {
- scriptService = new ScriptService(settings, enginesByName, scriptContextRegistry);
- } catch (IOException e) {
- throw new RuntimeException("Couldn't setup ScriptService", e);
- }
- }
-
- private Map<String, ScriptEngine> getEnginesByName(List<ScriptEngine> engines) {
- Map<String, ScriptEngine> enginesByName = new HashMap<>();
- for (ScriptEngine engine : engines) {
- ScriptEngine existing = enginesByName.put(engine.getType(), engine);
- if (existing != null) {
- throw new IllegalArgumentException("scripting language [" + engine.getType() + "] defined for engine [" +
- existing.getClass().getName() + "] and [" + engine.getClass().getName());
+ public ScriptModule(Settings settings, List<ScriptPlugin> scriptPlugins) {
+ Map<String, ScriptEngine> engines = new HashMap<>();
+ Map<String, ScriptContext> contexts = new HashMap<>(ScriptContext.BUILTINS);
+ for (ScriptPlugin plugin : scriptPlugins) {
+ for (ScriptContext context : plugin.getContexts()) {
+ ScriptContext oldContext = contexts.put(context.name, context);
+ if (oldContext != null) {
+ throw new IllegalArgumentException("Context name [" + context.name + "] defined twice");
+ }
+ }
+ ScriptEngine engine = plugin.getScriptEngine(settings);
+ if (engine != null) {
+ ScriptEngine existing = engines.put(engine.getType(), engine);
+ if (existing != null) {
+ throw new IllegalArgumentException("scripting language [" + engine.getType() + "] defined for engine [" +
+ existing.getClass().getName() + "] and [" + engine.getClass().getName());
+ }
}
}
- return Collections.unmodifiableMap(enginesByName);
+ scriptService = new ScriptService(settings, Collections.unmodifiableMap(engines), Collections.unmodifiableMap(contexts));
}
/**
diff --git a/core/src/main/java/org/elasticsearch/script/ScriptService.java b/core/src/main/java/org/elasticsearch/script/ScriptService.java
index b681d07e98..656149be34 100644
--- a/core/src/main/java/org/elasticsearch/script/ScriptService.java
+++ b/core/src/main/java/org/elasticsearch/script/ScriptService.java
@@ -82,11 +82,10 @@ public class ScriptService extends AbstractComponent implements Closeable, Clust
private final Set<String> contextsAllowed;
private final Map<String, ScriptEngine> engines;
+ private final Map<String, ScriptContext> contexts;
private final Cache<CacheKey, CompiledScript> cache;
- private final ScriptContextRegistry scriptContextRegistry;
-
private final ScriptMetrics scriptMetrics = new ScriptMetrics();
private ClusterState clusterState;
@@ -96,12 +95,12 @@ public class ScriptService extends AbstractComponent implements Closeable, Clust
private double scriptsPerMinCounter;
private double compilesAllowedPerNano;
- public ScriptService(Settings settings, Map<String, ScriptEngine> engines, ScriptContextRegistry scriptContextRegistry) throws IOException {
+ public ScriptService(Settings settings, Map<String, ScriptEngine> engines, Map<String, ScriptContext> contexts) {
super(settings);
Objects.requireNonNull(settings);
this.engines = Objects.requireNonNull(engines);
- Objects.requireNonNull(scriptContextRegistry);
+ this.contexts = Objects.requireNonNull(contexts);
if (Strings.hasLength(settings.get(DISABLE_DYNAMIC_SCRIPTING_SETTING))) {
throw new IllegalArgumentException(DISABLE_DYNAMIC_SCRIPTING_SETTING + " is not a supported setting, replace with fine-grained script settings. \n" +
@@ -166,7 +165,7 @@ public class ScriptService extends AbstractComponent implements Closeable, Clust
}
}
- if (scriptContextRegistry.isSupportedContext(settingContext)) {
+ if (contexts.containsKey(settingContext)) {
this.contextsAllowed.add(settingContext);
} else {
throw new IllegalArgumentException(
@@ -175,8 +174,6 @@ public class ScriptService extends AbstractComponent implements Closeable, Clust
}
}
- this.scriptContextRegistry = scriptContextRegistry;
-
int cacheMaxSize = SCRIPT_CACHE_SIZE_SETTING.get(settings);
CacheBuilder<CacheKey, CompiledScript> cacheBuilder = CacheBuilder.builder();
@@ -223,9 +220,9 @@ public class ScriptService extends AbstractComponent implements Closeable, Clust
/**
* Checks if a script can be executed and compiles it if needed, or returns the previously compiled and cached script.
*/
- public CompiledScript compile(Script script, ScriptContext scriptContext) {
+ public CompiledScript compile(Script script, ScriptContext context) {
Objects.requireNonNull(script);
- Objects.requireNonNull(scriptContext);
+ Objects.requireNonNull(context);
ScriptType type = script.getType();
String lang = script.getLang();
@@ -266,10 +263,10 @@ public class ScriptService extends AbstractComponent implements Closeable, Clust
// TODO: fix this through some API or something, that's wrong
// special exception to prevent expressions from compiling as update or mapping scripts
boolean expression = "expression".equals(script.getLang());
- boolean notSupported = scriptContext.getKey().equals(ScriptContext.Standard.UPDATE.getKey());
+ boolean notSupported = context.name.equals(ScriptContext.UPDATE.name);
if (expression && notSupported) {
throw new UnsupportedOperationException("scripts of type [" + script.getType() + "]," +
- " operation [" + scriptContext.getKey() + "] and lang [" + lang + "] are not supported");
+ " operation [" + context.name + "] and lang [" + lang + "] are not supported");
}
ScriptEngine scriptEngine = getEngine(lang);
@@ -278,12 +275,12 @@ public class ScriptService extends AbstractComponent implements Closeable, Clust
throw new IllegalArgumentException("cannot execute [" + type + "] scripts");
}
- if (scriptContextRegistry.isSupportedContext(scriptContext.getKey()) == false) {
- throw new IllegalArgumentException("script context [" + scriptContext.getKey() + "] not supported");
+ if (contexts.containsKey(context.name) == false) {
+ throw new IllegalArgumentException("script context [" + context.name + "] not supported");
}
- if (isContextEnabled(scriptContext) == false) {
- throw new IllegalArgumentException("cannot execute scripts using [" + scriptContext.getKey() + "] context");
+ if (isContextEnabled(context) == false) {
+ throw new IllegalArgumentException("cannot execute scripts using [" + context.name + "] context");
}
if (logger.isTraceEnabled()) {
@@ -380,7 +377,7 @@ public class ScriptService extends AbstractComponent implements Closeable, Clust
}
public boolean isContextEnabled(ScriptContext scriptContext) {
- return contextsAllowed == null || contextsAllowed.contains(scriptContext.getKey());
+ return contextsAllowed == null || contextsAllowed.contains(scriptContext.name);
}
public boolean isAnyContextEnabled() {