Add annotations to Painless whitelist (#43239)

This change adds the ability to attach annotative information for
classes, methods, fields, static methods, class bindings, and
instance bindings during Painless whitelisting.

Annotations are specified as @annotation or optionally as
@annotation[parameter="argument",...].

Annotations open up the ability to specify whitelist objects as
having a short name (no_import -> @no_import) or deprecated.
This commit is contained in:
Jack Conradson 2019-06-24 09:00:44 -07:00
parent eaa9ee1f16
commit 5eb044e635
26 changed files with 821 additions and 92 deletions

View File

@ -19,6 +19,8 @@
package org.elasticsearch.painless.spi;
import org.elasticsearch.painless.spi.annotation.WhitelistAnnotationParser;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@ -52,7 +54,8 @@ public final class Whitelist {
};
public static final List<Whitelist> BASE_WHITELISTS =
Collections.singletonList(WhitelistLoader.loadFromResourceFiles(Whitelist.class, BASE_WHITELIST_FILES));
Collections.singletonList(WhitelistLoader.loadFromResourceFiles(
Whitelist.class, WhitelistAnnotationParser.BASE_ANNOTATION_PARSERS, BASE_WHITELIST_FILES));
/** The {@link ClassLoader} used to look up the whitelisted Java classes, constructors, methods, and fields. */
public final ClassLoader classLoader;

View File

@ -19,9 +19,12 @@
package org.elasticsearch.painless.spi;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* Class represents the equivalent of a Java class in Painless complete with super classes,
@ -46,11 +49,6 @@ public final class WhitelistClass {
/** The Java class name this class represents. */
public final String javaClassName;
/**
* Allow the Java class name to only be specified as the fully-qualified name.
*/
public final boolean noImport;
/** The {@link List} of whitelisted ({@link WhitelistConstructor}s) available to this class. */
public final List<WhitelistConstructor> whitelistConstructors;
@ -60,17 +58,27 @@ public final class WhitelistClass {
/** The {@link List} of whitelisted ({@link WhitelistField}s) available to this class. */
public final List<WhitelistField> whitelistFields;
/** The {@link Map} of annotations for this class. */
public final Map<Class<?>, Object> painlessAnnotations;
/** Standard constructor. All values must be not {@code null}. */
public WhitelistClass(String origin, String javaClassName, boolean noImport,
List<WhitelistConstructor> whitelistConstructors, List<WhitelistMethod> whitelistMethods, List<WhitelistField> whitelistFields)
{
public WhitelistClass(String origin, String javaClassName,
List<WhitelistConstructor> whitelistConstructors, List<WhitelistMethod> whitelistMethods, List<WhitelistField> whitelistFields,
List<Object> painlessAnnotations) {
this.origin = Objects.requireNonNull(origin);
this.javaClassName = Objects.requireNonNull(javaClassName);
this.noImport = noImport;
this.whitelistConstructors = Collections.unmodifiableList(Objects.requireNonNull(whitelistConstructors));
this.whitelistMethods = Collections.unmodifiableList(Objects.requireNonNull(whitelistMethods));
this.whitelistFields = Collections.unmodifiableList(Objects.requireNonNull(whitelistFields));
if (painlessAnnotations.isEmpty()) {
this.painlessAnnotations = Collections.emptyMap();
} else {
this.painlessAnnotations = Collections.unmodifiableMap(Objects.requireNonNull(painlessAnnotations).stream()
.map(painlessAnnotation -> new AbstractMap.SimpleEntry<>(painlessAnnotation.getClass(), painlessAnnotation))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
}
}
}

View File

@ -19,8 +19,12 @@
package org.elasticsearch.painless.spi;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* A class binding represents a method call that stores state. Each class binding's Java class must
@ -51,9 +55,13 @@ public class WhitelistClassBinding {
*/
public final List<String> canonicalTypeNameParameters;
/** The {@link Map} of annotations for this class binding. */
public final Map<Class<?>, Object> painlessAnnotations;
/** Standard constructor. All values must be not {@code null}. */
public WhitelistClassBinding(String origin, String targetJavaClassName,
String methodName, String returnCanonicalTypeName, List<String> canonicalTypeNameParameters) {
String methodName, String returnCanonicalTypeName, List<String> canonicalTypeNameParameters,
List<Object> painlessAnnotations) {
this.origin = Objects.requireNonNull(origin);
this.targetJavaClassName = Objects.requireNonNull(targetJavaClassName);
@ -61,5 +69,13 @@ public class WhitelistClassBinding {
this.methodName = Objects.requireNonNull(methodName);
this.returnCanonicalTypeName = Objects.requireNonNull(returnCanonicalTypeName);
this.canonicalTypeNameParameters = Objects.requireNonNull(canonicalTypeNameParameters);
if (painlessAnnotations.isEmpty()) {
this.painlessAnnotations = Collections.emptyMap();
} else {
this.painlessAnnotations = Collections.unmodifiableMap(Objects.requireNonNull(painlessAnnotations).stream()
.map(painlessAnnotation -> new AbstractMap.SimpleEntry<>(painlessAnnotation.getClass(), painlessAnnotation))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
}
}
}

View File

@ -19,9 +19,12 @@
package org.elasticsearch.painless.spi;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* Constructor represents the equivalent of a Java constructor available as a whitelisted class
@ -40,9 +43,20 @@ public final class WhitelistConstructor {
*/
public final List<String> canonicalTypeNameParameters;
/** The {@link Map} of annotations for this constructor. */
public final Map<Class<?>, Object> painlessAnnotations;
/** Standard constructor. All values must be not {@code null}. */
public WhitelistConstructor(String origin, List<String> canonicalTypeNameParameters) {
public WhitelistConstructor(String origin, List<String> canonicalTypeNameParameters, List<Object> painlessAnnotations) {
this.origin = Objects.requireNonNull(origin);
this.canonicalTypeNameParameters = Collections.unmodifiableList(Objects.requireNonNull(canonicalTypeNameParameters));
if (painlessAnnotations.isEmpty()) {
this.painlessAnnotations = Collections.emptyMap();
} else {
this.painlessAnnotations = Collections.unmodifiableMap(Objects.requireNonNull(painlessAnnotations).stream()
.map(painlessAnnotation -> new AbstractMap.SimpleEntry<>(painlessAnnotation.getClass(), painlessAnnotation))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
}
}
}

View File

@ -19,7 +19,12 @@
package org.elasticsearch.painless.spi;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* Field represents the equivalent of a Java field available as a whitelisted class field
@ -37,10 +42,21 @@ public class WhitelistField {
/** The canonical type name for the field which can be used to look up the Java field through reflection. */
public final String canonicalTypeNameParameter;
/** The {@link Map} of annotations for this field. */
public final Map<Class<?>, Object> painlessAnnotations;
/** Standard constructor. All values must be not {@code null}. */
public WhitelistField(String origin, String fieldName, String canonicalTypeNameParameter) {
public WhitelistField(String origin, String fieldName, String canonicalTypeNameParameter, List<Object> painlessAnnotations) {
this.origin = Objects.requireNonNull(origin);
this.fieldName = Objects.requireNonNull(fieldName);
this.canonicalTypeNameParameter = Objects.requireNonNull(canonicalTypeNameParameter);
if (painlessAnnotations.isEmpty()) {
this.painlessAnnotations = Collections.emptyMap();
} else {
this.painlessAnnotations = Collections.unmodifiableMap(Objects.requireNonNull(painlessAnnotations).stream()
.map(painlessAnnotation -> new AbstractMap.SimpleEntry<>(painlessAnnotation.getClass(), painlessAnnotation))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
}
}
}

View File

@ -19,8 +19,12 @@
package org.elasticsearch.painless.spi;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* An instance binding represents a method call that stores state. Each instance binding must provide
@ -47,9 +51,13 @@ public class WhitelistInstanceBinding {
*/
public final List<String> canonicalTypeNameParameters;
/** The {@link Map} of annotations for this instance binding. */
public final Map<Class<?>, Object> painlessAnnotations;
/** Standard constructor. All values must be not {@code null}. */
public WhitelistInstanceBinding(String origin, Object targetInstance,
String methodName, String returnCanonicalTypeName, List<String> canonicalTypeNameParameters) {
String methodName, String returnCanonicalTypeName, List<String> canonicalTypeNameParameters,
List<Object> painlessAnnotations) {
this.origin = Objects.requireNonNull(origin);
this.targetInstance = Objects.requireNonNull(targetInstance);
@ -57,5 +65,13 @@ public class WhitelistInstanceBinding {
this.methodName = Objects.requireNonNull(methodName);
this.returnCanonicalTypeName = Objects.requireNonNull(returnCanonicalTypeName);
this.canonicalTypeNameParameters = Objects.requireNonNull(canonicalTypeNameParameters);
if (painlessAnnotations.isEmpty()) {
this.painlessAnnotations = Collections.emptyMap();
} else {
this.painlessAnnotations = Collections.unmodifiableMap(Objects.requireNonNull(painlessAnnotations).stream()
.map(painlessAnnotation -> new AbstractMap.SimpleEntry<>(painlessAnnotation.getClass(), painlessAnnotation))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
}
}
}

View File

@ -19,6 +19,8 @@
package org.elasticsearch.painless.spi;
import org.elasticsearch.painless.spi.annotation.WhitelistAnnotationParser;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.lang.reflect.Constructor;
@ -30,11 +32,22 @@ import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** Loads and creates a {@link Whitelist} from one to many text files. */
public final class WhitelistLoader {
/**
* Loads and creates a {@link Whitelist} from one to many text files using only the base annotation parsers.
* See {@link #loadFromResourceFiles(Class, Map, String...)} for information on how to structure a whitelist
* text file.
*/
public static Whitelist loadFromResourceFiles(Class<?> resource, String... filepaths) {
return loadFromResourceFiles(resource, WhitelistAnnotationParser.BASE_ANNOTATION_PARSERS, filepaths);
}
/**
* Loads and creates a {@link Whitelist} from one to many text files. The file paths are passed in as an array of
* {@link String}s with a single {@link Class} to be be used to load the resources where each {@link String}
@ -54,7 +67,7 @@ public final class WhitelistLoader {
* a Painless type name with the exception that any dollar symbols used as part of inner classes will
* be replaced with dot symbols. </li>
* <li> short Java type name - The text after the final dot symbol of any specified Java class. A
* short type Java name may be excluded by using the 'no_import' token during Painless class parsing
* short type Java name may be excluded by using the 'no_import' attribute during Painless class parsing
* as described later. </li>
* </ul>
*
@ -65,8 +78,8 @@ public final class WhitelistLoader {
* be ignored by the parser. </li>
* <li> Primitive types may be specified starting with 'class' and followed by the Java type name,
* an opening bracket, a newline, a closing bracket, and a final newline. </li>
* <li> Complex types may be specified starting with 'class' and followed the fully-qualified Java
* class name, optionally followed by an 'no_import' token, an opening bracket, a newline,
* <li> Complex types may be specified starting with 'class' and followed by the fully-qualified Java
* class name, optionally followed by a 'no_import' attribute, an opening bracket, a newline,
* constructor/method/field specifications, a closing bracket, and a final newline. Within a complex
* type the following may be parsed:
* <ul>
@ -90,6 +103,10 @@ public final class WhitelistLoader {
* of the field, followed by the Java name of the field (which all be the Painless name
* for the field), and a newline. </li>
* </ul>
* <li> Annotations may be added starting with an at, followed by a name, optionally an opening brace,
* a parameter name, an equals, an opening quote, an argument value, a closing quote, (possibly repeated
* for multiple arguments,) and a closing brace. Multiple annotations may be added after a class (before
* the opening bracket), after a method, or after field. </li>
* </ul>
*
* Note there must be a one-to-one correspondence of Painless type names to Java type/class names.
@ -110,7 +127,7 @@ public final class WhitelistLoader {
*
* # complex types
*
* class my.package.Example no_import {
* class my.package.Example @no_import {
* # constructors
* ()
* (int)
@ -120,7 +137,8 @@ public final class WhitelistLoader {
* # method
* Example add(int, def)
* int add(Example, Example)
* void example()
* void example() @deprecated[use example 2 instead]
* void example2()
*
* # augmented
* Example some.other.Class sub(Example, int, def)
@ -132,7 +150,7 @@ public final class WhitelistLoader {
* }
* }
*/
public static Whitelist loadFromResourceFiles(Class<?> resource, String... filepaths) {
public static Whitelist loadFromResourceFiles(Class<?> resource, Map<String, WhitelistAnnotationParser> parsers, String... filepaths) {
List<WhitelistClass> whitelistClasses = new ArrayList<>();
List<WhitelistMethod> whitelistStatics = new ArrayList<>();
List<WhitelistClassBinding> whitelistClassBindings = new ArrayList<>();
@ -149,10 +167,10 @@ public final class WhitelistLoader {
String parseType = null;
String whitelistClassOrigin = null;
String javaClassName = null;
boolean noImport = false;
List<WhitelistConstructor> whitelistConstructors = null;
List<WhitelistMethod> whitelistMethods = null;
List<WhitelistField> whitelistFields = null;
List<Object> classAnnotations = null;
while ((line = reader.readLine()) != null) {
number = reader.getLineNumber();
@ -164,7 +182,7 @@ public final class WhitelistLoader {
}
// Handle a new class by resetting all the variables necessary to construct a new WhitelistClass for the whitelist.
// Expects the following format: 'class' ID 'no_import'? '{' '\n'
// Expects the following format: 'class' ID annotations? '{' '\n'
if (line.startsWith("class ")) {
// Ensure the final token of the line is '{'.
if (line.endsWith("{") == false) {
@ -176,19 +194,19 @@ public final class WhitelistLoader {
throw new IllegalArgumentException("invalid definition: cannot embed class definition [" + line + "]");
}
// Parse the Java class name.
String[] tokens = line.substring(5, line.length() - 1).trim().split("\\s+");
// Parse the Java class name and annotations if they exist.
int annotationIndex = line.indexOf('@');
// Ensure the correct number of tokens.
if (tokens.length == 2 && "no_import".equals(tokens[1])) {
noImport = true;
} else if (tokens.length != 1) {
throw new IllegalArgumentException("invalid class definition: failed to parse class name [" + line + "]");
if (annotationIndex == -1) {
annotationIndex = line.length() - 1;
classAnnotations = Collections.emptyList();
} else {
classAnnotations = parseWhitelistAnnotations(parsers, line.substring(annotationIndex, line.length() - 1));
}
parseType = "class";
whitelistClassOrigin = "[" + filepath + "]:[" + number + "]";
javaClassName = tokens[0];
javaClassName = line.substring(5, annotationIndex).trim();
// Reset all the constructors, methods, and fields to support a new class.
whitelistConstructors = new ArrayList<>();
@ -217,22 +235,22 @@ public final class WhitelistLoader {
// Create a new WhitelistClass with all the previously gathered constructors, methods,
// augmented methods, and fields, and add it to the list of whitelisted classes.
if ("class".equals(parseType)) {
whitelistClasses.add(new WhitelistClass(whitelistClassOrigin, javaClassName, noImport,
whitelistConstructors, whitelistMethods, whitelistFields));
whitelistClasses.add(new WhitelistClass(whitelistClassOrigin, javaClassName,
whitelistConstructors, whitelistMethods, whitelistFields, classAnnotations));
whitelistClassOrigin = null;
javaClassName = null;
noImport = false;
whitelistConstructors = null;
whitelistMethods = null;
whitelistFields = null;
classAnnotations = null;
}
// Reset the parseType.
parseType = null;
// Handle static import definition types.
// Expects the following format: ID ID '(' ( ID ( ',' ID )* )? ')' ( 'from_class' | 'bound_to' ) ID '\n'
// Expects the following format: ID ID '(' ( ID ( ',' ID )* )? ')' ( 'from_class' | 'bound_to' ) ID annotations? '\n'
} else if ("static_import".equals(parseType)) {
// Mark the origin of this parsable object.
String origin = "[" + filepath + "]:[" + number + "]";
@ -274,8 +292,19 @@ public final class WhitelistLoader {
canonicalTypeNameParameters = new String[0];
}
// Parse the annotations if they exist.
List<Object> annotations;
int annotationIndex = line.indexOf('@');
if (annotationIndex == -1) {
annotationIndex = line.length();
annotations = Collections.emptyList();
} else {
annotations = parseWhitelistAnnotations(parsers, line.substring(annotationIndex));
}
// Parse the static import type and class.
tokens = line.substring(parameterEndIndex + 1).trim().split("\\s+");
tokens = line.substring(parameterEndIndex + 1, annotationIndex).trim().split("\\s+");
String staticImportType;
String targetJavaClassName;
@ -291,10 +320,12 @@ public final class WhitelistLoader {
// Add a static import method or binding depending on the static import type.
if ("from_class".equals(staticImportType)) {
whitelistStatics.add(new WhitelistMethod(origin, targetJavaClassName,
methodName, returnCanonicalTypeName, Arrays.asList(canonicalTypeNameParameters)));
methodName, returnCanonicalTypeName, Arrays.asList(canonicalTypeNameParameters),
annotations));
} else if ("bound_to".equals(staticImportType)) {
whitelistClassBindings.add(new WhitelistClassBinding(origin, targetJavaClassName,
methodName, returnCanonicalTypeName, Arrays.asList(canonicalTypeNameParameters)));
methodName, returnCanonicalTypeName, Arrays.asList(canonicalTypeNameParameters),
annotations));
} else {
throw new IllegalArgumentException("invalid static import definition: " +
"unexpected static import type [" + staticImportType + "] [" + line + "]");
@ -306,36 +337,38 @@ public final class WhitelistLoader {
String origin = "[" + filepath + "]:[" + number + "]";
// Handle the case for a constructor definition.
// Expects the following format: '(' ( ID ( ',' ID )* )? ')' '\n'
// Expects the following format: '(' ( ID ( ',' ID )* )? ')' annotations? '\n'
if (line.startsWith("(")) {
// Ensure the final token of the line is ')'.
if (line.endsWith(")") == false) {
// Parse the constructor parameters.
int parameterEndIndex = line.indexOf(')');
if (parameterEndIndex == -1) {
throw new IllegalArgumentException(
"invalid constructor definition: expected a closing parenthesis [" + line + "]");
"illegal constructor definition: end of constructor parameters not found [" + line + "]");
}
// Parse the constructor parameters.
String[] tokens = line.substring(1, line.length() - 1).replaceAll("\\s+", "").split(",");
String[] canonicalTypeNameParameters = line.substring(1, parameterEndIndex).replaceAll("\\s+", "").split(",");
// Handle the case for a constructor with no parameters.
if ("".equals(tokens[0])) {
tokens = new String[0];
if ("".equals(canonicalTypeNameParameters[0])) {
canonicalTypeNameParameters = new String[0];
}
whitelistConstructors.add(new WhitelistConstructor(origin, Arrays.asList(tokens)));
// Parse the annotations if they exist.
List<Object> annotations;
int annotationIndex = line.indexOf('@');
annotations = annotationIndex == -1 ?
Collections.emptyList() : parseWhitelistAnnotations(parsers, line.substring(annotationIndex));
// Handle the case for a method or augmented method definition.
// Expects the following format: ID ID? ID '(' ( ID ( ',' ID )* )? ')' '\n'
whitelistConstructors.add(new WhitelistConstructor(
origin, Arrays.asList(canonicalTypeNameParameters), annotations));
// Handle the case for a method or augmented method definition.
// Expects the following format: ID ID? ID '(' ( ID ( ',' ID )* )? ')' annotations? '\n'
} else if (line.contains("(")) {
// Ensure the final token of the line is ')'.
if (line.endsWith(")") == false) {
throw new IllegalArgumentException(
"invalid method definition: expected a closing parenthesis [" + line + "]");
}
// Parse the tokens prior to the method parameters.
int parameterIndex = line.indexOf('(');
String[] tokens = line.substring(0, parameterIndex).trim().split("\\s+");
int parameterStartIndex = line.indexOf('(');
String[] tokens = line.substring(0, parameterStartIndex).trim().split("\\s+");
String methodName;
String javaAugmentedClassName;
@ -354,28 +387,54 @@ public final class WhitelistLoader {
String returnCanonicalTypeName = tokens[0];
// Parse the method parameters.
tokens = line.substring(parameterIndex + 1, line.length() - 1).replaceAll("\\s+", "").split(",");
int parameterEndIndex = line.indexOf(')');
// Handle the case for a method with no parameters.
if ("".equals(tokens[0])) {
tokens = new String[0];
if (parameterEndIndex == -1) {
throw new IllegalArgumentException(
"illegal static import definition: end of method parameters not found [" + line + "]");
}
whitelistMethods.add(new WhitelistMethod(origin, javaAugmentedClassName, methodName,
returnCanonicalTypeName, Arrays.asList(tokens)));
String[] canonicalTypeNameParameters =
line.substring(parameterStartIndex + 1, parameterEndIndex).replaceAll("\\s+", "").split(",");
// Handle the case for a field definition.
// Expects the following format: ID ID '\n'
// Handle the case for a method with no parameters.
if ("".equals(canonicalTypeNameParameters[0])) {
canonicalTypeNameParameters = new String[0];
}
// Parse the annotations if they exist.
List<Object> annotations;
int annotationIndex = line.indexOf('@');
annotations = annotationIndex == -1 ?
Collections.emptyList() : parseWhitelistAnnotations(parsers, line.substring(annotationIndex));
whitelistMethods.add(new WhitelistMethod(origin, javaAugmentedClassName, methodName,
returnCanonicalTypeName, Arrays.asList(canonicalTypeNameParameters),
annotations));
// Handle the case for a field definition.
// Expects the following format: ID ID annotations? '\n'
} else {
// Parse the annotations if they exist.
List<Object> annotations;
int annotationIndex = line.indexOf('@');
if (annotationIndex == -1) {
annotationIndex = line.length();
annotations = Collections.emptyList();
} else {
annotations = parseWhitelistAnnotations(parsers, line.substring(annotationIndex));
}
// Parse the field tokens.
String[] tokens = line.split("\\s+");
String[] tokens = line.substring(0, annotationIndex).split("\\s+");
// Ensure the correct number of tokens.
if (tokens.length != 2) {
throw new IllegalArgumentException("invalid field definition: unexpected format [" + line + "]");
}
whitelistFields.add(new WhitelistField(origin, tokens[1], tokens[0]));
whitelistFields.add(new WhitelistField(origin, tokens[1], tokens[0], annotations));
}
} else {
throw new IllegalArgumentException("invalid definition: unable to parse line [" + line + "]");
@ -396,5 +455,85 @@ public final class WhitelistLoader {
return new Whitelist(loader, whitelistClasses, whitelistStatics, whitelistClassBindings, Collections.emptyList());
}
private static List<Object> parseWhitelistAnnotations(
Map<String, WhitelistAnnotationParser> parsers, String line) {
List<Object> annotations;
line = line.trim();
if (line.isEmpty()) {
annotations = Collections.emptyList();
} else {
if (line.charAt(0) != '@') {
throw new IllegalArgumentException("invalid annotation: expected at symbol [" + line + "]");
}
if (line.length() < 2) {
throw new IllegalArgumentException("invalid annotation: expected name [" + line + "]");
}
String[] annotationStrings = line.substring(1).split("@");
annotations = new ArrayList<>(annotationStrings.length);
for (String annotationString : annotationStrings) {
String name;
Map<String, String> arguments;
annotationString = annotationString.trim();
int index = annotationString.indexOf('[');
if (index == -1) {
name = annotationString;
arguments = Collections.emptyMap();
} else {
if (annotationString.charAt(annotationString.length() - 1) != ']') {
throw new IllegalArgumentException("invalid annotation: expected closing brace [" + line + "]");
}
name = annotationString.substring(0, index);
arguments = new HashMap<>();
String[] argumentsStrings = annotationString.substring(index + 1, annotationString.length() - 1).split(",");
for (String argumentString : argumentsStrings) {
String[] argumentKeyValue = argumentString.split("=");
if (argumentKeyValue.length != 2) {
throw new IllegalArgumentException("invalid annotation: expected key=\"value\" [" + line + "]");
}
String argumentKey = argumentKeyValue[0].trim();
if (argumentKey.isEmpty()) {
throw new IllegalArgumentException("invalid annotation: expected key=\"value\" [" + line + "]");
}
String argumentValue = argumentKeyValue[1];
if (argumentValue.length() < 3 || argumentValue.charAt(0) != '"' ||
argumentValue.charAt(argumentValue.length() - 1) != '"') {
throw new IllegalArgumentException("invalid annotation: expected key=\"value\" [" + line + "]");
}
argumentValue = argumentValue.substring(1, argumentValue.length() - 1);
arguments.put(argumentKey, argumentValue);
}
}
WhitelistAnnotationParser parser = parsers.get(name);
if (parser == null) {
throw new IllegalArgumentException("invalid annotation: parser not found for [" + name + "] [" + line + "]");
}
annotations.add(parser.parse(arguments));
}
}
return annotations;
}
private WhitelistLoader() {}
}

View File

@ -19,9 +19,12 @@
package org.elasticsearch.painless.spi;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* Method represents the equivalent of a Java method available as a whitelisted class method
@ -61,18 +64,30 @@ public class WhitelistMethod {
*/
public final List<String> canonicalTypeNameParameters;
/** The {@link Map} of annotations for this method. */
public final Map<Class<?>, Object> painlessAnnotations;
/**
* Standard constructor. All values must be not {@code null} with the exception of
* augmentedCanonicalClassName; augmentedCanonicalClassName will be {@code null} unless the method
* is augmented as described in the class documentation.
*/
public WhitelistMethod(String origin, String augmentedCanonicalClassName, String methodName,
String returnCanonicalTypeName, List<String> canonicalTypeNameParameters) {
String returnCanonicalTypeName, List<String> canonicalTypeNameParameters,
List<Object> painlessAnnotations) {
this.origin = Objects.requireNonNull(origin);
this.augmentedCanonicalClassName = augmentedCanonicalClassName;
this.methodName = methodName;
this.returnCanonicalTypeName = Objects.requireNonNull(returnCanonicalTypeName);
this.canonicalTypeNameParameters = Collections.unmodifiableList(Objects.requireNonNull(canonicalTypeNameParameters));
if (painlessAnnotations.isEmpty()) {
this.painlessAnnotations = Collections.emptyMap();
} else {
this.painlessAnnotations = Collections.unmodifiableMap(Objects.requireNonNull(painlessAnnotations).stream()
.map(painlessAnnotation -> new AbstractMap.SimpleEntry<>(painlessAnnotation.getClass(), painlessAnnotation))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
}
}
}

View File

@ -0,0 +1,35 @@
/*
* 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.painless.spi.annotation;
public class DeprecatedAnnotation {
public static final String NAME = "deprecated";
private final String message;
public DeprecatedAnnotation(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}

View File

@ -0,0 +1,44 @@
/*
* 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.painless.spi.annotation;
import java.util.Map;
public class DeprecatedAnnotationParser implements WhitelistAnnotationParser {
public static final DeprecatedAnnotationParser INSTANCE = new DeprecatedAnnotationParser();
public static final String MESSAGE = "message";
private DeprecatedAnnotationParser() {
}
@Override
public Object parse(Map<String, String> arguments) {
String message = arguments.getOrDefault(MESSAGE, "");
if ((arguments.isEmpty() || arguments.size() == 1 && arguments.containsKey(MESSAGE)) == false) {
throw new IllegalArgumentException("unexpected parameters for [@deprecation] annotation, found " + arguments);
}
return new DeprecatedAnnotation(message);
}
}

View File

@ -0,0 +1,31 @@
/*
* 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.painless.spi.annotation;
public class NoImportAnnotation {
public static final String NAME = "no_import";
public static final NoImportAnnotation INSTANCE = new NoImportAnnotation();
private NoImportAnnotation() {
}
}

View File

@ -0,0 +1,40 @@
/*
* 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.painless.spi.annotation;
import java.util.Map;
public class NoImportAnnotationParser implements WhitelistAnnotationParser {
public static final NoImportAnnotationParser INSTANCE = new NoImportAnnotationParser();
private NoImportAnnotationParser() {
}
@Override
public Object parse(Map<String, String> arguments) {
if (arguments.isEmpty() == false) {
throw new IllegalArgumentException("unexpected parameters for [@no_import] annotation, found " + arguments);
}
return NoImportAnnotation.INSTANCE;
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.painless.spi.annotation;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* WhitelistAnnotationParser is an interface used to define how to
* parse an annotation against any whitelist object while loading.
*/
public interface WhitelistAnnotationParser {
Map<String, WhitelistAnnotationParser> BASE_ANNOTATION_PARSERS = Collections.unmodifiableMap(
Stream.of(
new AbstractMap.SimpleEntry<>(NoImportAnnotation.NAME, NoImportAnnotationParser.INSTANCE),
new AbstractMap.SimpleEntry<>(DeprecatedAnnotation.NAME, DeprecatedAnnotationParser.INSTANCE)
).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
);
Object parse(Map<String, String> arguments);
}

View File

@ -30,6 +30,7 @@ import org.elasticsearch.painless.spi.WhitelistConstructor;
import org.elasticsearch.painless.spi.WhitelistField;
import org.elasticsearch.painless.spi.WhitelistInstanceBinding;
import org.elasticsearch.painless.spi.WhitelistMethod;
import org.elasticsearch.painless.spi.annotation.NoImportAnnotation;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.GeneratorAdapter;
@ -119,7 +120,8 @@ public final class PainlessLookupBuilder {
for (WhitelistClass whitelistClass : whitelist.whitelistClasses) {
origin = whitelistClass.origin;
painlessLookupBuilder.addPainlessClass(
whitelist.classLoader, whitelistClass.javaClassName, whitelistClass.noImport == false);
whitelist.classLoader, whitelistClass.javaClassName,
whitelistClass.painlessAnnotations.containsKey(NoImportAnnotation.class) == false);
}
}

View File

@ -19,7 +19,7 @@
# This file contains a whitelist for functions to be used in Score context
class org.elasticsearch.script.ScoreScript no_import {
class org.elasticsearch.script.ScoreScript @no_import {
}
static_import {
@ -27,10 +27,10 @@ static_import {
double sigmoid(double, double, double) from_class org.elasticsearch.script.ScoreScriptUtils
double randomScore(org.elasticsearch.script.ScoreScript, int, String) bound_to org.elasticsearch.script.ScoreScriptUtils$RandomScoreField
double randomScore(org.elasticsearch.script.ScoreScript, int) bound_to org.elasticsearch.script.ScoreScriptUtils$RandomScoreDoc
double decayGeoLinear(String, String, String, double, GeoPoint) bound_to org.elasticsearch.script.ScoreScriptUtils$DecayGeoLinear
double decayGeoExp(String, String, String, double, GeoPoint) bound_to org.elasticsearch.script.ScoreScriptUtils$DecayGeoExp
double decayGeoGauss(String, String, String, double, GeoPoint) bound_to org.elasticsearch.script.ScoreScriptUtils$DecayGeoGauss
double decayNumericLinear(double, double, double, double, double) bound_to org.elasticsearch.script.ScoreScriptUtils$DecayNumericLinear
double decayGeoLinear(String, String, String, double, GeoPoint) bound_to org.elasticsearch.script.ScoreScriptUtils$DecayGeoLinear
double decayGeoExp(String, String, String, double, GeoPoint) bound_to org.elasticsearch.script.ScoreScriptUtils$DecayGeoExp
double decayGeoGauss(String, String, String, double, GeoPoint) bound_to org.elasticsearch.script.ScoreScriptUtils$DecayGeoGauss
double decayNumericLinear(double, double, double, double, double)bound_to org.elasticsearch.script.ScoreScriptUtils$DecayNumericLinear
double decayNumericExp(double, double, double, double, double) bound_to org.elasticsearch.script.ScoreScriptUtils$DecayNumericExp
double decayNumericGauss(double, double, double, double, double) bound_to org.elasticsearch.script.ScoreScriptUtils$DecayNumericGauss
double decayDateLinear(String, String, String, double, JodaCompatibleZonedDateTime) bound_to org.elasticsearch.script.ScoreScriptUtils$DecayDateLinear
@ -38,4 +38,3 @@ static_import {
double decayDateGauss(String, String, String, double, JodaCompatibleZonedDateTime) bound_to org.elasticsearch.script.ScoreScriptUtils$DecayDateGauss
}

View File

@ -24,31 +24,31 @@
#### Primitive types
class void no_import {
class void @no_import {
}
class boolean no_import {
class boolean @no_import {
}
class byte no_import {
class byte @no_import {
}
class short no_import {
class short @no_import {
}
class char no_import {
class char @no_import {
}
class int no_import {
class int @no_import {
}
class long no_import {
class long @no_import {
}
class float no_import {
class float @no_import {
}
class double no_import {
class double @no_import {
}
#### Painless debugging API

View File

@ -0,0 +1,102 @@
/*
* 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.painless;
import org.elasticsearch.painless.spi.annotation.WhitelistAnnotationParser;
import java.util.Map;
public class AnnotationTestObject {
public static class TestAnnotation {
public static final String NAME = "test_annotation";
private final String one;
private final String two;
private final String three;
public TestAnnotation(String one, String two, String three) {
this.one = one;
this.two = two;
this.three = three;
}
public String getOne() {
return one;
}
public String getTwo() {
return two;
}
public String getThree() {
return three;
}
}
public static class TestAnnotationParser implements WhitelistAnnotationParser {
public static final TestAnnotationParser INSTANCE = new TestAnnotationParser();
private TestAnnotationParser() {
}
@Override
public Object parse(Map<String, String> arguments) {
if (arguments.size() != 3) {
throw new IllegalArgumentException("expected three arguments");
}
String one = arguments.get("one");
if (one == null) {
throw new IllegalArgumentException("missing one");
}
String two = arguments.get("two");
if (two == null) {
throw new IllegalArgumentException("missing two");
}
String three = arguments.get("three");
if (three == null) {
throw new IllegalArgumentException("missing three");
}
return new TestAnnotation(one, two, three);
}
}
public void deprecatedMethod() {
}
public void annotatedTestMethod() {
}
public void annotatedMultipleMethod() {
}
}

View File

@ -103,9 +103,9 @@ public class BindingsTests extends ScriptTestCase {
InstanceBindingTestClass instanceBindingTestClass = new InstanceBindingTestClass(1);
WhitelistInstanceBinding getter = new WhitelistInstanceBinding("test", instanceBindingTestClass,
"setInstanceBindingValue", "void", Collections.singletonList("int"));
"setInstanceBindingValue", "void", Collections.singletonList("int"), Collections.emptyList());
WhitelistInstanceBinding setter = new WhitelistInstanceBinding("test", instanceBindingTestClass,
"getInstanceBindingValue", "int", Collections.emptyList());
"getInstanceBindingValue", "int", Collections.emptyList(), Collections.emptyList());
List<WhitelistInstanceBinding> instanceBindingsList = new ArrayList<>();
instanceBindingsList.add(getter);
instanceBindingsList.add(setter);

View File

@ -0,0 +1,85 @@
/*
* 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.painless;
import org.elasticsearch.painless.spi.Whitelist;
import org.elasticsearch.painless.spi.WhitelistClass;
import org.elasticsearch.painless.spi.WhitelistLoader;
import org.elasticsearch.painless.spi.WhitelistMethod;
import org.elasticsearch.painless.spi.annotation.DeprecatedAnnotation;
import org.elasticsearch.painless.spi.annotation.NoImportAnnotation;
import org.elasticsearch.painless.spi.annotation.WhitelistAnnotationParser;
import java.util.HashMap;
import java.util.Map;
public class WhitelistLoaderTests extends ScriptTestCase {
public void testAnnotations() {
Map<String, WhitelistAnnotationParser> parsers = new HashMap<>(WhitelistAnnotationParser.BASE_ANNOTATION_PARSERS);
parsers.put(AnnotationTestObject.TestAnnotation.NAME, AnnotationTestObject.TestAnnotationParser.INSTANCE);
Whitelist whitelist = WhitelistLoader.loadFromResourceFiles(Whitelist.class, parsers, "org.elasticsearch.painless.annotation");
assertEquals(1, whitelist.whitelistClasses.size());
WhitelistClass whitelistClass = whitelist.whitelistClasses.get(0);
assertNotNull(whitelistClass.painlessAnnotations.get(NoImportAnnotation.class));
assertEquals(1, whitelistClass.painlessAnnotations.size());
assertEquals(3, whitelistClass.whitelistMethods.size());
int count = 0;
for (WhitelistMethod whitelistMethod : whitelistClass.whitelistMethods) {
if ("deprecatedMethod".equals(whitelistMethod.methodName)) {
assertEquals("use another method",
((DeprecatedAnnotation)whitelistMethod.painlessAnnotations.get(DeprecatedAnnotation.class)).getMessage());
assertEquals(1, whitelistMethod.painlessAnnotations.size());
++count;
}
if ("annotatedTestMethod".equals(whitelistMethod.methodName)) {
AnnotationTestObject.TestAnnotation ta =
((AnnotationTestObject.TestAnnotation)whitelistMethod.painlessAnnotations.get(
AnnotationTestObject.TestAnnotation.class));
assertEquals("one", ta.getOne());
assertEquals("two", ta.getTwo());
assertEquals("three", ta.getThree());
assertEquals(1, whitelistMethod.painlessAnnotations.size());
++count;
}
if ("annotatedMultipleMethod".equals(whitelistMethod.methodName)) {
assertEquals("test",
((DeprecatedAnnotation)whitelistMethod.painlessAnnotations.get(DeprecatedAnnotation.class)).getMessage());
AnnotationTestObject.TestAnnotation ta =
((AnnotationTestObject.TestAnnotation)whitelistMethod.painlessAnnotations.get(
AnnotationTestObject.TestAnnotation.class));
assertEquals("one", ta.getOne());
assertEquals("two", ta.getTwo());
assertEquals("three", ta.getThree());
assertEquals(2, whitelistMethod.painlessAnnotations.size());
++count;
}
}
assertEquals(3, count);
}
}

View File

@ -0,0 +1,7 @@
# whitelist for annotation tests
class org.elasticsearch.painless.AnnotationTestObject @no_import {
void deprecatedMethod() @deprecated[message="use another method"]
void annotatedTestMethod() @test_annotation[one="one",two="two",three="three"]
void annotatedMultipleMethod() @test_annotation[one="one",two="two",three="three"] @deprecated[message="test"]
}

View File

@ -2,7 +2,7 @@
class org.elasticsearch.painless.BindingsTests$BindingsTestScript {
}
class org.elasticsearch.painless.FeatureTestObject no_import {
class org.elasticsearch.painless.FeatureTestObject @no_import {
int z
()
(int,int)

View File

@ -0,0 +1,41 @@
/*
* 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.example.painlesswhitelist;
public class ExamplePainlessAnnotation {
public static final String NAME = "example_annotation";
public int category;
public String message;
public ExamplePainlessAnnotation(int category, String message) {
this.category = category;
this.message = message;
}
public int getCategory() {
return category;
}
public String getMessage() {
return message;
}
}

View File

@ -0,0 +1,62 @@
/*
* 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.example.painlesswhitelist;
import org.elasticsearch.painless.spi.annotation.WhitelistAnnotationParser;
import java.util.Map;
public class ExampleWhitelistAnnotationParser implements WhitelistAnnotationParser {
public static final ExampleWhitelistAnnotationParser INSTANCE = new ExampleWhitelistAnnotationParser();
private ExampleWhitelistAnnotationParser() {
}
@Override
public Object parse(Map<String, String> arguments) {
if (arguments.size() != 2) {
throw new IllegalArgumentException("expected exactly two arguments");
}
String categoryString = arguments.get("category");
if (categoryString == null) {
throw new IllegalArgumentException("expected category argument");
}
int category;
try {
category = Integer.parseInt(categoryString);
} catch (NumberFormatException nfe) {
throw new IllegalArgumentException("expected category as an int, found [" + categoryString + "]", nfe);
}
String message = arguments.get("message");
if (categoryString == null) {
throw new IllegalArgumentException("expected message argument");
}
return new ExamplePainlessAnnotation(category, message);
}
}

View File

@ -23,30 +23,34 @@ import org.elasticsearch.painless.spi.PainlessExtension;
import org.elasticsearch.painless.spi.Whitelist;
import org.elasticsearch.painless.spi.WhitelistInstanceBinding;
import org.elasticsearch.painless.spi.WhitelistLoader;
import org.elasticsearch.painless.spi.annotation.WhitelistAnnotationParser;
import org.elasticsearch.script.FieldScript;
import org.elasticsearch.script.ScriptContext;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** An extension of painless which adds a whitelist. */
public class ExampleWhitelistExtension implements PainlessExtension {
private static final Whitelist WHITELIST =
WhitelistLoader.loadFromResourceFiles(ExampleWhitelistExtension.class, "example_whitelist.txt");
@Override
public Map<ScriptContext<?>, List<Whitelist>> getContextWhitelists() {
Map<String, WhitelistAnnotationParser> parsers = new HashMap<>(WhitelistAnnotationParser.BASE_ANNOTATION_PARSERS);
parsers.put(ExamplePainlessAnnotation.NAME, ExampleWhitelistAnnotationParser.INSTANCE);
Whitelist classWhitelist =
WhitelistLoader.loadFromResourceFiles(ExampleWhitelistExtension.class, parsers, "example_whitelist.txt");
ExampleWhitelistedInstance ewi = new ExampleWhitelistedInstance(1);
WhitelistInstanceBinding addValue = new WhitelistInstanceBinding("example addValue", ewi,
"addValue", "int", Collections.singletonList("int"));
"addValue", "int", Collections.singletonList("int"), Collections.emptyList());
WhitelistInstanceBinding getValue = new WhitelistInstanceBinding("example getValue", ewi,
"getValue", "int", Collections.emptyList());
"getValue", "int", Collections.emptyList(), Collections.emptyList());
Whitelist instanceWhitelist = new Whitelist(ewi.getClass().getClassLoader(), Collections.emptyList(),
Collections.emptyList(), Collections.emptyList(), Arrays.asList(addValue, getValue));
return Collections.singletonMap(FieldScript.CONTEXT, Arrays.asList(WHITELIST, instanceWhitelist));
return Collections.singletonMap(FieldScript.CONTEXT, Arrays.asList(classWhitelist, instanceWhitelist));
}
}

View File

@ -54,4 +54,9 @@ public class ExampleWhitelistedClass {
public static int toInt(String x) {
return Integer.parseInt(x);
}
// example method to attach annotations in whitelist
public void annotate() {
// some logic here
}
}

View File

@ -33,6 +33,9 @@ class org.elasticsearch.example.painlesswhitelist.ExampleWhitelistedClass {
# getter and setter for private member
int getPrivateMemberAccessor()
void setPrivateMemberAccessor(int)
# annotation
void annotate() @example_annotation[category="1",message="example annotation"]
}
class java.lang.String {