org.opensearch.script.ScriptContext Maven / Gradle / Ivy
Show all versions of opensearch Show documentation
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/
/*
* 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.
*/
/*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/
package org.opensearch.script;
import org.opensearch.common.annotation.PublicApi;
import org.opensearch.common.collect.Tuple;
import org.opensearch.common.unit.TimeValue;
import java.lang.reflect.Method;
/**
* The information necessary to compile and run a script.
*
* A {@link ScriptContext} contains the information related to a single use case and the interfaces
* and methods necessary for a {@link ScriptEngine} to implement.
*
* There are at least two (and optionally a third) related classes which must be defined.
*
* The InstanceType is a class which users of the script api call to execute a script. It
* may be stateful. Instances of
* the InstanceType may be executed multiple times by a caller with different arguments. This
* class must have an abstract method named {@code execute} which {@link ScriptEngine} implementations
* will define.
*
* The FactoryType is a factory class returned by the {@link ScriptService} when compiling
* a script. This class must be stateless so it is cacheable by the {@link ScriptService}. It must
* have one of the following:
*
* - An abstract method named {@code newInstance} which returns an instance of InstanceType
* - An abstract method named {@code newFactory} which returns an instance of StatefulFactoryType
*
*
* The StatefulFactoryType is an optional class which allows a stateful factory from the
* stateless factory type required by the {@link ScriptService}. If defined, the StatefulFactoryType
* must have a method named {@code newInstance} which returns an instance of InstanceType.
*
* Both the FactoryType and StatefulFactoryType may have abstract methods to indicate
* whether a variable is used in a script. These method should return a {@code boolean} and their name
* should start with {@code needs}, followed by the variable name, with the first letter uppercased.
* For example, to check if a variable {@code doc} is used, a method {@code boolean needsDoc()} should be added.
* If the variable name starts with an underscore, for example, {@code _score}, the needs method would
* be {@code boolean needs_score()}.
*
* @opensearch.api
*/
@PublicApi(since = "1.0.0")
public final class ScriptContext {
/** A unique identifier for this context. */
public final String name;
/** A factory class for constructing script or stateful factory instances. */
public final Class factoryClazz;
/** A factory class for construct script instances. */
public final Class> statefulFactoryClazz;
/** A class that is an instance of a script. */
public final Class> instanceClazz;
/** The default size of the cache for the context if not overridden */
public final int cacheSizeDefault;
/** The default expiration of a script in the cache for the context, if not overridden */
public final TimeValue cacheExpireDefault;
/** The default max compilation rate for scripts in this context. Script compilation is throttled if this is exceeded */
public final Tuple maxCompilationRateDefault;
/** Construct a context with the related instance and compiled classes with caller provided cache defaults */
public ScriptContext(
String name,
Class factoryClazz,
int cacheSizeDefault,
TimeValue cacheExpireDefault,
Tuple maxCompilationRateDefault
) {
this.name = name;
this.factoryClazz = factoryClazz;
Method newInstanceMethod = findMethod("FactoryType", factoryClazz, "newInstance");
Method newFactoryMethod = findMethod("FactoryType", factoryClazz, "newFactory");
if (newFactoryMethod != null) {
assert newInstanceMethod == null;
statefulFactoryClazz = newFactoryMethod.getReturnType();
newInstanceMethod = findMethod("StatefulFactoryType", statefulFactoryClazz, "newInstance");
if (newInstanceMethod == null) {
throw new IllegalArgumentException(
"Could not find method newInstance StatefulFactoryType class ["
+ statefulFactoryClazz.getName()
+ "] for script context ["
+ name
+ "]"
);
}
} else if (newInstanceMethod != null) {
assert newFactoryMethod == null;
statefulFactoryClazz = null;
} else {
throw new IllegalArgumentException(
"Could not find method newInstance or method newFactory on FactoryType class ["
+ factoryClazz.getName()
+ "] for script context ["
+ name
+ "]"
);
}
instanceClazz = newInstanceMethod.getReturnType();
this.cacheSizeDefault = cacheSizeDefault;
this.cacheExpireDefault = cacheExpireDefault;
this.maxCompilationRateDefault = maxCompilationRateDefault;
}
/** Construct a context with the related instance and compiled classes with defaults for cacheSizeDefault, cacheExpireDefault and
* maxCompilationRateDefault */
public ScriptContext(String name, Class factoryClazz) {
// cache size default, cache expire default, max compilation rate are defaults from ScriptService.
this(name, factoryClazz, 100, TimeValue.timeValueMillis(0), new Tuple<>(75, TimeValue.timeValueMinutes(5)));
}
/** Returns a method with the given name, or throws an exception if multiple are found. */
private Method findMethod(String type, Class> clazz, String methodName) {
Method foundMethod = null;
for (Method method : clazz.getMethods()) {
if (method.getName().equals(methodName)) {
if (foundMethod != null) {
throw new IllegalArgumentException(
"Cannot have multiple "
+ methodName
+ " methods on "
+ type
+ " class ["
+ clazz.getName()
+ "] for script context ["
+ name
+ "]"
);
}
foundMethod = method;
}
}
return foundMethod;
}
}