Merge branch 'master' into ccr

* master:
  [TEST] Mute SlackMessageTests.testTemplateRender
  Docs: Explain closing the high level client
  [ML] Re-enable memory limit integration tests (#31328)
  [test] disable packaging tests for suse boxes
  Add nio transport to security plugin (#31942)
  XContentTests : Insert random fields at random positions (#30867)
  Force execution of fetch tasks (#31974)
  Fix unreachable error condition in AmazonS3Fixture (#32005)
  Tests: Fix SearchFieldsIT.testDocValueFields (#31995)
  Add Expected Reciprocal Rank metric (#31891)
  [ML] Get ForecastRequestStats doc in RestoreModelSnapshotIT (#31973)
  SQL: Add support for single parameter text manipulating functions (#31874)
  [ML] Ensure immutability of MlMetadata (#31957)
  Tests: Mute SearchFieldsIT.testDocValueFields()
  muted tests due to #31940
  Work around reported problem in eclipse (#31960)
  Move build integration tests out of :buildSrc project (#31961)
  Tests: Remove use of joda time in some tests (#31922)
  [Test] Reactive 3rd party tests on CI (#31919)
  SQL: Support for escape sequences (#31884)
  SQL: HAVING clause should accept only aggregates (#31872)
  Docs: fix typo in datehistogram (#31972)
  Switch url repository rest tests to new style requests (#31944)
  Switch reindex tests to new style requests (#31941)
  Docs: Added note about cloud service to installation and getting started
  [DOCS] Removes alternative docker pull example (#31934)
  Add Snapshots Status API to High Level Rest Client (#31515)
  ingest: date_index_name processor template resolution (#31841)
  Test: fix null failure in watcher test (#31968)
  Switch test framework to new style requests (#31939)
  Switch low level rest tests to new style Requests (#31938)
  Switch high level rest tests to new style requests (#31937)
  [ML] Mute test failing due to Java 11 date time format parsing bug (#31899)
  [TEST] Mute SlackMessageTests.testTemplateRender
  Fix assertIngestDocument wrongfully passing (#31913)
  Remove unused reference to filePermissionsCache (#31923)
  rolling upgrade should use a replica to prevent relocations while running a scroll
  HLREST: Bundle the x-pack protocol project (#31904)
  Increase logging level for testStressMaybeFlush
  Added lenient flag for synonym token filter (#31484)
  [X-Pack] Beats centralized management: security role + licensing (#30520)
  HLRest: Move xPackInfo() to xPack().info() (#31905)
  Docs: add security delete role to api call table (#31907)
  [test] port archive distribution packaging tests (#31314)
  Watcher: Slack message empty text (#31596)
  [ML] Mute failing DetectionRulesIT.testCondition() test
  Fix broken NaN check in MovingFunctions#stdDev() (#31888)
  Date: Add DateFormatters class that uses java.time (#31856)
  [ML] Switch native QA tests to a 3 node cluster (#31757)
  Change trappy float comparison (#31889)
  Fix building AD URL from domain name (#31849)
  Add opaque_id to audit logging (#31878)
  re-enable backcompat tests
  add support for is_write_index in put-alias body parsing (#31674)
  Improve release notes script (#31833)
  [DOCS] Fix broken link in painless example
  Handle missing values in painless (#30975)
  Remove the ability to index or query context suggestions without context (#31007)
  Ingest: Enable Templated Fieldnames in Rename (#31690)
  [Docs] Fix typo in the Rollup API Quick Reference (#31855)
  Ingest: Add ignore_missing option to RemoveProc (#31693)
  Add template config for Beat state to X-Pack Monitoring (#31809)
  Watcher: Add ssl.trust email account setting (#31684)
  Remove link to oss-MSI (#31844)
  Painless: Restructure Definition/Whitelist (#31879)
  HLREST: Add x-pack-info API (#31870)
This commit is contained in:
Nhat Nguyen 2018-07-12 17:33:57 -04:00
commit b1f5d361b3
343 changed files with 13944 additions and 4512 deletions

View File

@ -435,6 +435,9 @@ allprojects {
if (isEclipse) {
// set this so generated dirs will be relative to eclipse build
project.buildDir = eclipseBuild
// Work around https://docs.gradle.org/current/userguide/java_gradle_plugin.html confusing Eclipse by the metadata
// it adds to the classpath
project.file("$buildDir/pluginUnderTestMetadata").mkdirs()
}
eclipse.classpath.file.whenMerged { classpath ->
// give each source folder a unique corresponding output folder

View File

@ -128,6 +128,10 @@ if (project == rootProject) {
}
mavenCentral()
}
test {
include "**/*Tests.class"
exclude "**/*IT.class"
}
}
/*****************************************************************************
@ -152,6 +156,18 @@ if (project != rootProject) {
jarHell.enabled = false
thirdPartyAudit.enabled = false
// tests can't be run with randomized test runner
// it's fine as we run them as part of :buildSrc
test.enabled = false
task integTest(type: Test) {
exclude "**/*Tests.class"
include "**/*IT.class"
testClassesDirs = sourceSets.test.output.classesDirs
classpath = sourceSets.test.runtimeClasspath
inputs.dir(file("src/testKit"))
}
check.dependsOn(integTest)
// TODO: re-enable once randomizedtesting gradle code is published and removed from here
licenseHeaders.enabled = false

View File

@ -691,6 +691,7 @@ class BuildPlugin implements Plugin<Project> {
systemProperty 'tests.task', path
systemProperty 'tests.security.manager', 'true'
systemProperty 'jna.nosys', 'true'
systemProperty 'es.scripting.exception_for_missing_value', 'true'
// TODO: remove setting logging level via system property
systemProperty 'tests.logger.level', 'WARN'
for (Map.Entry<String, String> property : System.properties.entrySet()) {

View File

@ -526,7 +526,11 @@ class VagrantTestPlugin implements Plugin<Project> {
project.gradle.removeListener(batsPackagingReproListener)
}
if (project.extensions.esvagrant.boxes.contains(box)) {
packagingTest.dependsOn(batsPackagingTest)
// these tests are temporarily disabled for suse boxes while we debug an issue
// https://github.com/elastic/elasticsearch/issues/30295
if (box.equals("opensuse-42") == false && box.equals("sles-12") == false) {
packagingTest.dependsOn(batsPackagingTest)
}
}
}
@ -546,9 +550,15 @@ class VagrantTestPlugin implements Plugin<Project> {
javaPackagingTest.command = 'ssh'
javaPackagingTest.args = ['--command', 'sudo bash "$PACKAGING_TESTS/run-tests.sh"']
} else {
// powershell sessions run over winrm always run as administrator, whether --elevated is passed or not. however
// remote sessions have some restrictions on what they can do, such as impersonating another user (or the same user
// without administrator elevation), which we need to do for these tests. passing --elevated runs the session
// as a scheduled job locally on the vm as a true administrator to get around this limitation
//
// https://github.com/hashicorp/vagrant/blob/9c299a2a357fcf87f356bb9d56e18a037a53d138/plugins/communicators/winrm/communicator.rb#L195-L225
// https://devops-collective-inc.gitbooks.io/secrets-of-powershell-remoting/content/manuscript/accessing-remote-computers.html
javaPackagingTest.command = 'winrm'
// winrm commands run as administrator
javaPackagingTest.args = ['--command', 'powershell -File "$Env:PACKAGING_TESTS/run-tests.ps1"']
javaPackagingTest.args = ['--elevated', '--command', 'powershell -File "$Env:PACKAGING_TESTS/run-tests.ps1"']
}
TaskExecutionAdapter javaPackagingReproListener = createReproListener(project, javaPackagingTest.path)
@ -559,7 +569,11 @@ class VagrantTestPlugin implements Plugin<Project> {
project.gradle.removeListener(javaPackagingReproListener)
}
if (project.extensions.esvagrant.boxes.contains(box)) {
packagingTest.dependsOn(javaPackagingTest)
// these tests are temporarily disabled for suse boxes while we debug an issue
// https://github.com/elastic/elasticsearch/issues/30295
if (box.equals("opensuse-42") == false && box.equals("sles-12") == false) {
packagingTest.dependsOn(javaPackagingTest)
}
}
/*

View File

@ -1,5 +1,3 @@
import org.elasticsearch.gradle.precommit.PrecommitTasks
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
@ -18,29 +16,86 @@ import org.elasticsearch.gradle.precommit.PrecommitTasks
* specific language governing permissions and limitations
* under the License.
*/
import org.elasticsearch.gradle.precommit.PrecommitTasks
import org.gradle.api.XmlProvider
import org.gradle.api.publish.maven.MavenPublication
buildscript {
repositories {
maven {
url 'https://plugins.gradle.org/m2/'
}
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
}
}
apply plugin: 'elasticsearch.build'
apply plugin: 'elasticsearch.rest-test'
apply plugin: 'nebula.maven-base-publish'
apply plugin: 'nebula.maven-scm'
apply plugin: 'com.github.johnrengelman.shadow'
group = 'org.elasticsearch.client'
archivesBaseName = 'elasticsearch-rest-high-level-client'
publishing {
publications {
nebula {
artifactId = archivesBaseName
publications {
nebula(MavenPublication) {
artifact shadowJar
artifactId = archivesBaseName
/*
* Configure the pom to include the "shadow" as compile dependencies
* because that is how we're using them but remove all other dependencies
* because they've been shaded into the jar.
*/
pom.withXml { XmlProvider xml ->
Node root = xml.asNode()
root.remove(root.dependencies)
Node dependenciesNode = root.appendNode('dependencies')
project.configurations.shadow.allDependencies.each {
if (false == it instanceof SelfResolvingDependency) {
Node dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
dependencyNode.appendNode('scope', 'compile')
}
}
}
}
}
}
/*
* We need somewhere to configure dependencies that we don't wish to shade
* into the high level REST client. The shadow plugin creates a "shadow"
* configuration which is *almost* exactly that. It is never bundled into
* the shaded jar but is used for main source compilation. Unfortunately,
* by default it is not used for *test* source compilation and isn't used
* in tests at all. This change makes it available for test compilation.
* A change below makes it available for testing.
*/
sourceSets {
test {
compileClasspath += configurations.shadow
}
}
dependencies {
compile "org.elasticsearch:elasticsearch:${version}"
compile "org.elasticsearch.client:elasticsearch-rest-client:${version}"
compile "org.elasticsearch.plugin:parent-join-client:${version}"
compile "org.elasticsearch.plugin:aggs-matrix-stats-client:${version}"
compile "org.elasticsearch.plugin:rank-eval-client:${version}"
compile "org.elasticsearch.plugin:lang-mustache-client:${version}"
/*
* Everything in the "shadow" configuration is *not* copied into the
* shadowJar.
*/
shadow "org.elasticsearch:elasticsearch:${version}"
shadow "org.elasticsearch.client:elasticsearch-rest-client:${version}"
shadow "org.elasticsearch.plugin:parent-join-client:${version}"
shadow "org.elasticsearch.plugin:aggs-matrix-stats-client:${version}"
shadow "org.elasticsearch.plugin:rank-eval-client:${version}"
shadow "org.elasticsearch.plugin:lang-mustache-client:${version}"
compile project(':x-pack:protocol')
testCompile "org.elasticsearch.client:test:${version}"
testCompile "org.elasticsearch.test:framework:${version}"
@ -63,3 +118,48 @@ forbiddenApisMain {
signaturesURLs += [PrecommitTasks.getResource('/forbidden/http-signatures.txt')]
signaturesURLs += [file('src/main/resources/forbidden/rest-high-level-signatures.txt').toURI().toURL()]
}
shadowJar {
classifier = null
mergeServiceFiles()
}
// We don't need normal jar, we use shadow jar instead
jar.enabled = false
assemble.dependsOn shadowJar
javadoc {
/*
* Bundle all of the javadoc from all of the shaded projects into this one
* so we don't *have* to publish javadoc for all of the "client" jars.
*/
configurations.compile.dependencies.all { Dependency dep ->
Project p = dependencyToProject(dep)
if (p != null) {
evaluationDependsOn(p.path)
source += p.sourceSets.main.allJava
}
}
}
/*
* Use the jar for testing so we have tests of the bundled jar.
* Use the "shadow" configuration for testing because we need things
* in it.
*/
test {
classpath -= compileJava.outputs.files
classpath -= configurations.compile
classpath -= configurations.runtime
classpath += configurations.shadow
classpath += shadowJar.outputs.files
dependsOn shadowJar
}
integTestRunner {
classpath -= compileJava.outputs.files
classpath -= configurations.compile
classpath -= configurations.runtime
classpath += configurations.shadow
classpath += shadowJar.outputs.files
dependsOn shadowJar
}

View File

@ -43,6 +43,7 @@ import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequest;
@ -104,6 +105,7 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.VersionType;
import org.elasticsearch.index.rankeval.RankEvalRequest;
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
import org.elasticsearch.rest.action.search.RestSearchAction;
import org.elasticsearch.script.mustache.MultiSearchTemplateRequest;
import org.elasticsearch.script.mustache.SearchTemplateRequest;
@ -115,8 +117,10 @@ import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.EnumSet;
import java.util.Locale;
import java.util.StringJoiner;
import java.util.stream.Collectors;
final class RequestConverters {
static final XContentType REQUEST_BODY_CONTENT_TYPE = XContentType.JSON;
@ -960,6 +964,20 @@ final class RequestConverters {
return request;
}
static Request snapshotsStatus(SnapshotsStatusRequest snapshotsStatusRequest) {
String endpoint = new EndpointBuilder().addPathPartAsIs("_snapshot")
.addPathPart(snapshotsStatusRequest.repository())
.addCommaSeparatedPathParts(snapshotsStatusRequest.snapshots())
.addPathPartAsIs("_status")
.build();
Request request = new Request(HttpGet.METHOD_NAME, endpoint);
Params parameters = new Params(request);
parameters.withMasterTimeout(snapshotsStatusRequest.masterNodeTimeout());
parameters.withIgnoreUnavailable(snapshotsStatusRequest.ignoreUnavailable());
return request;
}
static Request deleteSnapshot(DeleteSnapshotRequest deleteSnapshotRequest) {
String endpoint = new EndpointBuilder().addPathPartAsIs("_snapshot")
.addPathPart(deleteSnapshotRequest.repository())
@ -1065,6 +1083,19 @@ final class RequestConverters {
return request;
}
static Request xPackInfo(XPackInfoRequest infoRequest) {
Request request = new Request(HttpGet.METHOD_NAME, "/_xpack");
if (false == infoRequest.isVerbose()) {
request.addParameter("human", "false");
}
if (false == infoRequest.getCategories().equals(EnumSet.allOf(XPackInfoRequest.Category.class))) {
request.addParameter("categories", infoRequest.getCategories().stream()
.map(c -> c.toString().toLowerCase(Locale.ROOT))
.collect(Collectors.joining(",")));
}
return request;
}
private static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType) throws IOException {
BytesRef source = XContentHelper.toXContent(toXContent, xContentType, false).toBytesRef();
return new ByteArrayEntity(source.bytes, source.offset, source.length, createContentType(xContentType));
@ -1246,7 +1277,7 @@ final class RequestConverters {
}
Params withIndicesOptions(IndicesOptions indicesOptions) {
putParam("ignore_unavailable", Boolean.toString(indicesOptions.ignoreUnavailable()));
withIgnoreUnavailable(indicesOptions.ignoreUnavailable());
putParam("allow_no_indices", Boolean.toString(indicesOptions.allowNoIndices()));
String expandWildcards;
if (indicesOptions.expandWildcardsOpen() == false && indicesOptions.expandWildcardsClosed() == false) {
@ -1265,6 +1296,12 @@ final class RequestConverters {
return this;
}
Params withIgnoreUnavailable(boolean ignoreUnavailable) {
// Always explicitly place the ignore_unavailable value.
putParam("ignore_unavailable", Boolean.toString(ignoreUnavailable));
return this;
}
Params withHuman(boolean human) {
if (human) {
putParam("human", Boolean.toString(human));

View File

@ -202,6 +202,7 @@ public class RestHighLevelClient implements Closeable {
private final IngestClient ingestClient = new IngestClient(this);
private final SnapshotClient snapshotClient = new SnapshotClient(this);
private final TasksClient tasksClient = new TasksClient(this);
private final XPackClient xPackClient = new XPackClient(this);
/**
* Creates a {@link RestHighLevelClient} given the low level {@link RestClientBuilder} that allows to build the
@ -292,6 +293,19 @@ public class RestHighLevelClient implements Closeable {
return tasksClient;
}
/**
* A wrapper for the {@link RestHighLevelClient} that provides methods for
* accessing the Elastic Licensed X-Pack APIs that are shipped with the
* default distribution of Elasticsearch. All of these APIs will 404 if run
* against the OSS distribution of Elasticsearch.
* <p>
* See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/xpack-api.html">
* X-Pack APIs on elastic.co</a> for more information.
*/
public final XPackClient xpack() {
return xPackClient;
}
/**
* Executes a bulk request using the Bulk API.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html">Bulk API on elastic.co</a>
@ -668,7 +682,7 @@ public class RestHighLevelClient implements Closeable {
emptySet());
}
/**
* Executes a request using the Multi Search Template API.
*
@ -678,9 +692,9 @@ public class RestHighLevelClient implements Closeable {
public final MultiSearchTemplateResponse multiSearchTemplate(MultiSearchTemplateRequest multiSearchTemplateRequest,
RequestOptions options) throws IOException {
return performRequestAndParseEntity(multiSearchTemplateRequest, RequestConverters::multiSearchTemplate,
options, MultiSearchTemplateResponse::fromXContext, emptySet());
}
options, MultiSearchTemplateResponse::fromXContext, emptySet());
}
/**
* Asynchronously executes a request using the Multi Search Template API
*
@ -692,7 +706,7 @@ public class RestHighLevelClient implements Closeable {
ActionListener<MultiSearchTemplateResponse> listener) {
performRequestAsyncAndParseEntity(multiSearchTemplateRequest, RequestConverters::multiSearchTemplate,
options, MultiSearchTemplateResponse::fromXContext, listener, emptySet());
}
}
/**
* Asynchronously executes a request using the Ranking Evaluation API.

View File

@ -30,6 +30,8 @@ import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyReposito
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryResponse;
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotResponse;
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
@ -221,6 +223,35 @@ public final class SnapshotClient {
GetSnapshotsResponse::fromXContent, listener, emptySet());
}
/**
* Gets the status of requested snapshots.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore
* API on elastic.co</a>
* @param snapshotsStatusRequest the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response
* @throws IOException in case there is a problem sending the request or parsing back the response
*/
public SnapshotsStatusResponse status(SnapshotsStatusRequest snapshotsStatusRequest, RequestOptions options)
throws IOException {
return restHighLevelClient.performRequestAndParseEntity(snapshotsStatusRequest, RequestConverters::snapshotsStatus, options,
SnapshotsStatusResponse::fromXContent, emptySet());
}
/**
* Asynchronously gets the status of requested snapshots.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore
* API on elastic.co</a>
* @param snapshotsStatusRequest the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener the listener to be notified upon request completion
*/
public void statusAsync(SnapshotsStatusRequest snapshotsStatusRequest, RequestOptions options,
ActionListener<SnapshotsStatusResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(snapshotsStatusRequest, RequestConverters::snapshotsStatus, options,
SnapshotsStatusResponse::fromXContent, listener, emptySet());
}
/**
* Deletes a snapshot.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore

View File

@ -0,0 +1,73 @@
/*
* 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.client;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
import java.io.IOException;
import static java.util.Collections.emptySet;
/**
* A wrapper for the {@link RestHighLevelClient} that provides methods for
* accessing the Elastic Licensed X-Pack APIs that are shipped with the
* default distribution of Elasticsearch. All of these APIs will 404 if run
* against the OSS distribution of Elasticsearch.
* <p>
* See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/xpack-api.html">
* X-Pack APIs on elastic.co</a> for more information.
*/
public final class XPackClient {
private final RestHighLevelClient restHighLevelClient;
XPackClient(RestHighLevelClient restHighLevelClient) {
this.restHighLevelClient = restHighLevelClient;
}
/**
* Fetch information about X-Pack from the cluster.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/info-api.html">
* the docs</a> for more.
* @param request the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response
* @throws IOException in case there is a problem sending the request or parsing back the response
*/
public XPackInfoResponse info(XPackInfoRequest request, RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(request, RequestConverters::xPackInfo, options,
XPackInfoResponse::fromXContent, emptySet());
}
/**
* Asynchronously fetch information about X-Pack from the cluster.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/info-api.html">
* the docs</a> for more.
* @param request the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener the listener to be notified upon request completion
*/
public void infoAsync(XPackInfoRequest request, RequestOptions options,
ActionListener<XPackInfoResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request, RequestConverters::xPackInfo, options,
XPackInfoResponse::fromXContent, listener, emptySet());
}
}

View File

@ -612,7 +612,7 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
createIndex(index, Settings.EMPTY);
closeIndex(index);
ResponseException exception = expectThrows(ResponseException.class,
() -> client().performRequest(HttpGet.METHOD_NAME, index + "/_search"));
() -> client().performRequest(new Request(HttpGet.METHOD_NAME, index + "/_search")));
assertThat(exception.getResponse().getStatusLine().getStatusCode(), equalTo(RestStatus.BAD_REQUEST.getStatus()));
assertThat(exception.getMessage().contains(index), equalTo(true));
@ -621,7 +621,7 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
highLevelClient().indices()::openAsync);
assertTrue(openIndexResponse.isAcknowledged());
Response response = client().performRequest(HttpGet.METHOD_NAME, index + "/_search");
Response response = client().performRequest(new Request(HttpGet.METHOD_NAME, index + "/_search"));
assertThat(response.getStatusLine().getStatusCode(), equalTo(RestStatus.OK.getStatus()));
}
@ -650,7 +650,7 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
public void testCloseExistingIndex() throws IOException {
String index = "index";
createIndex(index, Settings.EMPTY);
Response response = client().performRequest(HttpGet.METHOD_NAME, index + "/_search");
Response response = client().performRequest(new Request(HttpGet.METHOD_NAME, index + "/_search"));
assertThat(response.getStatusLine().getStatusCode(), equalTo(RestStatus.OK.getStatus()));
CloseIndexRequest closeIndexRequest = new CloseIndexRequest(index);
@ -659,7 +659,7 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
assertTrue(closeIndexResponse.isAcknowledged());
ResponseException exception = expectThrows(ResponseException.class,
() -> client().performRequest(HttpGet.METHOD_NAME, index + "/_search"));
() -> client().performRequest(new Request(HttpGet.METHOD_NAME, index + "/_search")));
assertThat(exception.getResponse().getStatusLine().getStatusCode(), equalTo(RestStatus.BAD_REQUEST.getStatus()));
assertThat(exception.getMessage().contains(index), equalTo(true));
}
@ -817,7 +817,7 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
assertFalse(execute(getAliasesRequest, highLevelClient().indices()::existsAlias, highLevelClient().indices()::existsAliasAsync));
createIndex("index", Settings.EMPTY);
client().performRequest(HttpPut.METHOD_NAME, "/index/_alias/alias");
client().performRequest(new Request(HttpPut.METHOD_NAME, "/index/_alias/alias"));
assertTrue(execute(getAliasesRequest, highLevelClient().indices()::existsAlias, highLevelClient().indices()::existsAliasAsync));
GetAliasesRequest getAliasesRequest2 = new GetAliasesRequest();
@ -936,10 +936,10 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
public void testGetAlias() throws IOException {
{
createIndex("index1", Settings.EMPTY);
client().performRequest(HttpPut.METHOD_NAME, "/index1/_alias/alias1");
client().performRequest(new Request(HttpPut.METHOD_NAME, "/index1/_alias/alias1"));
createIndex("index2", Settings.EMPTY);
client().performRequest(HttpPut.METHOD_NAME, "/index2/_alias/alias2");
client().performRequest(new Request(HttpPut.METHOD_NAME, "/index2/_alias/alias2"));
createIndex("index3", Settings.EMPTY);
}
@ -1075,7 +1075,7 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase {
assertThat(getAliasesResponse.getError(), equalTo("alias [" + alias + "] missing"));
}
createIndex(index, Settings.EMPTY);
client().performRequest(HttpPut.METHOD_NAME, index + "/_alias/" + alias);
client().performRequest(new Request(HttpPut.METHOD_NAME, index + "/_alias/" + alias));
{
GetAliasesRequest getAliasesRequest = new GetAliasesRequest().indices(index, "non_existent_index");
GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias,

View File

@ -21,8 +21,13 @@ package org.elasticsearch.client;
import org.apache.http.client.methods.HttpGet;
import org.elasticsearch.action.main.MainResponse;
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
import org.elasticsearch.protocol.xpack.XPackInfoResponse.FeatureSetsInfo.FeatureSet;
import org.elasticsearch.protocol.xpack.license.LicenseStatus;
import java.io.IOException;
import java.util.EnumSet;
import java.util.Map;
public class PingAndInfoIT extends ESRestHighLevelClientTestCase {
@ -31,16 +36,16 @@ public class PingAndInfoIT extends ESRestHighLevelClientTestCase {
assertTrue(highLevelClient().ping(RequestOptions.DEFAULT));
}
@SuppressWarnings("unchecked")
public void testInfo() throws IOException {
MainResponse info = highLevelClient().info(RequestOptions.DEFAULT);
// compare with what the low level client outputs
Map<String, Object> infoAsMap = entityAsMap(adminClient().performRequest(HttpGet.METHOD_NAME, "/"));
Map<String, Object> infoAsMap = entityAsMap(adminClient().performRequest(new Request(HttpGet.METHOD_NAME, "/")));
assertEquals(infoAsMap.get("cluster_name"), info.getClusterName().value());
assertEquals(infoAsMap.get("cluster_uuid"), info.getClusterUuid());
// only check node name existence, might be a different one from what was hit by low level client in multi-node cluster
assertNotNull(info.getNodeName());
@SuppressWarnings("unchecked")
Map<String, Object> versionMap = (Map<String, Object>) infoAsMap.get("version");
assertEquals(versionMap.get("build_flavor"), info.getBuild().flavor().displayName());
assertEquals(versionMap.get("build_type"), info.getBuild().type().displayName());
@ -51,4 +56,49 @@ public class PingAndInfoIT extends ESRestHighLevelClientTestCase {
assertEquals(versionMap.get("lucene_version"), info.getVersion().luceneVersion.toString());
}
public void testXPackInfo() throws IOException {
XPackInfoRequest request = new XPackInfoRequest();
request.setCategories(EnumSet.allOf(XPackInfoRequest.Category.class));
request.setVerbose(true);
XPackInfoResponse info = highLevelClient().xpack().info(request, RequestOptions.DEFAULT);
MainResponse mainResponse = highLevelClient().info(RequestOptions.DEFAULT);
assertEquals(mainResponse.getBuild().shortHash(), info.getBuildInfo().getHash());
assertEquals("basic", info.getLicenseInfo().getType());
assertEquals("basic", info.getLicenseInfo().getMode());
assertEquals(LicenseStatus.ACTIVE, info.getLicenseInfo().getStatus());
FeatureSet graph = info.getFeatureSetsInfo().getFeatureSets().get("graph");
assertNotNull(graph.description());
assertFalse(graph.available());
assertTrue(graph.enabled());
assertNull(graph.nativeCodeInfo());
FeatureSet monitoring = info.getFeatureSetsInfo().getFeatureSets().get("monitoring");
assertNotNull(monitoring.description());
assertTrue(monitoring.available());
assertTrue(monitoring.enabled());
assertNull(monitoring.nativeCodeInfo());
FeatureSet ml = info.getFeatureSetsInfo().getFeatureSets().get("ml");
assertNotNull(ml.description());
assertFalse(ml.available());
assertTrue(ml.enabled());
assertEquals(mainResponse.getVersion().toString(),
ml.nativeCodeInfo().get("version").toString().replace("-SNAPSHOT", ""));
}
public void testXPackInfoEmptyRequest() throws IOException {
XPackInfoResponse info = highLevelClient().xpack().info(new XPackInfoRequest(), RequestOptions.DEFAULT);
/*
* The default in the transport client is non-verbose and returning
* no categories which is the opposite of the default when you use
* the API over REST. We don't want to break the transport client
* even though it doesn't feel like a good default.
*/
assertNull(info.getBuildInfo());
assertNull(info.getLicenseInfo());
assertNull(info.getFeatureSetsInfo());
}
}

View File

@ -19,8 +19,6 @@
package org.elasticsearch.client;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
@ -37,7 +35,6 @@ import org.junit.Before;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@ -49,19 +46,17 @@ public class RankEvalIT extends ESRestHighLevelClientTestCase {
@Before
public void indexDocuments() throws IOException {
StringEntity doc = new StringEntity("{\"text\":\"berlin\"}", ContentType.APPLICATION_JSON);
client().performRequest("PUT", "/index/doc/1", Collections.emptyMap(), doc);
doc = new StringEntity("{\"text\":\"amsterdam\"}", ContentType.APPLICATION_JSON);
client().performRequest("PUT", "/index/doc/2", Collections.emptyMap(), doc);
client().performRequest("PUT", "/index/doc/3", Collections.emptyMap(), doc);
client().performRequest("PUT", "/index/doc/4", Collections.emptyMap(), doc);
client().performRequest("PUT", "/index/doc/5", Collections.emptyMap(), doc);
client().performRequest("PUT", "/index/doc/6", Collections.emptyMap(), doc);
client().performRequest("POST", "/index/_refresh");
// add another index to test basic multi index support
client().performRequest("PUT", "/index2/doc/7", Collections.emptyMap(), doc);
client().performRequest("POST", "/index2/_refresh");
Request berlin = new Request("PUT", "/index/doc/berlin");
berlin.setJsonEntity("{\"text\":\"berlin\"}");
client().performRequest(berlin);
for (int i = 0; i < 6; i++) {
// add another index to test basic multi index support
String index = i == 0 ? "index2" : "index";
Request amsterdam = new Request("PUT", "/" + index + "/doc/amsterdam" + i);
amsterdam.setJsonEntity("{\"text\":\"amsterdam\"}");
client().performRequest(amsterdam);
}
client().performRequest(new Request("POST", "/_refresh"));
}
/**
@ -71,10 +66,10 @@ public class RankEvalIT extends ESRestHighLevelClientTestCase {
public void testRankEvalRequest() throws IOException {
SearchSourceBuilder testQuery = new SearchSourceBuilder();
testQuery.query(new MatchAllQueryBuilder());
List<RatedDocument> amsterdamRatedDocs = createRelevant("index" , "2", "3", "4", "5");
amsterdamRatedDocs.addAll(createRelevant("index2", "7"));
List<RatedDocument> amsterdamRatedDocs = createRelevant("index" , "amsterdam1", "amsterdam2", "amsterdam3", "amsterdam4");
amsterdamRatedDocs.addAll(createRelevant("index2", "amsterdam0"));
RatedRequest amsterdamRequest = new RatedRequest("amsterdam_query", amsterdamRatedDocs, testQuery);
RatedRequest berlinRequest = new RatedRequest("berlin_query", createRelevant("index", "1"), testQuery);
RatedRequest berlinRequest = new RatedRequest("berlin_query", createRelevant("index", "berlin"), testQuery);
List<RatedRequest> specifications = new ArrayList<>();
specifications.add(amsterdamRequest);
specifications.add(berlinRequest);
@ -94,7 +89,7 @@ public class RankEvalIT extends ESRestHighLevelClientTestCase {
assertEquals(7, hitsAndRatings.size());
for (RatedSearchHit hit : hitsAndRatings) {
String id = hit.getSearchHit().getId();
if (id.equals("1") || id.equals("6")) {
if (id.equals("berlin") || id.equals("amsterdam5")) {
assertFalse(hit.getRating().isPresent());
} else {
assertEquals(1, hit.getRating().get().intValue());
@ -106,7 +101,7 @@ public class RankEvalIT extends ESRestHighLevelClientTestCase {
assertEquals(7, hitsAndRatings.size());
for (RatedSearchHit hit : hitsAndRatings) {
String id = hit.getSearchHit().getId();
if (id.equals("1")) {
if (id.equals("berlin")) {
assertEquals(1, hit.getRating().get().intValue());
} else {
assertFalse(hit.getRating().isPresent());
@ -114,7 +109,7 @@ public class RankEvalIT extends ESRestHighLevelClientTestCase {
}
// now try this when test2 is closed
client().performRequest("POST", "index2/_close", Collections.emptyMap());
client().performRequest(new Request("POST", "index2/_close"));
rankEvalRequest.indicesOptions(IndicesOptions.fromParameters(null, "true", null, SearchRequest.DEFAULT_INDICES_OPTIONS));
response = execute(rankEvalRequest, highLevelClient()::rankEval, highLevelClient()::rankEvalAsync);
}

View File

@ -43,6 +43,7 @@ import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotReq
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest;
import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
@ -123,6 +124,7 @@ import org.elasticsearch.index.rankeval.RankEvalRequest;
import org.elasticsearch.index.rankeval.RankEvalSpec;
import org.elasticsearch.index.rankeval.RatedRequest;
import org.elasticsearch.index.rankeval.RestRankEvalAction;
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
import org.elasticsearch.repositories.fs.FsRepository;
import org.elasticsearch.rest.action.search.RestSearchAction;
import org.elasticsearch.script.ScriptType;
@ -150,6 +152,7 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -173,6 +176,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXC
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
@ -2169,6 +2173,29 @@ public class RequestConvertersTests extends ESTestCase {
assertNull(request.getEntity());
}
public void testSnapshotsStatus() {
Map<String, String> expectedParams = new HashMap<>();
String repository = randomIndicesNames(1, 1)[0];
String[] snapshots = randomIndicesNames(1, 5);
StringBuilder snapshotNames = new StringBuilder(snapshots[0]);
for (int idx = 1; idx < snapshots.length; idx++) {
snapshotNames.append(",").append(snapshots[idx]);
}
boolean ignoreUnavailable = randomBoolean();
String endpoint = "/_snapshot/" + repository + "/" + snapshotNames.toString() + "/_status";
SnapshotsStatusRequest snapshotsStatusRequest = new SnapshotsStatusRequest(repository, snapshots);
setRandomMasterTimeout(snapshotsStatusRequest, expectedParams);
snapshotsStatusRequest.ignoreUnavailable(ignoreUnavailable);
expectedParams.put("ignore_unavailable", Boolean.toString(ignoreUnavailable));
Request request = RequestConverters.snapshotsStatus(snapshotsStatusRequest);
assertThat(request.getEndpoint(), equalTo(endpoint));
assertThat(request.getMethod(), equalTo(HttpGet.METHOD_NAME));
assertThat(request.getParameters(), equalTo(expectedParams));
assertThat(request.getEntity(), is(nullValue()));
}
public void testDeleteSnapshot() {
Map<String, String> expectedParams = new HashMap<>();
String repository = randomIndicesNames(1, 1)[0];
@ -2465,6 +2492,37 @@ public class RequestConvertersTests extends ESTestCase {
+ "previous requests have content-type [" + xContentType + "]", exception.getMessage());
}
public void testXPackInfo() {
XPackInfoRequest infoRequest = new XPackInfoRequest();
Map<String, String> expectedParams = new HashMap<>();
infoRequest.setVerbose(randomBoolean());
if (false == infoRequest.isVerbose()) {
expectedParams.put("human", "false");
}
int option = between(0, 2);
switch (option) {
case 0:
infoRequest.setCategories(EnumSet.allOf(XPackInfoRequest.Category.class));
break;
case 1:
infoRequest.setCategories(EnumSet.of(XPackInfoRequest.Category.FEATURES));
expectedParams.put("categories", "features");
break;
case 2:
infoRequest.setCategories(EnumSet.of(XPackInfoRequest.Category.FEATURES, XPackInfoRequest.Category.BUILD));
expectedParams.put("categories", "build,features");
break;
default:
throw new IllegalArgumentException("invalid option [" + option + "]");
}
Request request = RequestConverters.xPackInfo(infoRequest);
assertEquals(HttpGet.METHOD_NAME, request.getMethod());
assertEquals("/_xpack", request.getEndpoint());
assertNull(request.getEntity());
assertEquals(expectedParams, request.getParameters());
}
/**
* Randomize the {@link FetchSourceContext} request parameters.
*/

View File

@ -19,12 +19,8 @@
package org.elasticsearch.client;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.nio.entity.NStringEntity;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.explain.ExplainRequest;
@ -101,85 +97,106 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
@Before
public void indexDocuments() throws IOException {
StringEntity doc1 = new StringEntity("{\"type\":\"type1\", \"num\":10, \"num2\":50}", ContentType.APPLICATION_JSON);
client().performRequest(HttpPut.METHOD_NAME, "/index/type/1", Collections.emptyMap(), doc1);
StringEntity doc2 = new StringEntity("{\"type\":\"type1\", \"num\":20, \"num2\":40}", ContentType.APPLICATION_JSON);
client().performRequest(HttpPut.METHOD_NAME, "/index/type/2", Collections.emptyMap(), doc2);
StringEntity doc3 = new StringEntity("{\"type\":\"type1\", \"num\":50, \"num2\":35}", ContentType.APPLICATION_JSON);
client().performRequest(HttpPut.METHOD_NAME, "/index/type/3", Collections.emptyMap(), doc3);
StringEntity doc4 = new StringEntity("{\"type\":\"type2\", \"num\":100, \"num2\":10}", ContentType.APPLICATION_JSON);
client().performRequest(HttpPut.METHOD_NAME, "/index/type/4", Collections.emptyMap(), doc4);
StringEntity doc5 = new StringEntity("{\"type\":\"type2\", \"num\":100, \"num2\":10}", ContentType.APPLICATION_JSON);
client().performRequest(HttpPut.METHOD_NAME, "/index/type/5", Collections.emptyMap(), doc5);
client().performRequest(HttpPost.METHOD_NAME, "/index/_refresh");
{
Request doc1 = new Request(HttpPut.METHOD_NAME, "/index/type/1");
doc1.setJsonEntity("{\"type\":\"type1\", \"num\":10, \"num2\":50}");
client().performRequest(doc1);
Request doc2 = new Request(HttpPut.METHOD_NAME, "/index/type/2");
doc2.setJsonEntity("{\"type\":\"type1\", \"num\":20, \"num2\":40}");
client().performRequest(doc2);
Request doc3 = new Request(HttpPut.METHOD_NAME, "/index/type/3");
doc3.setJsonEntity("{\"type\":\"type1\", \"num\":50, \"num2\":35}");
client().performRequest(doc3);
Request doc4 = new Request(HttpPut.METHOD_NAME, "/index/type/4");
doc4.setJsonEntity("{\"type\":\"type2\", \"num\":100, \"num2\":10}");
client().performRequest(doc4);
Request doc5 = new Request(HttpPut.METHOD_NAME, "/index/type/5");
doc5.setJsonEntity("{\"type\":\"type2\", \"num\":100, \"num2\":10}");
client().performRequest(doc5);
}
{
Request doc1 = new Request(HttpPut.METHOD_NAME, "/index1/doc/1");
doc1.setJsonEntity("{\"field\":\"value1\", \"rating\": 7}");
client().performRequest(doc1);
Request doc2 = new Request(HttpPut.METHOD_NAME, "/index1/doc/2");
doc2.setJsonEntity("{\"field\":\"value2\"}");
client().performRequest(doc2);
}
StringEntity doc = new StringEntity("{\"field\":\"value1\", \"rating\": 7}", ContentType.APPLICATION_JSON);
client().performRequest(HttpPut.METHOD_NAME, "/index1/doc/1", Collections.emptyMap(), doc);
doc = new StringEntity("{\"field\":\"value2\"}", ContentType.APPLICATION_JSON);
client().performRequest(HttpPut.METHOD_NAME, "/index1/doc/2", Collections.emptyMap(), doc);
StringEntity mappings = new StringEntity(
"{" +
" \"mappings\": {" +
" \"doc\": {" +
" \"properties\": {" +
" \"rating\": {" +
" \"type\": \"keyword\"" +
" }" +
" }" +
" }" +
" }" +
"}}",
ContentType.APPLICATION_JSON);
client().performRequest("PUT", "/index2", Collections.emptyMap(), mappings);
doc = new StringEntity("{\"field\":\"value1\", \"rating\": \"good\"}", ContentType.APPLICATION_JSON);
client().performRequest(HttpPut.METHOD_NAME, "/index2/doc/3", Collections.emptyMap(), doc);
doc = new StringEntity("{\"field\":\"value2\"}", ContentType.APPLICATION_JSON);
client().performRequest(HttpPut.METHOD_NAME, "/index2/doc/4", Collections.emptyMap(), doc);
doc = new StringEntity("{\"field\":\"value1\"}", ContentType.APPLICATION_JSON);
client().performRequest(HttpPut.METHOD_NAME, "/index3/doc/5", Collections.emptyMap(), doc);
doc = new StringEntity("{\"field\":\"value2\"}", ContentType.APPLICATION_JSON);
client().performRequest(HttpPut.METHOD_NAME, "/index3/doc/6", Collections.emptyMap(), doc);
mappings = new StringEntity(
"{" +
{
Request create = new Request("PUT", "/index2");
create.setJsonEntity(
"{" +
" \"mappings\": {" +
" \"doc\": {" +
" \"properties\": {" +
" \"field1\": {" +
" \"type\": \"keyword\"," +
" \"store\": true" +
" }," +
" \"field2\": {" +
" \"type\": \"keyword\"," +
" \"store\": true" +
" \"rating\": {" +
" \"type\": \"keyword\"" +
" }" +
" }" +
" }" +
" }" +
"}}",
ContentType.APPLICATION_JSON);
client().performRequest(HttpPut.METHOD_NAME, "/index4", Collections.emptyMap(), mappings);
doc = new StringEntity("{\"field1\":\"value1\", \"field2\":\"value2\"}", ContentType.APPLICATION_JSON);
client().performRequest(HttpPut.METHOD_NAME, "/index4/doc/1", Collections.emptyMap(), doc);
StringEntity aliasFilter = new StringEntity(
"{" +
" \"actions\" : [" +
" {" +
" \"add\" : {" +
" \"index\" : \"index4\"," +
" \"alias\" : \"alias4\"," +
" \"filter\" : { \"term\" : { \"field2\" : \"value1\" } }" +
" }" +
" }" +
" ]" +
"}",
ContentType.APPLICATION_JSON);
client().performRequest(HttpPost.METHOD_NAME, "/_aliases", Collections.emptyMap(), aliasFilter);
"}");
client().performRequest(create);
Request doc3 = new Request(HttpPut.METHOD_NAME, "/index2/doc/3");
doc3.setJsonEntity("{\"field\":\"value1\", \"rating\": \"good\"}");
client().performRequest(doc3);
Request doc4 = new Request(HttpPut.METHOD_NAME, "/index2/doc/4");
doc4.setJsonEntity("{\"field\":\"value2\"}");
client().performRequest(doc4);
}
client().performRequest(HttpPost.METHOD_NAME, "/index1,index2,index3,index4/_refresh");
{
Request doc5 = new Request(HttpPut.METHOD_NAME, "/index3/doc/5");
doc5.setJsonEntity("{\"field\":\"value1\"}");
client().performRequest(doc5);
Request doc6 = new Request(HttpPut.METHOD_NAME, "/index3/doc/6");
doc6.setJsonEntity("{\"field\":\"value2\"}");
client().performRequest(doc6);
}
{
Request create = new Request(HttpPut.METHOD_NAME, "/index4");
create.setJsonEntity(
"{" +
" \"mappings\": {" +
" \"doc\": {" +
" \"properties\": {" +
" \"field1\": {" +
" \"type\": \"keyword\"," +
" \"store\": true" +
" }," +
" \"field2\": {" +
" \"type\": \"keyword\"," +
" \"store\": true" +
" }" +
" }" +
" }" +
" }" +
"}");
client().performRequest(create);
Request doc1 = new Request(HttpPut.METHOD_NAME, "/index4/doc/1");
doc1.setJsonEntity("{\"field1\":\"value1\", \"field2\":\"value2\"}");
client().performRequest(doc1);
Request createFilteredAlias = new Request(HttpPost.METHOD_NAME, "/_aliases");
createFilteredAlias.setJsonEntity(
"{" +
" \"actions\" : [" +
" {" +
" \"add\" : {" +
" \"index\" : \"index4\"," +
" \"alias\" : \"alias4\"," +
" \"filter\" : { \"term\" : { \"field2\" : \"value1\" } }" +
" }" +
" }" +
" ]" +
"}");
client().performRequest(createFilteredAlias);
}
client().performRequest(new Request(HttpPost.METHOD_NAME, "/_refresh"));
}
public void testSearchNoQuery() throws IOException {
@ -377,7 +394,9 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
public void testSearchWithParentJoin() throws IOException {
final String indexName = "child_example";
StringEntity parentMapping = new StringEntity("{\n" +
Request createIndex = new Request(HttpPut.METHOD_NAME, "/" + indexName);
createIndex.setJsonEntity(
"{\n" +
" \"mappings\": {\n" +
" \"qa\" : {\n" +
" \"properties\" : {\n" +
@ -388,9 +407,11 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
" }\n" +
" }\n" +
" }" +
"}", ContentType.APPLICATION_JSON);
client().performRequest(HttpPut.METHOD_NAME, "/" + indexName, Collections.emptyMap(), parentMapping);
StringEntity questionDoc = new StringEntity("{\n" +
"}");
client().performRequest(createIndex);
Request questionDoc = new Request(HttpPut.METHOD_NAME, "/" + indexName + "/qa/1");
questionDoc.setJsonEntity(
"{\n" +
" \"body\": \"<p>I have Windows 2003 server and i bought a new Windows 2008 server...\",\n" +
" \"title\": \"Whats the best way to file transfer my site from server to a newer one?\",\n" +
" \"tags\": [\n" +
@ -399,9 +420,12 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
" \"file-transfer\"\n" +
" ],\n" +
" \"qa_join_field\" : \"question\"\n" +
"}", ContentType.APPLICATION_JSON);
client().performRequest(HttpPut.METHOD_NAME, "/" + indexName + "/qa/1", Collections.emptyMap(), questionDoc);
StringEntity answerDoc1 = new StringEntity("{\n" +
"}");
client().performRequest(questionDoc);
Request answerDoc1 = new Request(HttpPut.METHOD_NAME, "/" + indexName + "/qa/2");
answerDoc1.addParameter("routing", "1");
answerDoc1.setJsonEntity(
"{\n" +
" \"owner\": {\n" +
" \"location\": \"Norfolk, United Kingdom\",\n" +
" \"display_name\": \"Sam\",\n" +
@ -413,9 +437,12 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
" \"parent\" : \"1\"\n" +
" },\n" +
" \"creation_date\": \"2009-05-04T13:45:37.030\"\n" +
"}", ContentType.APPLICATION_JSON);
client().performRequest(HttpPut.METHOD_NAME, "/" + indexName + "/qa/2", Collections.singletonMap("routing", "1"), answerDoc1);
StringEntity answerDoc2 = new StringEntity("{\n" +
"}");
client().performRequest(answerDoc1);
Request answerDoc2 = new Request(HttpPut.METHOD_NAME, "/" + indexName + "/qa/3");
answerDoc2.addParameter("routing", "1");
answerDoc2.setJsonEntity(
"{\n" +
" \"owner\": {\n" +
" \"location\": \"Norfolk, United Kingdom\",\n" +
" \"display_name\": \"Troll\",\n" +
@ -427,9 +454,9 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
" \"parent\" : \"1\"\n" +
" },\n" +
" \"creation_date\": \"2009-05-05T13:45:37.030\"\n" +
"}", ContentType.APPLICATION_JSON);
client().performRequest(HttpPut.METHOD_NAME, "/" + indexName + "/qa/3", Collections.singletonMap("routing", "1"), answerDoc2);
client().performRequest(HttpPost.METHOD_NAME, "/_refresh");
"}");
client().performRequest(answerDoc2);
client().performRequest(new Request(HttpPost.METHOD_NAME, "/_refresh"));
TermsAggregationBuilder leafTermAgg = new TermsAggregationBuilder("top-names", ValueType.STRING)
.field("owner.display_name.keyword").size(10);
@ -506,9 +533,10 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
}
public void testSearchWithWeirdScriptFields() throws Exception {
HttpEntity entity = new NStringEntity("{ \"field\":\"value\"}", ContentType.APPLICATION_JSON);
client().performRequest("PUT", "test/type/1", Collections.emptyMap(), entity);
client().performRequest("POST", "/test/_refresh");
Request doc = new Request("PUT", "test/type/1");
doc.setJsonEntity("{\"field\":\"value\"}");
client().performRequest(doc);
client().performRequest(new Request("POST", "/test/_refresh"));
{
SearchRequest searchRequest = new SearchRequest("test").source(SearchSourceBuilder.searchSource()
@ -547,13 +575,13 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
}
public void testSearchScroll() throws Exception {
for (int i = 0; i < 100; i++) {
XContentBuilder builder = jsonBuilder().startObject().field("field", i).endObject();
HttpEntity entity = new NStringEntity(Strings.toString(builder), ContentType.APPLICATION_JSON);
client().performRequest(HttpPut.METHOD_NAME, "test/type1/" + Integer.toString(i), Collections.emptyMap(), entity);
Request doc = new Request(HttpPut.METHOD_NAME, "/test/type1/" + Integer.toString(i));
doc.setJsonEntity(Strings.toString(builder));
client().performRequest(doc);
}
client().performRequest(HttpPost.METHOD_NAME, "/test/_refresh");
client().performRequest(new Request(HttpPost.METHOD_NAME, "/test/_refresh"));
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().size(35).sort("field", SortOrder.ASC);
SearchRequest searchRequest = new SearchRequest("test").scroll(TimeValue.timeValueMinutes(2)).source(searchSourceBuilder);
@ -878,11 +906,11 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
assertToXContentEquivalent(expectedSource, actualSource, XContentType.JSON);
}
public void testMultiSearchTemplate() throws Exception {
MultiSearchTemplateRequest multiSearchTemplateRequest = new MultiSearchTemplateRequest();
SearchTemplateRequest goodRequest = new SearchTemplateRequest();
goodRequest.setRequest(new SearchRequest("index"));
goodRequest.setScriptType(ScriptType.INLINE);
@ -900,8 +928,8 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
goodRequest.setExplain(true);
goodRequest.setProfile(true);
multiSearchTemplateRequest.add(goodRequest);
SearchTemplateRequest badRequest = new SearchTemplateRequest();
badRequest.setRequest(new SearchRequest("index"));
badRequest.setScriptType(ScriptType.INLINE);
@ -910,17 +938,17 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
scriptParams.put("number", 10);
badRequest.setScriptParams(scriptParams);
multiSearchTemplateRequest.add(badRequest);
multiSearchTemplateRequest.add(badRequest);
MultiSearchTemplateResponse multiSearchTemplateResponse =
execute(multiSearchTemplateRequest, highLevelClient()::multiSearchTemplate,
execute(multiSearchTemplateRequest, highLevelClient()::multiSearchTemplate,
highLevelClient()::multiSearchTemplateAsync);
Item[] responses = multiSearchTemplateResponse.getResponses();
assertEquals(2, responses.length);
assertNull(responses[0].getResponse().getSource());
SearchResponse goodResponse =responses[0].getResponse().getResponse();
assertNotNull(goodResponse);
@ -930,18 +958,18 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
assertThat(goodResponse.getHits().getMaxScore(), greaterThan(0f));
SearchHit hit = goodResponse.getHits().getHits()[0];
assertNotNull(hit.getExplanation());
assertFalse(goodResponse.getProfileResults().isEmpty());
assertFalse(goodResponse.getProfileResults().isEmpty());
assertNull(responses[0].getResponse().getSource());
assertThat(responses[1].isFailure(), Matchers.is(true));
assertNotNull(responses[1].getFailureMessage());
assertNotNull(responses[1].getFailureMessage());
assertThat(responses[1].getFailureMessage(), containsString("json_parse_exception"));
}
public void testMultiSearchTemplateAllBad() throws Exception {
MultiSearchTemplateRequest multiSearchTemplateRequest = new MultiSearchTemplateRequest();
SearchTemplateRequest badRequest1 = new SearchTemplateRequest();
badRequest1.setRequest(new SearchRequest("index"));
badRequest1.setScriptType(ScriptType.INLINE);
@ -957,8 +985,8 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
scriptParams.put("number", "BAD NUMBER");
badRequest1.setScriptParams(scriptParams);
multiSearchTemplateRequest.add(badRequest1);
SearchTemplateRequest badRequest2 = new SearchTemplateRequest();
badRequest2.setRequest(new SearchRequest("index"));
badRequest2.setScriptType(ScriptType.INLINE);
@ -967,13 +995,13 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
scriptParams.put("number", "BAD NUMBER");
badRequest2.setScriptParams(scriptParams);
multiSearchTemplateRequest.add(badRequest2);
// The whole HTTP request should fail if no nested search requests are valid
multiSearchTemplateRequest.add(badRequest2);
// The whole HTTP request should fail if no nested search requests are valid
ElasticsearchStatusException exception = expectThrows(ElasticsearchStatusException.class,
() -> execute(multiSearchTemplateRequest, highLevelClient()::multiSearchTemplate,
() -> execute(multiSearchTemplateRequest, highLevelClient()::multiSearchTemplate,
highLevelClient()::multiSearchTemplateAsync));
assertEquals(RestStatus.BAD_REQUEST, exception.status());
assertThat(exception.getMessage(), containsString("no requests added"));
}

View File

@ -28,6 +28,9 @@ import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequ
import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryResponse;
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest;
import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryResponse;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
@ -43,6 +46,7 @@ import java.util.stream.Collectors;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
public class SnapshotIT extends ESRestHighLevelClientTestCase {
@ -173,6 +177,34 @@ public class SnapshotIT extends ESRestHighLevelClientTestCase {
contains("test_snapshot1", "test_snapshot2"));
}
public void testSnapshotsStatus() throws IOException {
String testRepository = "test";
String testSnapshot = "snapshot";
String testIndex = "test_index";
PutRepositoryResponse putRepositoryResponse = createTestRepository(testRepository, FsRepository.TYPE, "{\"location\": \".\"}");
assertTrue(putRepositoryResponse.isAcknowledged());
createIndex(testIndex, Settings.EMPTY);
CreateSnapshotRequest createSnapshotRequest = new CreateSnapshotRequest(testRepository, testSnapshot);
createSnapshotRequest.indices(testIndex);
createSnapshotRequest.waitForCompletion(true);
CreateSnapshotResponse createSnapshotResponse = createTestSnapshot(createSnapshotRequest);
// check that the request went ok without parsing JSON here. When using the high level client, check acknowledgement instead.
assertEquals(RestStatus.OK, createSnapshotResponse.status());
SnapshotsStatusRequest request = new SnapshotsStatusRequest();
request.repository(testRepository);
request.snapshots(new String[]{testSnapshot});
SnapshotsStatusResponse response = execute(request, highLevelClient().snapshot()::status,
highLevelClient().snapshot()::statusAsync);
assertThat(response.getSnapshots().size(), equalTo(1));
assertThat(response.getSnapshots().get(0).getSnapshot().getRepository(), equalTo(testRepository));
assertThat(response.getSnapshots().get(0).getSnapshot().getSnapshotId().getName(), equalTo(testSnapshot));
assertThat(response.getSnapshots().get(0).getIndices().containsKey(testIndex), is(true));
}
public void testDeleteSnapshot() throws IOException {
String repository = "test_repository";
String snapshot = "test_snapshot";

View File

@ -19,8 +19,6 @@
package org.elasticsearch.client.documentation;
import org.apache.http.entity.ContentType;
import org.apache.http.nio.entity.NStringEntity;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.DocWriteRequest;
@ -66,7 +64,6 @@ import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@ -756,7 +753,9 @@ public class CRUDDocumentationIT extends ESRestHighLevelClientTestCase {
public void testGet() throws Exception {
RestHighLevelClient client = highLevelClient();
{
String mappings = "{\n" +
Request createIndex = new Request("PUT", "/posts");
createIndex.setJsonEntity(
"{\n" +
" \"mappings\" : {\n" +
" \"doc\" : {\n" +
" \"properties\" : {\n" +
@ -767,10 +766,8 @@ public class CRUDDocumentationIT extends ESRestHighLevelClientTestCase {
" }\n" +
" }\n" +
" }\n" +
"}";
NStringEntity entity = new NStringEntity(mappings, ContentType.APPLICATION_JSON);
Response response = client().performRequest("PUT", "/posts", Collections.emptyMap(), entity);
"}");
Response response = client().performRequest(createIndex);
assertEquals(200, response.getStatusLine().getStatusCode());
IndexRequest indexRequest = new IndexRequest("posts", "doc", "1")
@ -1071,21 +1068,21 @@ public class CRUDDocumentationIT extends ESRestHighLevelClientTestCase {
RestHighLevelClient client = highLevelClient();
{
String mappings = "{\n" +
" \"mappings\" : {\n" +
" \"type\" : {\n" +
" \"properties\" : {\n" +
" \"foo\" : {\n" +
" \"type\": \"text\",\n" +
" \"store\": true\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
NStringEntity entity = new NStringEntity(mappings, ContentType.APPLICATION_JSON);
Response response = client().performRequest("PUT", "/index", Collections.emptyMap(), entity);
Request createIndex = new Request("PUT", "/index");
createIndex.setJsonEntity(
"{\n" +
" \"mappings\" : {\n" +
" \"type\" : {\n" +
" \"properties\" : {\n" +
" \"foo\" : {\n" +
" \"type\": \"text\",\n" +
" \"store\": true\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}");
Response response = client().performRequest(createIndex);
assertEquals(200, response.getStatusLine().getStatusCode());
}

View File

@ -22,14 +22,24 @@ package org.elasticsearch.client.documentation;
import org.apache.http.HttpHost;
import org.elasticsearch.Build;
import org.elasticsearch.Version;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.LatchedActionListener;
import org.elasticsearch.action.main.MainResponse;
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
import org.elasticsearch.protocol.xpack.XPackInfoResponse.BuildInfo;
import org.elasticsearch.protocol.xpack.XPackInfoResponse.FeatureSetsInfo;
import org.elasticsearch.protocol.xpack.XPackInfoResponse.LicenseInfo;
import java.io.IOException;
import java.util.EnumSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Documentation for miscellaneous APIs in the high level java client.
@ -66,6 +76,59 @@ public class MiscellaneousDocumentationIT extends ESRestHighLevelClientTestCase
assertTrue(response);
}
public void testXPackInfo() throws Exception {
RestHighLevelClient client = highLevelClient();
{
//tag::x-pack-info-execute
XPackInfoRequest request = new XPackInfoRequest();
request.setVerbose(true); // <1>
request.setCategories(EnumSet.of( // <2>
XPackInfoRequest.Category.BUILD,
XPackInfoRequest.Category.LICENSE,
XPackInfoRequest.Category.FEATURES));
XPackInfoResponse response = client.xpack().info(request, RequestOptions.DEFAULT);
//end::x-pack-info-execute
//tag::x-pack-info-response
BuildInfo build = response.getBuildInfo(); // <1>
LicenseInfo license = response.getLicenseInfo(); // <2>
assertEquals(XPackInfoResponse.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS,
license.getExpiryDate()); // <3>
FeatureSetsInfo features = response.getFeatureSetsInfo(); // <4>
//end::x-pack-info-response
assertNotNull(response.getBuildInfo());
assertNotNull(response.getLicenseInfo());
assertNotNull(response.getFeatureSetsInfo());
}
{
XPackInfoRequest request = new XPackInfoRequest();
// tag::x-pack-info-execute-listener
ActionListener<XPackInfoResponse> listener = new ActionListener<XPackInfoResponse>() {
@Override
public void onResponse(XPackInfoResponse indexResponse) {
// <1>
}
@Override
public void onFailure(Exception e) {
// <2>
}
};
// end::x-pack-info-execute-listener
// Replace the empty listener by a blocking listener in test
final CountDownLatch latch = new CountDownLatch(1);
listener = new LatchedActionListener<>(listener, latch);
// tag::x-pack-info-execute-async
client.xpack().infoAsync(request, RequestOptions.DEFAULT, listener); // <1>
// end::x-pack-info-execute-async
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
}
public void testInitializationFromClientBuilder() throws IOException {
//tag::rest-high-level-client-init
RestHighLevelClient client = new RestHighLevelClient(

View File

@ -37,11 +37,16 @@ import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.delete.DeleteSnapshotResponse;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotStats;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotStatus;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse;
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.cluster.SnapshotsInProgress;
import org.elasticsearch.cluster.metadata.RepositoryMetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
@ -84,8 +89,8 @@ import static org.hamcrest.Matchers.equalTo;
public class SnapshotClientDocumentationIT extends ESRestHighLevelClientTestCase {
private static final String repositoryName = "test_repository";
private static final String snapshotName = "test_snapshot";
private static final String indexName = "test_index";
public void testSnapshotCreateRepository() throws IOException {
RestHighLevelClient client = highLevelClient();
@ -466,6 +471,7 @@ public class SnapshotClientDocumentationIT extends ESRestHighLevelClientTestCase
RestHighLevelClient client = highLevelClient();
createTestRepositories();
createTestIndex();
createTestSnapshots();
// tag::get-snapshots-request
@ -543,10 +549,84 @@ public class SnapshotClientDocumentationIT extends ESRestHighLevelClientTestCase
}
}
public void testSnapshotSnapshotsStatus() throws IOException {
RestHighLevelClient client = highLevelClient();
createTestRepositories();
createTestIndex();
createTestSnapshots();
// tag::snapshots-status-request
SnapshotsStatusRequest request = new SnapshotsStatusRequest();
// end::snapshots-status-request
// tag::snapshots-status-request-repository
request.repository(repositoryName); // <1>
// end::snapshots-status-request-repository
// tag::snapshots-status-request-snapshots
String [] snapshots = new String[] {snapshotName};
request.snapshots(snapshots); // <1>
// end::snapshots-status-request-snapshots
// tag::snapshots-status-request-ignoreUnavailable
request.ignoreUnavailable(true); // <1>
// end::snapshots-status-request-ignoreUnavailable
// tag::snapshots-status-request-masterTimeout
request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1>
request.masterNodeTimeout("1m"); // <2>
// end::snapshots-status-request-masterTimeout
// tag::snapshots-status-execute
SnapshotsStatusResponse response = client.snapshot().status(request, RequestOptions.DEFAULT);
// end::snapshots-status-execute
// tag::snapshots-status-response
List<SnapshotStatus> snapshotStatusesResponse = response.getSnapshots();
SnapshotStatus snapshotStatus = snapshotStatusesResponse.get(0); // <1>
SnapshotsInProgress.State snapshotState = snapshotStatus.getState(); // <2>
SnapshotStats shardStats = snapshotStatus.getIndices().get(indexName).getShards().get(0).getStats(); // <3>
// end::snapshots-status-response
assertThat(snapshotStatusesResponse.size(), equalTo(1));
assertThat(snapshotStatusesResponse.get(0).getSnapshot().getRepository(), equalTo(SnapshotClientDocumentationIT.repositoryName));
assertThat(snapshotStatusesResponse.get(0).getSnapshot().getSnapshotId().getName(), equalTo(snapshotName));
assertThat(snapshotState.completed(), equalTo(true));
}
public void testSnapshotSnapshotsStatusAsync() throws InterruptedException {
RestHighLevelClient client = highLevelClient();
{
SnapshotsStatusRequest request = new SnapshotsStatusRequest();
// tag::snapshots-status-execute-listener
ActionListener<SnapshotsStatusResponse> listener =
new ActionListener<SnapshotsStatusResponse>() {
@Override
public void onResponse(SnapshotsStatusResponse snapshotsStatusResponse) {
// <1>
}
@Override
public void onFailure(Exception e) {
// <2>
}
};
// end::snapshots-status-execute-listener
// Replace the empty listener with a blocking listener in test
final CountDownLatch latch = new CountDownLatch(1);
listener = new LatchedActionListener<>(listener, latch);
// tag::snapshots-status-execute-async
client.snapshot().statusAsync(request, RequestOptions.DEFAULT, listener); // <1>
// end::snapshots-status-execute-async
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
}
public void testSnapshotDeleteSnapshot() throws IOException {
RestHighLevelClient client = highLevelClient();
createTestRepositories();
createTestIndex();
createTestSnapshots();
// tag::delete-snapshot-request
@ -608,9 +688,14 @@ public class SnapshotClientDocumentationIT extends ESRestHighLevelClientTestCase
assertTrue(highLevelClient().snapshot().createRepository(request, RequestOptions.DEFAULT).isAcknowledged());
}
private void createTestIndex() throws IOException {
createIndex(indexName, Settings.EMPTY);
}
private void createTestSnapshots() throws IOException {
Request createSnapshot = new Request("put", String.format(Locale.ROOT, "_snapshot/%s/%s", repositoryName, snapshotName));
createSnapshot.addParameter("wait_for_completion", "true");
createSnapshot.setJsonEntity("{\"indices\":\"" + indexName + "\"}");
Response response = highLevelClient().getLowLevelClient().performRequest(createSnapshot);
// check that the request went ok without parsing JSON here. When using the high level client, check acknowledgement instead.
assertEquals(200, response.getStatusLine().getStatusCode());

View File

@ -72,11 +72,14 @@ public class RestClientBuilderIntegTests extends RestClientTestCase {
}
public void testBuilderUsesDefaultSSLContext() throws Exception {
assumeFalse("Due to bug inside jdk, this test can't momentarily run with java 11. " +
"See: https://github.com/elastic/elasticsearch/issues/31940",
System.getProperty("java.version").contains("11"));
final SSLContext defaultSSLContext = SSLContext.getDefault();
try {
try (RestClient client = buildRestClient()) {
try {
client.performRequest("GET", "/");
client.performRequest(new Request("GET", "/"));
fail("connection should have been rejected due to SSL handshake");
} catch (Exception e) {
assertThat(e.getMessage(), containsString("General SSLEngine problem"));
@ -85,7 +88,7 @@ public class RestClientBuilderIntegTests extends RestClientTestCase {
SSLContext.setDefault(getSslContext());
try (RestClient client = buildRestClient()) {
Response response = client.performRequest("GET", "/");
Response response = client.performRequest(new Request("GET", "/"));
assertEquals(200, response.getStatusLine().getStatusCode());
}
} finally {

View File

@ -256,35 +256,51 @@ public class RestClientSingleHostIntegTests extends RestClientTestCase {
public void testEncodeParams() throws IOException {
{
Response response = restClient.performRequest("PUT", "/200", Collections.singletonMap("routing", "this/is/the/routing"));
Request request = new Request("PUT", "/200");
request.addParameter("routing", "this/is/the/routing");
Response response = restClient.performRequest(request);
assertEquals(pathPrefix + "/200?routing=this%2Fis%2Fthe%2Frouting", response.getRequestLine().getUri());
}
{
Response response = restClient.performRequest("PUT", "/200", Collections.singletonMap("routing", "this|is|the|routing"));
Request request = new Request("PUT", "/200");
request.addParameter("routing", "this|is|the|routing");
Response response = restClient.performRequest(request);
assertEquals(pathPrefix + "/200?routing=this%7Cis%7Cthe%7Crouting", response.getRequestLine().getUri());
}
{
Response response = restClient.performRequest("PUT", "/200", Collections.singletonMap("routing", "routing#1"));
Request request = new Request("PUT", "/200");
request.addParameter("routing", "routing#1");
Response response = restClient.performRequest(request);
assertEquals(pathPrefix + "/200?routing=routing%231", response.getRequestLine().getUri());
}
{
Response response = restClient.performRequest("PUT", "/200", Collections.singletonMap("routing", "中文"));
Request request = new Request("PUT", "/200");
request.addParameter("routing", "中文");
Response response = restClient.performRequest(request);
assertEquals(pathPrefix + "/200?routing=%E4%B8%AD%E6%96%87", response.getRequestLine().getUri());
}
{
Response response = restClient.performRequest("PUT", "/200", Collections.singletonMap("routing", "foo bar"));
Request request = new Request("PUT", "/200");
request.addParameter("routing", "foo bar");
Response response = restClient.performRequest(request);
assertEquals(pathPrefix + "/200?routing=foo+bar", response.getRequestLine().getUri());
}
{
Response response = restClient.performRequest("PUT", "/200", Collections.singletonMap("routing", "foo+bar"));
Request request = new Request("PUT", "/200");
request.addParameter("routing", "foo+bar");
Response response = restClient.performRequest(request);
assertEquals(pathPrefix + "/200?routing=foo%2Bbar", response.getRequestLine().getUri());
}
{
Response response = restClient.performRequest("PUT", "/200", Collections.singletonMap("routing", "foo/bar"));
Request request = new Request("PUT", "/200");
request.addParameter("routing", "foo/bar");
Response response = restClient.performRequest(request);
assertEquals(pathPrefix + "/200?routing=foo%2Fbar", response.getRequestLine().getUri());
}
{
Response response = restClient.performRequest("PUT", "/200", Collections.singletonMap("routing", "foo^bar"));
Request request = new Request("PUT", "/200");
request.addParameter("routing", "foo^bar");
Response response = restClient.performRequest(request);
assertEquals(pathPrefix + "/200?routing=foo%5Ebar", response.getRequestLine().getUri());
}
}
@ -341,14 +357,14 @@ public class RestClientSingleHostIntegTests extends RestClientTestCase {
public void testUrlWithoutLeadingSlash() throws Exception {
if (pathPrefix.length() == 0) {
try {
restClient.performRequest("GET", "200");
restClient.performRequest(new Request("GET", "200"));
fail("request should have failed");
} catch (ResponseException e) {
assertEquals(404, e.getResponse().getStatusLine().getStatusCode());
}
} else {
{
Response response = restClient.performRequest("GET", "200");
Response response = restClient.performRequest(new Request("GET", "200"));
//a trailing slash gets automatically added if a pathPrefix is configured
assertEquals(200, response.getStatusLine().getStatusCode());
}
@ -357,7 +373,7 @@ public class RestClientSingleHostIntegTests extends RestClientTestCase {
try (RestClient restClient = RestClient.builder(
new HttpHost(httpServer.getAddress().getHostString(), httpServer.getAddress().getPort()))
.setPathPrefix(pathPrefix.substring(1)).build()) {
Response response = restClient.performRequest("GET", "200");
Response response = restClient.performRequest(new Request("GET", "200"));
//a trailing slash gets automatically added if a pathPrefix is configured
assertEquals(200, response.getStatusLine().getStatusCode());
}

View File

@ -267,7 +267,7 @@ public class RestClientDocumentation {
}
{
//tag::rest-client-response2
Response response = restClient.performRequest("GET", "/");
Response response = restClient.performRequest(new Request("GET", "/"));
RequestLine requestLine = response.getRequestLine(); // <1>
HttpHost host = response.getHost(); // <2>
int statusCode = response.getStatusLine().getStatusCode(); // <3>

View File

@ -46,6 +46,12 @@ my %Group_Labels = (
'other' => 'NOT CLASSIFIED',
);
my %Area_Overrides = (
':ml' => 'Machine Learning',
':beats' => 'Beats Plugin',
':Docs' => 'Docs Infrastructure'
);
use JSON();
use Encode qw(encode_utf8);
@ -175,8 +181,14 @@ ISSUE:
# uncomment for including/excluding PRs already issued in other versions
# next if grep {$_->{name}=~/^v2/} @{$issue->{labels}};
my %labels = map { $_->{name} => 1 } @{ $issue->{labels} };
my ($header) = map { m{:[^/]+/(.+)} && $1 }
grep {/^:/} sort keys %labels;
my @area_labels = grep {/^:/} sort keys %labels;
my ($header) = map { m{:[^/]+/(.+)} && $1 } @area_labels;
if (scalar @area_labels > 1) {
$header = "MULTIPLE AREA LABELS";
}
if (scalar @area_labels == 1 && exists $Area_Overrides{$area_labels[0]}) {
$header = $Area_Overrides{$area_labels[0]};
}
$header ||= 'NOT CLASSIFIED';
for (@Groups) {
if ( $labels{$_} ) {

View File

@ -130,12 +130,11 @@ include-tagged::{doc-tests}/MiscellaneousDocumentationIT.java[rest-high-level-cl
--------------------------------------------------
The high-level client will internally create the low-level client used to
perform requests based on the provided builder, and manage its lifecycle.
The high-level client instance needs to be closed when no longer needed so that
all the resources used by it get properly released, as well as the underlying
http client instance and its threads. This can be done through the `close`
method, which will close the internal `RestClient` instance.
perform requests based on the provided builder. That low-level client
maintains a pool of connections and starts some threads so you should
close the high-level client when you are well and truly done with
it and it will in turn close the internal low-level client to free those
resources. This can be done through the `close`:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------

View File

@ -0,0 +1,64 @@
[[java-rest-high-x-pack-info]]
=== X-Pack Info API
[[java-rest-high-x-pack-info-execution]]
==== Execution
General information about the installed {xpack} features can be retrieved
using the `xPackInfo()` method:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MiscellaneousDocumentationIT.java[x-pack-info-execute]
--------------------------------------------------
<1> Enable verbose mode. The default is `false` but `true` will return
more information.
<2> Set the categories of information to retrieve. The the default is to
return no information which is useful for checking if {xpack} is installed
but not much else.
[[java-rest-high-x-pack-info-response]]
==== Response
The returned `XPackInfoResponse` can contain `BuildInfo`, `LicenseInfo`,
and `FeatureSetsInfo` depending on the categories requested.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MiscellaneousDocumentationIT.java[x-pack-info-response]
--------------------------------------------------
<1> `BuildInfo` contains the commit hash from which Elasticsearch was
built and the timestamp that the x-pack module was created.
<2> `LicenseInfo` contains the type of license that the cluster is using
and its expiration date.
<3> Basic licenses do not expire and will return this constant.
<4> `FeatureSetsInfo` contains a `Map` from the name of a feature to
information about a feature like whether or not it is available under
the current license.
[[java-rest-high-x-pack-info-async]]
==== Asynchronous Execution
This request can be executed asynchronously:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MiscellaneousDocumentationIT.java[x-pack-info-execute-async]
--------------------------------------------------
<1> The `XPackInfoRequest` to execute and the `ActionListener` to use when
the execution completes
The asynchronous method does not block and returns immediately. Once it is
completed the `ActionListener` is called back using the `onResponse` method
if the execution successfully completed or using the `onFailure` method if
it failed.
A typical listener for `XPackInfoResponse` looks like:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MiscellaneousDocumentationIT.java[x-pack-info-execute-listener]
--------------------------------------------------
<1> Called when the execution is successfully completed. The response is
provided as an argument
<2> Called in case of failure. The raised exception is provided as an argument

View File

@ -0,0 +1,97 @@
[[java-rest-high-snapshot-snapshots-status]]
=== Snapshots Status API
The Snapshots Status API allows to retrieve detailed information about snapshots in progress.
[[java-rest-high-snapshot-snapshots-status-request]]
==== Snapshots Status Request
A `SnapshotsStatusRequest`:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-request]
--------------------------------------------------
==== Required Arguments
The following arguments must be provided:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-request-repository]
--------------------------------------------------
<1> Sets the repository to check for snapshot statuses
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-request-snapshots]
--------------------------------------------------
<1> The list of snapshot names to check the status of
==== Optional Arguments
The following arguments can optionally be provided:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-request-ignoreUnavailable]
--------------------------------------------------
<1> The command will fail if some of the snapshots are unavailable. The `ignore_unavailable` flag
set to true will return all snapshots that are currently available.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-request-masterTimeout]
--------------------------------------------------
<1> Timeout to connect to the master node as a `TimeValue`
<2> Timeout to connect to the master node as a `String`
[[java-rest-high-snapshot-snapshots-status-sync]]
==== Synchronous Execution
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-execute]
--------------------------------------------------
[[java-rest-high-snapshot-snapshots-status-async]]
==== Asynchronous Execution
The asynchronous execution of retrieving snapshot statuses requires both the
`SnapshotsStatusRequest` instance and an `ActionListener` instance to be
passed to the asynchronous method:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-execute-async]
--------------------------------------------------
<1> The `SnapshotsStatusRequest` to execute and the `ActionListener`
to use when the execution completes
The asynchronous method does not block and returns immediately. Once it is
completed the `ActionListener` is called back using the `onResponse` method
if the execution successfully completed or using the `onFailure` method if
it failed.
A typical listener for `SnapshotsStatusResponse` looks like:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-execute-listener]
--------------------------------------------------
<1> Called when the execution is successfully completed. The response is
provided as an argument
<2> Called in case of a failure. The raised exception is provided as an argument
[[java-rest-high-snapshot-snapshots-status-response]]
==== Snapshots Status Response
The returned `SnapshotsStatusResponse` allows to retrieve information about the
executed operation as follows:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[snapshots-status-response]
--------------------------------------------------
<1> Request contains a list of snapshot statuses
<2> Each status contains information about the snapshot
<3> Example of reading snapshot statistics about a specific index and shard

View File

@ -53,9 +53,11 @@ The Java High Level REST Client supports the following Miscellaneous APIs:
* <<java-rest-high-main>>
* <<java-rest-high-ping>>
* <<java-rest-high-x-pack-info>>
include::miscellaneous/main.asciidoc[]
include::miscellaneous/ping.asciidoc[]
include::miscellaneous/x-pack-info.asciidoc[]
== Indices APIs
@ -152,6 +154,7 @@ The Java High Level REST Client supports the following Snapshot APIs:
* <<java-rest-high-snapshot-verify-repository>>
* <<java-rest-high-snapshot-create-snapshot>>
* <<java-rest-high-snapshot-get-snapshots>>
* <<java-rest-high-snapshot-snapshots-status>>
* <<java-rest-high-snapshot-delete-snapshot>>
include::snapshot/get_repository.asciidoc[]
@ -160,6 +163,7 @@ include::snapshot/delete_repository.asciidoc[]
include::snapshot/verify_repository.asciidoc[]
include::snapshot/create_snapshot.asciidoc[]
include::snapshot/get_snapshots.asciidoc[]
include::snapshot/snapshots_status.asciidoc[]
include::snapshot/delete_snapshot.asciidoc[]
== Tasks APIs
@ -181,4 +185,3 @@ The Java High Level REST Client supports the following Scripts APIs:
include::script/get_script.asciidoc[]
include::script/delete_script.asciidoc[]

View File

@ -119,6 +119,30 @@ GET hockey/_search
----------------------------------------------------------------
// CONSOLE
[float]
===== Missing values
If you request the value from a field `field` that isnt in
the document, `doc['field'].value` for this document returns:
- `0` if a `field` has a numeric datatype (long, double etc.)
- `false` is a `field` has a boolean datatype
- epoch date if a `field` has a date datatype
- `null` if a `field` has a string datatype
- `null` if a `field` has a geo datatype
- `""` if a `field` has a binary datatype
IMPORTANT: Starting in 7.0, `doc['field'].value` throws an exception if
the field is missing in a document. To enable this behavior now,
set a {ref}/jvm-options.html[`jvm.option`]
`-Des.scripting.exception_for_missing_value=true` on a node. If you do not enable
this behavior, a deprecation warning is logged on start up.
To check if a document is missing a value, you can call
`doc['field'].size() == 0`.
[float]
==== Updating Fields with Painless

View File

@ -33,7 +33,7 @@ Available expressions for interval: `year` (`1y`), `quarter` (`1q`), `month` (`1
Time values can also be specified via abbreviations supported by <<time-units,time units>> parsing.
Note that fractional time values are not supported, but you can address this by shifting to another
time unit (e.g., `1.5h` could instead be specified as `90m`). Also note that time intervals larger than
than days do not support arbitrary values but can only be one unit large (e.g. `1y` is valid, `2y` is not).
days do not support arbitrary values but can only be one unit large (e.g. `1y` is valid, `2y` is not).
[source,js]
--------------------------------------------------

View File

@ -50,7 +50,49 @@ PUT /test_index
The above configures a `search_synonyms` filter, with a path of
`analysis/synonym.txt` (relative to the `config` location). The
`search_synonyms` analyzer is then configured with the filter.
Additional settings are: `expand` (defaults to `true`).
Additional settings are:
* `expand` (defaults to `true`).
* `lenient` (defaults to `false`). If `true` ignores exceptions while parsing the synonym configuration. It is important
to note that only those synonym rules which cannot get parsed are ignored. For instance consider the following request:
[source,js]
--------------------------------------------------
PUT /test_index
{
"settings": {
"index" : {
"analysis" : {
"analyzer" : {
"synonym" : {
"tokenizer" : "standard",
"filter" : ["my_stop", "synonym_graph"]
}
},
"filter" : {
"my_stop": {
"type" : "stop",
"stopwords": ["bar"]
},
"synonym_graph" : {
"type" : "synonym_graph",
"lenient": true,
"synonyms" : ["foo, bar => baz"]
}
}
}
}
}
}
--------------------------------------------------
// CONSOLE
With the above request the word `bar` gets skipped but a mapping `foo => baz` is still added. However, if the mapping
being added was "foo, baz => bar" nothing would get added to the synonym list. This is because the target word for the
mapping is itself eliminated because it was a stop word. Similarly, if the mapping was "bar, foo, baz" and `expand` was
set to `false` no mapping would get added as when `expand=false` the target mapping is the first word. However, if
`expand=true` then the mappings added would be equivalent to `foo, baz => foo, baz` i.e, all mappings other than the
stop word.
[float]
==== `tokenizer` and `ignore_case` are deprecated

View File

@ -33,12 +33,55 @@ PUT /test_index
The above configures a `synonym` filter, with a path of
`analysis/synonym.txt` (relative to the `config` location). The
`synonym` analyzer is then configured with the filter. Additional
settings is: `expand` (defaults to `true`).
`synonym` analyzer is then configured with the filter.
This filter tokenize synonyms with whatever tokenizer and token filters
appear before it in the chain.
Additional settings are:
* `expand` (defaults to `true`).
* `lenient` (defaults to `false`). If `true` ignores exceptions while parsing the synonym configuration. It is important
to note that only those synonym rules which cannot get parsed are ignored. For instance consider the following request:
[source,js]
--------------------------------------------------
PUT /test_index
{
"settings": {
"index" : {
"analysis" : {
"analyzer" : {
"synonym" : {
"tokenizer" : "standard",
"filter" : ["my_stop", "synonym"]
}
},
"filter" : {
"my_stop": {
"type" : "stop",
"stopwords": ["bar"]
},
"synonym" : {
"type" : "synonym",
"lenient": true,
"synonyms" : ["foo, bar => baz"]
}
}
}
}
}
}
--------------------------------------------------
// CONSOLE
With the above request the word `bar` gets skipped but a mapping `foo => baz` is still added. However, if the mapping
being added was "foo, baz => bar" nothing would get added to the synonym list. This is because the target word for the
mapping is itself eliminated because it was a stop word. Similarly, if the mapping was "bar, foo, baz" and `expand` was
set to `false` no mapping would get added as when `expand=false` the target mapping is the first word. However, if
`expand=true` then the mappings added would be equivalent to `foo, baz => foo, baz` i.e, all mappings other than the
stop word.
[float]
==== `tokenizer` and `ignore_case` are deprecated

View File

@ -104,6 +104,11 @@ With that out of the way, let's get started with the fun part...
== Installation
You can skip installation completely by using our hosted
Elasticsearch Service on https://www.elastic.co/cloud[Elastic Cloud], which is
available on AWS and GCP. You can
https://www.elastic.co/cloud/elasticsearch-service/signup[try out the hosted service] for free.
Elasticsearch requires at least Java 8. Specifically as of this writing, it is recommended that you use the Oracle JDK version {jdk}. Java installation varies from platform to platform so we won't go into those details here. Oracle's recommended installation documentation can be found on http://docs.oracle.com/javase/8/docs/technotes/guides/install/install_overview.html[Oracle's website]. Suffice to say, before you install Elasticsearch, please check your Java version first by running (and then install/upgrade accordingly if needed):
[source,sh]

View File

@ -1767,8 +1767,9 @@ Removes existing fields. If one field doesn't exist, an exception will be thrown
.Remove Options
[options="header"]
|======
| Name | Required | Default | Description
| `field` | yes | - | Fields to be removed
| Name | Required | Default | Description
| `field` | yes | - | Fields to be removed
| `ignore_missing` | no | `false` | If `true` and `field` does not exist or is `null`, the processor quietly exits without modifying the document
|======
Here is an example to remove a single field:

View File

@ -85,6 +85,13 @@ for a particular index with the index setting `index.max_regex_length`.
Search requests with extra content after the main object will no longer be accepted
by the `_search` endpoint. A parsing exception will be thrown instead.
==== Context Completion Suggester
The ability to query and index context enabled suggestions without context,
deprecated in 6.x, has been removed. Context enabled suggestion queries
without contexts have to visit every suggestion, which degrades the search performance
considerably.
==== Semantics changed for `max_concurrent_shard_requests`
`max_concurrent_shard_requests` used to limit the total number of concurrent shard

View File

@ -1,6 +1,11 @@
[[install-elasticsearch]]
== Installing Elasticsearch
Elasticsearch can be run on your own hardware or using our hosted
Elasticsearch Service on https://www.elastic.co/cloud[Elastic Cloud], which is
available on AWS and GCP. You can
https://www.elastic.co/cloud/elasticsearch-service/signup[try out the hosted service] for free.
Elasticsearch is provided in the following package formats:
[horizontal]
@ -38,7 +43,7 @@ Elasticsearch on Windows. MSIs may be downloaded from the Elasticsearch website.
`docker`::
Images are available for running Elasticsearch as Docker containers. They may be
downloaded from the Elastic Docker Registry.
downloaded from the Elastic Docker Registry.
+
{ref}/docker.html[Install {es} with Docker]

View File

@ -8,8 +8,6 @@ A list of all published Docker images and tags can be found in
https://www.docker.elastic.co[www.docker.elastic.co]. The source code can be found
on https://github.com/elastic/elasticsearch-docker/tree/{branch}[GitHub].
==== Image types
These images are free to use under the Elastic license. They contain open source
and free commercial features and access to paid commercial features.
{xpack-ref}/license-management.html[Start a 30-day trial] to try out all of the
@ -17,9 +15,6 @@ paid commercial features. See the
https://www.elastic.co/subscriptions[Subscriptions] page for information about
Elastic license levels.
Alternatively, you can download `-oss` images, which contain only features that
are available under the Apache 2.0 license.
==== Pulling the image
Obtaining {es} for Docker is as simple as issuing a +docker pull+ command
@ -34,14 +29,17 @@ endif::[]
ifeval::["{release-state}"!="unreleased"]
Docker images can be retrieved with the following commands:
For example, the Docker image can be retrieved with the following command:
["source","sh",subs="attributes"]
--------------------------------------------
docker pull {docker-repo}:{version}
docker pull {docker-repo}-oss:{version}
--------------------------------------------
Alternatively, you can download other Docker images that contain only features
that are available under the Apache 2.0 license from
https://www.docker.elastic.co[www.docker.elastic.co].
endif::[]
[[docker-cli-run]]

View File

@ -34,10 +34,6 @@ ifeval::["{release-state}"!="unreleased"]
Download the `.msi` package for Elasticsearch v{version} from https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-{version}.msi
Alternatively, you can download the following package, which contains only
features that are available under the Apache 2.0 license:
https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-oss-{version}.msi
endif::[]
[[install-msi-gui]]

View File

@ -21,12 +21,19 @@ package org.elasticsearch.nio;
import java.io.IOException;
import java.util.function.Consumer;
import java.util.function.Predicate;
public class BytesChannelContext extends SocketChannelContext {
public BytesChannelContext(NioSocketChannel channel, NioSelector selector, Consumer<Exception> exceptionHandler,
ReadWriteHandler handler, InboundChannelBuffer channelBuffer) {
super(channel, selector, exceptionHandler, handler, channelBuffer);
this(channel, selector, exceptionHandler, handler, channelBuffer, ALWAYS_ALLOW_CHANNEL);
}
public BytesChannelContext(NioSocketChannel channel, NioSelector selector, Consumer<Exception> exceptionHandler,
ReadWriteHandler handler, InboundChannelBuffer channelBuffer,
Predicate<NioSocketChannel> allowChannelPredicate) {
super(channel, selector, exceptionHandler, handler, channelBuffer, allowChannelPredicate);
}
@Override
@ -77,7 +84,7 @@ public class BytesChannelContext extends SocketChannelContext {
@Override
public boolean selectorShouldClose() {
return isPeerClosed() || hasIOException() || isClosing.get();
return closeNow() || isClosing.get();
}
/**

View File

@ -47,6 +47,11 @@ public abstract class ChannelContext<S extends SelectableChannel & NetworkChanne
}
protected void register() throws IOException {
doSelectorRegister();
}
// Package private for testing
void doSelectorRegister() throws IOException {
setSelectionKey(rawChannel.register(getSelector().rawSelector(), 0));
}

View File

@ -31,6 +31,7 @@ import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* This context should implement the specific logic for a channel. When a channel receives a notification
@ -43,24 +44,28 @@ import java.util.function.Consumer;
*/
public abstract class SocketChannelContext extends ChannelContext<SocketChannel> {
public static final Predicate<NioSocketChannel> ALWAYS_ALLOW_CHANNEL = (c) -> true;
protected final NioSocketChannel channel;
protected final InboundChannelBuffer channelBuffer;
protected final AtomicBoolean isClosing = new AtomicBoolean(false);
private final ReadWriteHandler readWriteHandler;
private final Predicate<NioSocketChannel> allowChannelPredicate;
private final NioSelector selector;
private final CompletableContext<Void> connectContext = new CompletableContext<>();
private final LinkedList<FlushOperation> pendingFlushes = new LinkedList<>();
private boolean ioException;
private boolean peerClosed;
private boolean closeNow;
private Exception connectException;
protected SocketChannelContext(NioSocketChannel channel, NioSelector selector, Consumer<Exception> exceptionHandler,
ReadWriteHandler readWriteHandler, InboundChannelBuffer channelBuffer) {
ReadWriteHandler readWriteHandler, InboundChannelBuffer channelBuffer,
Predicate<NioSocketChannel> allowChannelPredicate) {
super(channel.getRawChannel(), exceptionHandler);
this.selector = selector;
this.channel = channel;
this.readWriteHandler = readWriteHandler;
this.channelBuffer = channelBuffer;
this.allowChannelPredicate = allowChannelPredicate;
}
@Override
@ -161,6 +166,14 @@ public abstract class SocketChannelContext extends ChannelContext<SocketChannel>
return pendingFlushes.peekFirst();
}
@Override
protected void register() throws IOException {
super.register();
if (allowChannelPredicate.test(channel) == false) {
closeNow = true;
}
}
@Override
public void closeFromSelector() throws IOException {
getSelector().assertOnSelectorThread();
@ -217,24 +230,20 @@ public abstract class SocketChannelContext extends ChannelContext<SocketChannel>
*/
public abstract boolean selectorShouldClose();
protected boolean hasIOException() {
return ioException;
}
protected boolean isPeerClosed() {
return peerClosed;
protected boolean closeNow() {
return closeNow;
}
protected int readFromChannel(ByteBuffer buffer) throws IOException {
try {
int bytesRead = rawChannel.read(buffer);
if (bytesRead < 0) {
peerClosed = true;
closeNow = true;
bytesRead = 0;
}
return bytesRead;
} catch (IOException e) {
ioException = true;
closeNow = true;
throw e;
}
}
@ -243,12 +252,12 @@ public abstract class SocketChannelContext extends ChannelContext<SocketChannel>
try {
int bytesRead = (int) rawChannel.read(buffers);
if (bytesRead < 0) {
peerClosed = true;
closeNow = true;
bytesRead = 0;
}
return bytesRead;
} catch (IOException e) {
ioException = true;
closeNow = true;
throw e;
}
}
@ -257,7 +266,7 @@ public abstract class SocketChannelContext extends ChannelContext<SocketChannel>
try {
return rawChannel.write(buffer);
} catch (IOException e) {
ioException = true;
closeNow = true;
throw e;
}
}
@ -266,7 +275,7 @@ public abstract class SocketChannelContext extends ChannelContext<SocketChannel>
try {
return (int) rawChannel.write(buffers);
} catch (IOException e) {
ioException = true;
closeNow = true;
throw e;
}
}

View File

@ -33,6 +33,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import static org.mockito.Matchers.any;
@ -77,7 +78,7 @@ public class SocketChannelContextTests extends ESTestCase {
when(rawChannel.write(any(ByteBuffer.class))).thenThrow(new IOException());
when(rawChannel.read(any(ByteBuffer[].class), anyInt(), anyInt())).thenThrow(new IOException());
when(rawChannel.read(any(ByteBuffer.class))).thenThrow(new IOException());
assertFalse(context.hasIOException());
assertFalse(context.closeNow());
expectThrows(IOException.class, () -> {
if (randomBoolean()) {
context.read();
@ -85,15 +86,31 @@ public class SocketChannelContextTests extends ESTestCase {
context.flushChannel();
}
});
assertTrue(context.hasIOException());
assertTrue(context.closeNow());
}
public void testSignalWhenPeerClosed() throws IOException {
when(rawChannel.read(any(ByteBuffer[].class), anyInt(), anyInt())).thenReturn(-1L);
when(rawChannel.read(any(ByteBuffer.class))).thenReturn(-1);
assertFalse(context.isPeerClosed());
assertFalse(context.closeNow());
context.read();
assertTrue(context.isPeerClosed());
assertTrue(context.closeNow());
}
public void testValidateInRegisterCanSucceed() throws IOException {
InboundChannelBuffer channelBuffer = InboundChannelBuffer.allocatingInstance();
context = new TestSocketChannelContext(channel, selector, exceptionHandler, readWriteHandler, channelBuffer, (c) -> true);
assertFalse(context.closeNow());
context.register();
assertFalse(context.closeNow());
}
public void testValidateInRegisterCanFail() throws IOException {
InboundChannelBuffer channelBuffer = InboundChannelBuffer.allocatingInstance();
context = new TestSocketChannelContext(channel, selector, exceptionHandler, readWriteHandler, channelBuffer, (c) -> false);
assertFalse(context.closeNow());
context.register();
assertTrue(context.closeNow());
}
public void testConnectSucceeds() throws IOException {
@ -277,7 +294,13 @@ public class SocketChannelContextTests extends ESTestCase {
private TestSocketChannelContext(NioSocketChannel channel, NioSelector selector, Consumer<Exception> exceptionHandler,
ReadWriteHandler readWriteHandler, InboundChannelBuffer channelBuffer) {
super(channel, selector, exceptionHandler, readWriteHandler, channelBuffer);
this(channel, selector, exceptionHandler, readWriteHandler, channelBuffer, ALWAYS_ALLOW_CHANNEL);
}
private TestSocketChannelContext(NioSocketChannel channel, NioSelector selector, Consumer<Exception> exceptionHandler,
ReadWriteHandler readWriteHandler, InboundChannelBuffer channelBuffer,
Predicate<NioSocketChannel> allowChannelPredicate) {
super(channel, selector, exceptionHandler, readWriteHandler, channelBuffer, allowChannelPredicate);
}
@Override
@ -309,6 +332,11 @@ public class SocketChannelContextTests extends ESTestCase {
public void closeChannel() {
isClosing.set(true);
}
@Override
void doSelectorRegister() {
// We do not want to call the actual register with selector method as it will throw a NPE
}
}
private static byte[] createMessage(int length) {

View File

@ -294,6 +294,7 @@ public final class ConstructingObjectParser<Value, Context> extends AbstractObje
}
}
@Override
public String getName() {
return objectParser.getName();
}

View File

@ -411,6 +411,7 @@ public final class ObjectParser<Value, Context> extends AbstractObjectParser<Val
INT_ARRAY(START_ARRAY, VALUE_NUMBER, VALUE_STRING),
BOOLEAN_ARRAY(START_ARRAY, VALUE_BOOLEAN),
OBJECT(START_OBJECT),
OBJECT_OR_NULL(START_OBJECT, VALUE_NULL),
OBJECT_ARRAY(START_OBJECT, START_ARRAY),
OBJECT_OR_BOOLEAN(START_OBJECT, VALUE_BOOLEAN),
OBJECT_OR_STRING(START_OBJECT, VALUE_STRING),

View File

@ -32,6 +32,8 @@ import org.elasticsearch.ingest.AbstractProcessor;
import org.elasticsearch.ingest.ConfigurationUtils;
import org.elasticsearch.ingest.IngestDocument;
import org.elasticsearch.ingest.Processor;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.TemplateScript;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
@ -42,21 +44,22 @@ public final class DateIndexNameProcessor extends AbstractProcessor {
public static final String TYPE = "date_index_name";
private final String field;
private final String indexNamePrefix;
private final String dateRounding;
private final String indexNameFormat;
private final TemplateScript.Factory indexNamePrefixTemplate;
private final TemplateScript.Factory dateRoundingTemplate;
private final TemplateScript.Factory indexNameFormatTemplate;
private final DateTimeZone timezone;
private final List<Function<String, DateTime>> dateFormats;
DateIndexNameProcessor(String tag, String field, List<Function<String, DateTime>> dateFormats, DateTimeZone timezone,
String indexNamePrefix, String dateRounding, String indexNameFormat) {
TemplateScript.Factory indexNamePrefixTemplate, TemplateScript.Factory dateRoundingTemplate,
TemplateScript.Factory indexNameFormatTemplate) {
super(tag);
this.field = field;
this.timezone = timezone;
this.dateFormats = dateFormats;
this.indexNamePrefix = indexNamePrefix;
this.dateRounding = dateRounding;
this.indexNameFormat = indexNameFormat;
this.indexNamePrefixTemplate = indexNamePrefixTemplate;
this.dateRoundingTemplate = dateRoundingTemplate;
this.indexNameFormatTemplate = indexNameFormatTemplate;
}
@Override
@ -83,6 +86,9 @@ public final class DateIndexNameProcessor extends AbstractProcessor {
if (dateTime == null) {
throw new IllegalArgumentException("unable to parse date [" + date + "]", lastException);
}
String indexNamePrefix = ingestDocument.renderTemplate(indexNamePrefixTemplate);
String indexNameFormat = ingestDocument.renderTemplate(indexNameFormatTemplate);
String dateRounding = ingestDocument.renderTemplate(dateRoundingTemplate);
DateTimeFormatter formatter = DateTimeFormat.forPattern(indexNameFormat);
StringBuilder builder = new StringBuilder()
@ -106,16 +112,16 @@ public final class DateIndexNameProcessor extends AbstractProcessor {
return field;
}
String getIndexNamePrefix() {
return indexNamePrefix;
TemplateScript.Factory getIndexNamePrefixTemplate() {
return indexNamePrefixTemplate;
}
String getDateRounding() {
return dateRounding;
TemplateScript.Factory getDateRoundingTemplate() {
return dateRoundingTemplate;
}
String getIndexNameFormat() {
return indexNameFormat;
TemplateScript.Factory getIndexNameFormatTemplate() {
return indexNameFormatTemplate;
}
DateTimeZone getTimezone() {
@ -128,6 +134,12 @@ public final class DateIndexNameProcessor extends AbstractProcessor {
public static final class Factory implements Processor.Factory {
private final ScriptService scriptService;
public Factory(ScriptService scriptService) {
this.scriptService = scriptService;
}
@Override
public DateIndexNameProcessor create(Map<String, Processor.Factory> registry, String tag,
Map<String, Object> config) throws Exception {
@ -154,9 +166,16 @@ public final class DateIndexNameProcessor extends AbstractProcessor {
String field = ConfigurationUtils.readStringProperty(TYPE, tag, config, "field");
String indexNamePrefix = ConfigurationUtils.readStringProperty(TYPE, tag, config, "index_name_prefix", "");
TemplateScript.Factory indexNamePrefixTemplate =
ConfigurationUtils.compileTemplate(TYPE, tag, "index_name_prefix", indexNamePrefix, scriptService);
String dateRounding = ConfigurationUtils.readStringProperty(TYPE, tag, config, "date_rounding");
TemplateScript.Factory dateRoundingTemplate =
ConfigurationUtils.compileTemplate(TYPE, tag, "date_rounding", dateRounding, scriptService);
String indexNameFormat = ConfigurationUtils.readStringProperty(TYPE, tag, config, "index_name_format", "yyyy-MM-dd");
return new DateIndexNameProcessor(tag, field, dateFormats, timezone, indexNamePrefix, dateRounding, indexNameFormat);
TemplateScript.Factory indexNameFormatTemplate =
ConfigurationUtils.compileTemplate(TYPE, tag, "index_name_format", indexNameFormat, scriptService);
return new DateIndexNameProcessor(tag, field, dateFormats, timezone, indexNamePrefixTemplate,
dateRoundingTemplate, indexNameFormatTemplate);
}
}

View File

@ -62,7 +62,7 @@ public class IngestCommonPlugin extends Plugin implements ActionPlugin, IngestPl
processors.put(DateProcessor.TYPE, new DateProcessor.Factory(parameters.scriptService));
processors.put(SetProcessor.TYPE, new SetProcessor.Factory(parameters.scriptService));
processors.put(AppendProcessor.TYPE, new AppendProcessor.Factory(parameters.scriptService));
processors.put(RenameProcessor.TYPE, new RenameProcessor.Factory());
processors.put(RenameProcessor.TYPE, new RenameProcessor.Factory(parameters.scriptService));
processors.put(RemoveProcessor.TYPE, new RemoveProcessor.Factory(parameters.scriptService));
processors.put(SplitProcessor.TYPE, new SplitProcessor.Factory());
processors.put(JoinProcessor.TYPE, new JoinProcessor.Factory());
@ -73,7 +73,7 @@ public class IngestCommonPlugin extends Plugin implements ActionPlugin, IngestPl
processors.put(GsubProcessor.TYPE, new GsubProcessor.Factory());
processors.put(FailProcessor.TYPE, new FailProcessor.Factory(parameters.scriptService));
processors.put(ForEachProcessor.TYPE, new ForEachProcessor.Factory());
processors.put(DateIndexNameProcessor.TYPE, new DateIndexNameProcessor.Factory());
processors.put(DateIndexNameProcessor.TYPE, new DateIndexNameProcessor.Factory(parameters.scriptService));
processors.put(SortProcessor.TYPE, new SortProcessor.Factory());
processors.put(GrokProcessor.TYPE, new GrokProcessor.Factory(GROK_PATTERNS, createGrokThreadWatchdog(parameters)));
processors.put(ScriptProcessor.TYPE, new ScriptProcessor.Factory(parameters.scriptService));
@ -97,12 +97,12 @@ public class IngestCommonPlugin extends Plugin implements ActionPlugin, IngestPl
Supplier<DiscoveryNodes> nodesInCluster) {
return Arrays.asList(new GrokProcessorGetAction.RestAction(settings, restController));
}
@Override
public List<Setting<?>> getSettings() {
return Arrays.asList(WATCHDOG_INTERVAL, WATCHDOG_MAX_EXECUTION_TIME);
}
private static ThreadWatchdog createGrokThreadWatchdog(Processor.Parameters parameters) {
long intervalMillis = WATCHDOG_INTERVAL.get(parameters.env.settings()).getMillis();
long maxExecutionTimeMillis = WATCHDOG_MAX_EXECUTION_TIME.get(parameters.env.settings()).getMillis();

View File

@ -39,10 +39,12 @@ public final class RemoveProcessor extends AbstractProcessor {
public static final String TYPE = "remove";
private final List<TemplateScript.Factory> fields;
private final boolean ignoreMissing;
RemoveProcessor(String tag, List<TemplateScript.Factory> fields) {
RemoveProcessor(String tag, List<TemplateScript.Factory> fields, boolean ignoreMissing) {
super(tag);
this.fields = new ArrayList<>(fields);
this.ignoreMissing = ignoreMissing;
}
public List<TemplateScript.Factory> getFields() {
@ -51,7 +53,16 @@ public final class RemoveProcessor extends AbstractProcessor {
@Override
public void execute(IngestDocument document) {
fields.forEach(document::removeField);
if (ignoreMissing) {
fields.forEach(field -> {
String path = document.renderTemplate(field);
if (document.hasField(path)) {
document.removeField(path);
}
});
} else {
fields.forEach(document::removeField);
}
}
@Override
@ -83,7 +94,8 @@ public final class RemoveProcessor extends AbstractProcessor {
final List<TemplateScript.Factory> compiledTemplates = fields.stream()
.map(f -> ConfigurationUtils.compileTemplate(TYPE, processorTag, "field", f, scriptService))
.collect(Collectors.toList());
return new RemoveProcessor(processorTag, compiledTemplates);
boolean ignoreMissing = ConfigurationUtils.readBooleanProperty(TYPE, processorTag, config, "ignore_missing", false);
return new RemoveProcessor(processorTag, compiledTemplates, ignoreMissing);
}
}
}

View File

@ -23,6 +23,8 @@ import org.elasticsearch.ingest.AbstractProcessor;
import org.elasticsearch.ingest.ConfigurationUtils;
import org.elasticsearch.ingest.IngestDocument;
import org.elasticsearch.ingest.Processor;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.TemplateScript;
import java.util.Map;
@ -33,22 +35,22 @@ public final class RenameProcessor extends AbstractProcessor {
public static final String TYPE = "rename";
private final String field;
private final String targetField;
private final TemplateScript.Factory field;
private final TemplateScript.Factory targetField;
private final boolean ignoreMissing;
RenameProcessor(String tag, String field, String targetField, boolean ignoreMissing) {
RenameProcessor(String tag, TemplateScript.Factory field, TemplateScript.Factory targetField, boolean ignoreMissing) {
super(tag);
this.field = field;
this.targetField = targetField;
this.ignoreMissing = ignoreMissing;
}
String getField() {
TemplateScript.Factory getField() {
return field;
}
String getTargetField() {
TemplateScript.Factory getTargetField() {
return targetField;
}
@ -58,28 +60,30 @@ public final class RenameProcessor extends AbstractProcessor {
@Override
public void execute(IngestDocument document) {
if (document.hasField(field, true) == false) {
String path = document.renderTemplate(field);
if (document.hasField(path, true) == false) {
if (ignoreMissing) {
return;
} else {
throw new IllegalArgumentException("field [" + field + "] doesn't exist");
throw new IllegalArgumentException("field [" + path + "] doesn't exist");
}
}
// We fail here if the target field point to an array slot that is out of range.
// If we didn't do this then we would fail if we set the value in the target_field
// and then on failure processors would not see that value we tried to rename as we already
// removed it.
if (document.hasField(targetField, true)) {
throw new IllegalArgumentException("field [" + targetField + "] already exists");
String target = document.renderTemplate(targetField);
if (document.hasField(target, true)) {
throw new IllegalArgumentException("field [" + target + "] already exists");
}
Object value = document.getFieldValue(field, Object.class);
document.removeField(field);
Object value = document.getFieldValue(path, Object.class);
document.removeField(path);
try {
document.setFieldValue(targetField, value);
document.setFieldValue(target, value);
} catch (Exception e) {
// setting the value back to the original field shouldn't as we just fetched it from that field:
document.setFieldValue(field, value);
document.setFieldValue(path, value);
throw e;
}
}
@ -90,13 +94,24 @@ public final class RenameProcessor extends AbstractProcessor {
}
public static final class Factory implements Processor.Factory {
private final ScriptService scriptService;
public Factory(ScriptService scriptService) {
this.scriptService = scriptService;
}
@Override
public RenameProcessor create(Map<String, Processor.Factory> registry, String processorTag,
Map<String, Object> config) throws Exception {
String field = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "field");
TemplateScript.Factory fieldTemplate = ConfigurationUtils.compileTemplate(TYPE, processorTag,
"field", field, scriptService);
String targetField = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "target_field");
TemplateScript.Factory targetFieldTemplate = ConfigurationUtils.compileTemplate(TYPE, processorTag,
"target_field", targetField, scriptService);
boolean ignoreMissing = ConfigurationUtils.readBooleanProperty(TYPE, processorTag, config, "ignore_missing", false);
return new RenameProcessor(processorTag, field, targetField, ignoreMissing);
return new RenameProcessor(processorTag, fieldTemplate, targetFieldTemplate , ignoreMissing);
}
}
}

View File

@ -20,18 +20,20 @@
package org.elasticsearch.ingest.common;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.ingest.TestTemplateService;
import org.elasticsearch.test.ESTestCase;
import org.hamcrest.Matchers;
import org.joda.time.DateTimeZone;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class DateIndexNameFactoryTests extends ESTestCase {
public void testDefaults() throws Exception {
DateIndexNameProcessor.Factory factory = new DateIndexNameProcessor.Factory();
DateIndexNameProcessor.Factory factory = new DateIndexNameProcessor.Factory(TestTemplateService.instance());
Map<String, Object> config = new HashMap<>();
config.put("field", "_field");
config.put("date_rounding", "y");
@ -39,14 +41,14 @@ public class DateIndexNameFactoryTests extends ESTestCase {
DateIndexNameProcessor processor = factory.create(null, null, config);
assertThat(processor.getDateFormats().size(), Matchers.equalTo(1));
assertThat(processor.getField(), Matchers.equalTo("_field"));
assertThat(processor.getIndexNamePrefix(), Matchers.equalTo(""));
assertThat(processor.getDateRounding(), Matchers.equalTo("y"));
assertThat(processor.getIndexNameFormat(), Matchers.equalTo("yyyy-MM-dd"));
assertThat(processor.getIndexNamePrefixTemplate().newInstance(Collections.emptyMap()).execute(), Matchers.equalTo(""));
assertThat(processor.getDateRoundingTemplate().newInstance(Collections.emptyMap()).execute(), Matchers.equalTo("y"));
assertThat(processor.getIndexNameFormatTemplate().newInstance(Collections.emptyMap()).execute(), Matchers.equalTo("yyyy-MM-dd"));
assertThat(processor.getTimezone(), Matchers.equalTo(DateTimeZone.UTC));
}
public void testSpecifyOptionalSettings() throws Exception {
DateIndexNameProcessor.Factory factory = new DateIndexNameProcessor.Factory();
DateIndexNameProcessor.Factory factory = new DateIndexNameProcessor.Factory(TestTemplateService.instance());
Map<String, Object> config = new HashMap<>();
config.put("field", "_field");
config.put("index_name_prefix", "_prefix");
@ -63,7 +65,7 @@ public class DateIndexNameFactoryTests extends ESTestCase {
config.put("index_name_format", "yyyyMMdd");
processor = factory.create(null, null, config);
assertThat(processor.getIndexNameFormat(), Matchers.equalTo("yyyyMMdd"));
assertThat(processor.getIndexNameFormatTemplate().newInstance(Collections.emptyMap()).execute(), Matchers.equalTo("yyyyMMdd"));
config = new HashMap<>();
config.put("field", "_field");
@ -80,11 +82,11 @@ public class DateIndexNameFactoryTests extends ESTestCase {
config.put("date_rounding", "y");
processor = factory.create(null, null, config);
assertThat(processor.getIndexNamePrefix(), Matchers.equalTo("_prefix"));
assertThat(processor.getIndexNamePrefixTemplate().newInstance(Collections.emptyMap()).execute(), Matchers.equalTo("_prefix"));
}
public void testRequiredFields() throws Exception {
DateIndexNameProcessor.Factory factory = new DateIndexNameProcessor.Factory();
DateIndexNameProcessor.Factory factory = new DateIndexNameProcessor.Factory(TestTemplateService.instance());
Map<String, Object> config = new HashMap<>();
config.put("date_rounding", "y");
ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class, () -> factory.create(null, null, config));
@ -95,5 +97,4 @@ public class DateIndexNameFactoryTests extends ESTestCase {
e = expectThrows(ElasticsearchParseException.class, () -> factory.create(null, null, config));
assertThat(e.getMessage(), Matchers.equalTo("[date_rounding] required property is missing"));
}
}

View File

@ -19,11 +19,14 @@
package org.elasticsearch.ingest.common;
import org.elasticsearch.ingest.IngestDocument;
import org.elasticsearch.ingest.TestTemplateService;
import org.elasticsearch.test.ESTestCase;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.function.Function;
@ -33,11 +36,8 @@ public class DateIndexNameProcessorTests extends ESTestCase {
public void testJodaPattern() throws Exception {
Function<String, DateTime> function = DateFormat.Joda.getFunction("yyyy-MM-dd'T'HH:mm:ss.SSSZ", DateTimeZone.UTC, Locale.ROOT);
DateIndexNameProcessor processor = new DateIndexNameProcessor(
"_tag", "_field", Collections.singletonList(function), DateTimeZone.UTC,
"events-", "y", "yyyyMMdd"
);
DateIndexNameProcessor processor = createProcessor("_field", Collections.singletonList(function),
DateTimeZone.UTC, "events-", "y", "yyyyMMdd");
IngestDocument document = new IngestDocument("_index", "_type", "_id", null, null, null,
Collections.singletonMap("_field", "2016-04-25T12:24:20.101Z"));
processor.execute(document);
@ -46,7 +46,7 @@ public class DateIndexNameProcessorTests extends ESTestCase {
public void testTAI64N()throws Exception {
Function<String, DateTime> function = DateFormat.Tai64n.getFunction(null, DateTimeZone.UTC, null);
DateIndexNameProcessor dateProcessor = new DateIndexNameProcessor("_tag", "_field", Collections.singletonList(function),
DateIndexNameProcessor dateProcessor = createProcessor("_field", Collections.singletonList(function),
DateTimeZone.UTC, "events-", "m", "yyyyMMdd");
IngestDocument document = new IngestDocument("_index", "_type", "_id", null, null, null,
Collections.singletonMap("_field", (randomBoolean() ? "@" : "") + "4000000050d506482dbdf024"));
@ -56,7 +56,7 @@ public class DateIndexNameProcessorTests extends ESTestCase {
public void testUnixMs()throws Exception {
Function<String, DateTime> function = DateFormat.UnixMs.getFunction(null, DateTimeZone.UTC, null);
DateIndexNameProcessor dateProcessor = new DateIndexNameProcessor("_tag", "_field", Collections.singletonList(function),
DateIndexNameProcessor dateProcessor = createProcessor("_field", Collections.singletonList(function),
DateTimeZone.UTC, "events-", "m", "yyyyMMdd");
IngestDocument document = new IngestDocument("_index", "_type", "_id", null, null, null,
Collections.singletonMap("_field", "1000500"));
@ -71,7 +71,7 @@ public class DateIndexNameProcessorTests extends ESTestCase {
public void testUnix()throws Exception {
Function<String, DateTime> function = DateFormat.Unix.getFunction(null, DateTimeZone.UTC, null);
DateIndexNameProcessor dateProcessor = new DateIndexNameProcessor("_tag", "_field", Collections.singletonList(function),
DateIndexNameProcessor dateProcessor = createProcessor("_field", Collections.singletonList(function),
DateTimeZone.UTC, "events-", "m", "yyyyMMdd");
IngestDocument document = new IngestDocument("_index", "_type", "_id", null, null, null,
Collections.singletonMap("_field", "1000.5"));
@ -79,4 +79,33 @@ public class DateIndexNameProcessorTests extends ESTestCase {
assertThat(document.getSourceAndMetadata().get("_index"), equalTo("<events-{19700101||/m{yyyyMMdd|UTC}}>"));
}
public void testTemplatedFields() throws Exception {
String indexNamePrefix = randomAlphaOfLength(10);
String dateRounding = randomFrom("y", "M", "w", "d", "h", "m", "s");
String indexNameFormat = randomFrom("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyyMMdd", "MM/dd/yyyy");
String date = Integer.toString(randomInt());
Function<String, DateTime> dateTimeFunction = DateFormat.Unix.getFunction(null, DateTimeZone.UTC, null);
DateIndexNameProcessor dateProcessor = createProcessor("_field",
Collections.singletonList(dateTimeFunction), DateTimeZone.UTC, indexNamePrefix,
dateRounding, indexNameFormat);
IngestDocument document = new IngestDocument("_index", "_type", "_id", null, null, null,
Collections.singletonMap("_field", date));
dateProcessor.execute(document);
assertThat(document.getSourceAndMetadata().get("_index"),
equalTo("<"+indexNamePrefix+"{"+DateTimeFormat.forPattern(indexNameFormat)
.print(dateTimeFunction.apply(date))+"||/"+dateRounding+"{"+indexNameFormat+"|UTC}}>"));
}
private DateIndexNameProcessor createProcessor(String field, List<Function<String, DateTime>> dateFormats,
DateTimeZone timezone, String indexNamePrefix, String dateRounding,
String indexNameFormat) {
return new DateIndexNameProcessor(randomAlphaOfLength(10), field, dateFormats, timezone,
new TestTemplateService.MockTemplateScript.Factory(indexNamePrefix),
new TestTemplateService.MockTemplateScript.Factory(dateRounding),
new TestTemplateService.MockTemplateScript.Factory(indexNameFormat)
);
}
}

View File

@ -24,9 +24,10 @@ import org.elasticsearch.ingest.RandomDocumentPicks;
import org.elasticsearch.ingest.TestTemplateService;
import org.elasticsearch.script.TemplateScript;
import org.elasticsearch.test.ESTestCase;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@ -36,19 +37,21 @@ import java.util.Map;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.joda.time.DateTimeZone.UTC;
public class DateProcessorTests extends ESTestCase {
private TemplateScript.Factory templatize(Locale locale) {
return new TestTemplateService.MockTemplateScript.Factory(locale.getLanguage());
}
private TemplateScript.Factory templatize(DateTimeZone timezone) {
return new TestTemplateService.MockTemplateScript.Factory(timezone.getID());
private TemplateScript.Factory templatize(ZoneId timezone) {
// prevent writing "UTC" as string, as joda time does not parse it
String id = timezone.equals(ZoneOffset.UTC) ? "UTC" : timezone.getId();
return new TestTemplateService.MockTemplateScript.Factory(id);
}
public void testJodaPattern() {
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10),
templatize(DateTimeZone.forID("Europe/Amsterdam")), templatize(Locale.ENGLISH),
templatize(ZoneId.of("Europe/Amsterdam")), templatize(Locale.ENGLISH),
"date_as_string", Collections.singletonList("yyyy dd MM hh:mm:ss"), "date_as_date");
Map<String, Object> document = new HashMap<>();
document.put("date_as_string", "2010 12 06 11:05:15");
@ -63,7 +66,7 @@ public class DateProcessorTests extends ESTestCase {
matchFormats.add("dd/MM/yyyy");
matchFormats.add("dd-MM-yyyy");
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10),
templatize(DateTimeZone.forID("Europe/Amsterdam")), templatize(Locale.ENGLISH),
templatize(ZoneId.of("Europe/Amsterdam")), templatize(Locale.ENGLISH),
"date_as_string", matchFormats, "date_as_date");
Map<String, Object> document = new HashMap<>();
@ -98,7 +101,7 @@ public class DateProcessorTests extends ESTestCase {
public void testInvalidJodaPattern() {
try {
DateProcessor processor = new DateProcessor(randomAlphaOfLength(10),
templatize(UTC), templatize(randomLocale(random())),
templatize(ZoneOffset.UTC), templatize(randomLocale(random())),
"date_as_string", Collections.singletonList("invalid pattern"), "date_as_date");
Map<String, Object> document = new HashMap<>();
document.put("date_as_string", "2010");
@ -112,7 +115,7 @@ public class DateProcessorTests extends ESTestCase {
public void testJodaPatternLocale() {
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10),
templatize(DateTimeZone.forID("Europe/Amsterdam")), templatize(Locale.ITALIAN),
templatize(ZoneId.of("Europe/Amsterdam")), templatize(Locale.ITALIAN),
"date_as_string", Collections.singletonList("yyyy dd MMM"), "date_as_date");
Map<String, Object> document = new HashMap<>();
document.put("date_as_string", "2010 12 giugno");
@ -123,18 +126,18 @@ public class DateProcessorTests extends ESTestCase {
public void testJodaPatternDefaultYear() {
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10),
templatize(DateTimeZone.forID("Europe/Amsterdam")), templatize(Locale.ENGLISH),
templatize(ZoneId.of("Europe/Amsterdam")), templatize(Locale.ENGLISH),
"date_as_string", Collections.singletonList("dd/MM"), "date_as_date");
Map<String, Object> document = new HashMap<>();
document.put("date_as_string", "12/06");
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
dateProcessor.execute(ingestDocument);
assertThat(ingestDocument.getFieldValue("date_as_date", String.class),
equalTo(DateTime.now().getYear() + "-06-12T00:00:00.000+02:00"));
equalTo(ZonedDateTime.now().getYear() + "-06-12T00:00:00.000+02:00"));
}
public void testTAI64N() {
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), templatize(DateTimeZone.forOffsetHours(2)),
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), templatize(ZoneOffset.ofHours(2)),
templatize(randomLocale(random())),
"date_as_string", Collections.singletonList("TAI64N"), "date_as_date");
Map<String, Object> document = new HashMap<>();
@ -146,8 +149,8 @@ public class DateProcessorTests extends ESTestCase {
}
public void testUnixMs() {
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), templatize(UTC), templatize(randomLocale(random())),
"date_as_string", Collections.singletonList("UNIX_MS"), "date_as_date");
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), templatize(ZoneOffset.UTC),
templatize(randomLocale(random())), "date_as_string", Collections.singletonList("UNIX_MS"), "date_as_date");
Map<String, Object> document = new HashMap<>();
document.put("date_as_string", "1000500");
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
@ -162,7 +165,7 @@ public class DateProcessorTests extends ESTestCase {
}
public void testUnix() {
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), templatize(UTC),
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10), templatize(ZoneOffset.UTC),
templatize(randomLocale(random())),
"date_as_string", Collections.singletonList("UNIX"), "date_as_date");
Map<String, Object> document = new HashMap<>();
@ -186,7 +189,7 @@ public class DateProcessorTests extends ESTestCase {
public void testInvalidLocale() {
DateProcessor processor = new DateProcessor(randomAlphaOfLength(10),
templatize(UTC), new TestTemplateService.MockTemplateScript.Factory("invalid_locale"),
templatize(ZoneOffset.UTC), new TestTemplateService.MockTemplateScript.Factory("invalid_locale"),
"date_as_string", Collections.singletonList("yyyy"), "date_as_date");
Map<String, Object> document = new HashMap<>();
document.put("date_as_string", "2010");

View File

@ -21,6 +21,7 @@ package org.elasticsearch.ingest.common;
import org.elasticsearch.ingest.IngestDocument;
import org.elasticsearch.ingest.Processor;
import org.elasticsearch.ingest.TestTemplateService;
import org.elasticsearch.test.ESTestCase;
import java.util.Collections;
@ -86,7 +87,8 @@ public class DotExpanderProcessorTests extends ESTestCase {
// so because foo is no branch field but a value field the `foo.bar` field can't be expanded
// into [foo].[bar], so foo should be renamed first into `[foo].[bar]:
IngestDocument document = new IngestDocument(source, Collections.emptyMap());
Processor processor = new RenameProcessor("_tag", "foo", "foo.bar", false);
Processor processor = new RenameProcessor("_tag", new TestTemplateService.MockTemplateScript.Factory("foo"),
new TestTemplateService.MockTemplateScript.Factory("foo.bar"), false);
processor.execute(document);
processor = new DotExpanderProcessor("_tag", null, "foo.bar");
processor.execute(document);

View File

@ -129,7 +129,7 @@ public class GrokProcessorTests extends ESTestCase {
public void testMissingFieldWithIgnoreMissing() throws Exception {
String fieldName = "foo.bar";
IngestDocument originalIngestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
IngestDocument ingestDocument = new IngestDocument(originalIngestDocument);
GrokProcessor processor = new GrokProcessor(randomAlphaOfLength(10), Collections.singletonMap("ONE", "1"),
Collections.singletonList("%{ONE:one}"), fieldName, false, true, ThreadWatchdog.noop());
processor.execute(ingestDocument);

View File

@ -33,7 +33,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.ingest.IngestDocumentMatcher.assertIngestDocument;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
@ -55,7 +54,7 @@ public class JsonProcessorTests extends ESTestCase {
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
jsonProcessor.execute(ingestDocument);
Map<String, Object> jsonified = ingestDocument.getFieldValue(randomTargetField, Map.class);
assertIngestDocument(ingestDocument.getFieldValue(randomTargetField, Object.class), jsonified);
assertEquals(ingestDocument.getFieldValue(randomTargetField, Object.class), jsonified);
}
public void testInvalidValue() {
@ -161,13 +160,10 @@ public class JsonProcessorTests extends ESTestCase {
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
jsonProcessor.execute(ingestDocument);
Map<String, Object> expected = new HashMap<>();
expected.put("a", 1);
expected.put("b", 2);
expected.put("c", "see");
IngestDocument expectedIngestDocument = RandomDocumentPicks.randomIngestDocument(random(), expected);
assertIngestDocument(ingestDocument, expectedIngestDocument);
Map<String, Object> sourceAndMetadata = ingestDocument.getSourceAndMetadata();
assertEquals(1, sourceAndMetadata.get("a"));
assertEquals(2, sourceAndMetadata.get("b"));
assertEquals("see", sourceAndMetadata.get("c"));
}
public void testAddBoolToRoot() {

View File

@ -27,6 +27,7 @@ import org.elasticsearch.test.ESTestCase;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
@ -37,7 +38,7 @@ public class RemoveProcessorTests extends ESTestCase {
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random());
String field = RandomDocumentPicks.randomExistingFieldName(random(), ingestDocument);
Processor processor = new RemoveProcessor(randomAlphaOfLength(10),
Collections.singletonList(new TestTemplateService.MockTemplateScript.Factory(field)));
Collections.singletonList(new TestTemplateService.MockTemplateScript.Factory(field)), false);
processor.execute(ingestDocument);
assertThat(ingestDocument.hasField(field), equalTo(false));
}
@ -45,8 +46,10 @@ public class RemoveProcessorTests extends ESTestCase {
public void testRemoveNonExistingField() throws Exception {
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
String fieldName = RandomDocumentPicks.randomFieldName(random());
Processor processor = new RemoveProcessor(randomAlphaOfLength(10),
Collections.singletonList(new TestTemplateService.MockTemplateScript.Factory(fieldName)));
Map<String, Object> config = new HashMap<>();
config.put("field", fieldName);
String processorTag = randomAlphaOfLength(10);
Processor processor = new RemoveProcessor.Factory(TestTemplateService.instance()).create(null, processorTag, config);
try {
processor.execute(ingestDocument);
fail("remove field should have failed");
@ -54,4 +57,15 @@ public class RemoveProcessorTests extends ESTestCase {
assertThat(e.getMessage(), containsString("not present as part of path [" + fieldName + "]"));
}
}
public void testIgnoreMissing() throws Exception {
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
String fieldName = RandomDocumentPicks.randomFieldName(random());
Map<String, Object> config = new HashMap<>();
config.put("field", fieldName);
config.put("ignore_missing", true);
String processorTag = randomAlphaOfLength(10);
Processor processor = new RemoveProcessor.Factory(TestTemplateService.instance()).create(null, processorTag, config);
processor.execute(ingestDocument);
}
}

View File

@ -20,8 +20,11 @@
package org.elasticsearch.ingest.common;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.ingest.TestTemplateService;
import org.elasticsearch.test.ESTestCase;
import org.junit.Before;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -29,21 +32,26 @@ import static org.hamcrest.CoreMatchers.equalTo;
public class RenameProcessorFactoryTests extends ESTestCase {
private RenameProcessor.Factory factory;
@Before
public void init() {
factory = new RenameProcessor.Factory(TestTemplateService.instance());
}
public void testCreate() throws Exception {
RenameProcessor.Factory factory = new RenameProcessor.Factory();
Map<String, Object> config = new HashMap<>();
config.put("field", "old_field");
config.put("target_field", "new_field");
String processorTag = randomAlphaOfLength(10);
RenameProcessor renameProcessor = factory.create(null, processorTag, config);
assertThat(renameProcessor.getTag(), equalTo(processorTag));
assertThat(renameProcessor.getField(), equalTo("old_field"));
assertThat(renameProcessor.getTargetField(), equalTo("new_field"));
assertThat(renameProcessor.getField().newInstance(Collections.emptyMap()).execute(), equalTo("old_field"));
assertThat(renameProcessor.getTargetField().newInstance(Collections.emptyMap()).execute(), equalTo("new_field"));
assertThat(renameProcessor.isIgnoreMissing(), equalTo(false));
}
public void testCreateWithIgnoreMissing() throws Exception {
RenameProcessor.Factory factory = new RenameProcessor.Factory();
Map<String, Object> config = new HashMap<>();
config.put("field", "old_field");
config.put("target_field", "new_field");
@ -51,13 +59,12 @@ public class RenameProcessorFactoryTests extends ESTestCase {
String processorTag = randomAlphaOfLength(10);
RenameProcessor renameProcessor = factory.create(null, processorTag, config);
assertThat(renameProcessor.getTag(), equalTo(processorTag));
assertThat(renameProcessor.getField(), equalTo("old_field"));
assertThat(renameProcessor.getTargetField(), equalTo("new_field"));
assertThat(renameProcessor.getField().newInstance(Collections.emptyMap()).execute(), equalTo("old_field"));
assertThat(renameProcessor.getTargetField().newInstance(Collections.emptyMap()).execute(), equalTo("new_field"));
assertThat(renameProcessor.isIgnoreMissing(), equalTo(true));
}
public void testCreateNoFieldPresent() throws Exception {
RenameProcessor.Factory factory = new RenameProcessor.Factory();
Map<String, Object> config = new HashMap<>();
config.put("target_field", "new_field");
try {
@ -69,7 +76,6 @@ public class RenameProcessorFactoryTests extends ESTestCase {
}
public void testCreateNoToPresent() throws Exception {
RenameProcessor.Factory factory = new RenameProcessor.Factory();
Map<String, Object> config = new HashMap<>();
config.put("field", "old_field");
try {

View File

@ -22,6 +22,7 @@ package org.elasticsearch.ingest.common;
import org.elasticsearch.ingest.IngestDocument;
import org.elasticsearch.ingest.Processor;
import org.elasticsearch.ingest.RandomDocumentPicks;
import org.elasticsearch.ingest.TestTemplateService;
import org.elasticsearch.test.ESTestCase;
import java.util.ArrayList;
@ -45,7 +46,7 @@ public class RenameProcessorTests extends ESTestCase {
do {
newFieldName = RandomDocumentPicks.randomFieldName(random());
} while (RandomDocumentPicks.canAddField(newFieldName, ingestDocument) == false || newFieldName.equals(fieldName));
Processor processor = new RenameProcessor(randomAlphaOfLength(10), fieldName, newFieldName, false);
Processor processor = createRenameProcessor(fieldName, newFieldName, false);
processor.execute(ingestDocument);
assertThat(ingestDocument.getFieldValue(newFieldName, Object.class), equalTo(fieldValue));
}
@ -63,7 +64,7 @@ public class RenameProcessorTests extends ESTestCase {
document.put("one", one);
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
Processor processor = new RenameProcessor(randomAlphaOfLength(10), "list.0", "item", false);
Processor processor = createRenameProcessor("list.0", "item", false);
processor.execute(ingestDocument);
Object actualObject = ingestDocument.getSourceAndMetadata().get("list");
assertThat(actualObject, instanceOf(List.class));
@ -76,7 +77,7 @@ public class RenameProcessorTests extends ESTestCase {
assertThat(actualObject, instanceOf(String.class));
assertThat(actualObject, equalTo("item1"));
processor = new RenameProcessor(randomAlphaOfLength(10), "list.0", "list.3", false);
processor = createRenameProcessor("list.0", "list.3", false);
try {
processor.execute(ingestDocument);
fail("processor execute should have failed");
@ -91,7 +92,7 @@ public class RenameProcessorTests extends ESTestCase {
public void testRenameNonExistingField() throws Exception {
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
String fieldName = RandomDocumentPicks.randomFieldName(random());
Processor processor = new RenameProcessor(randomAlphaOfLength(10), fieldName,
Processor processor = createRenameProcessor(fieldName,
RandomDocumentPicks.randomFieldName(random()), false);
try {
processor.execute(ingestDocument);
@ -105,7 +106,7 @@ public class RenameProcessorTests extends ESTestCase {
IngestDocument originalIngestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
IngestDocument ingestDocument = new IngestDocument(originalIngestDocument);
String fieldName = RandomDocumentPicks.randomFieldName(random());
Processor processor = new RenameProcessor(randomAlphaOfLength(10), fieldName,
Processor processor = createRenameProcessor(fieldName,
RandomDocumentPicks.randomFieldName(random()), true);
processor.execute(ingestDocument);
assertIngestDocument(originalIngestDocument, ingestDocument);
@ -114,7 +115,7 @@ public class RenameProcessorTests extends ESTestCase {
public void testRenameNewFieldAlreadyExists() throws Exception {
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random());
String fieldName = RandomDocumentPicks.randomExistingFieldName(random(), ingestDocument);
Processor processor = new RenameProcessor(randomAlphaOfLength(10), RandomDocumentPicks.randomExistingFieldName(
Processor processor = createRenameProcessor(RandomDocumentPicks.randomExistingFieldName(
random(), ingestDocument), fieldName, false);
try {
processor.execute(ingestDocument);
@ -129,7 +130,7 @@ public class RenameProcessorTests extends ESTestCase {
String fieldName = RandomDocumentPicks.randomFieldName(random());
ingestDocument.setFieldValue(fieldName, null);
String newFieldName = randomValueOtherThanMany(ingestDocument::hasField, () -> RandomDocumentPicks.randomFieldName(random()));
Processor processor = new RenameProcessor(randomAlphaOfLength(10), fieldName, newFieldName, false);
Processor processor = createRenameProcessor(fieldName, newFieldName, false);
processor.execute(ingestDocument);
assertThat(ingestDocument.hasField(fieldName), equalTo(false));
assertThat(ingestDocument.hasField(newFieldName), equalTo(true));
@ -149,7 +150,7 @@ public class RenameProcessorTests extends ESTestCase {
source.put("list", Collections.singletonList("item"));
IngestDocument ingestDocument = new IngestDocument(source, Collections.emptyMap());
Processor processor = new RenameProcessor(randomAlphaOfLength(10), "list", "new_field", false);
Processor processor = createRenameProcessor("list", "new_field", false);
try {
processor.execute(ingestDocument);
fail("processor execute should have failed");
@ -173,7 +174,7 @@ public class RenameProcessorTests extends ESTestCase {
source.put("list", Collections.singletonList("item"));
IngestDocument ingestDocument = new IngestDocument(source, Collections.emptyMap());
Processor processor = new RenameProcessor(randomAlphaOfLength(10), "list", "new_field", false);
Processor processor = createRenameProcessor("list", "new_field", false);
try {
processor.execute(ingestDocument);
fail("processor execute should have failed");
@ -188,12 +189,12 @@ public class RenameProcessorTests extends ESTestCase {
Map<String, Object> source = new HashMap<>();
source.put("foo", "bar");
IngestDocument ingestDocument = new IngestDocument(source, Collections.emptyMap());
Processor processor1 = new RenameProcessor(randomAlphaOfLength(10), "foo", "foo.bar", false);
Processor processor1 = createRenameProcessor("foo", "foo.bar", false);
processor1.execute(ingestDocument);
assertThat(ingestDocument.getFieldValue("foo", Map.class), equalTo(Collections.singletonMap("bar", "bar")));
assertThat(ingestDocument.getFieldValue("foo.bar", String.class), equalTo("bar"));
Processor processor2 = new RenameProcessor(randomAlphaOfLength(10), "foo.bar", "foo.bar.baz", false);
Processor processor2 = createRenameProcessor( "foo.bar", "foo.bar.baz", false);
processor2.execute(ingestDocument);
assertThat(ingestDocument.getFieldValue("foo", Map.class), equalTo(Collections.singletonMap("bar",
Collections.singletonMap("baz", "bar"))));
@ -201,9 +202,13 @@ public class RenameProcessorTests extends ESTestCase {
assertThat(ingestDocument.getFieldValue("foo.bar.baz", String.class), equalTo("bar"));
// for fun lets try to restore it (which don't allow today)
Processor processor3 = new RenameProcessor(randomAlphaOfLength(10), "foo.bar.baz", "foo", false);
Processor processor3 = createRenameProcessor("foo.bar.baz", "foo", false);
Exception e = expectThrows(IllegalArgumentException.class, () -> processor3.execute(ingestDocument));
assertThat(e.getMessage(), equalTo("field [foo] already exists"));
}
private RenameProcessor createRenameProcessor(String field, String targetField, boolean ignoreMissing) {
return new RenameProcessor(randomAlphaOfLength(10), new TestTemplateService.MockTemplateScript.Factory(field),
new TestTemplateService.MockTemplateScript.Factory(targetField), ignoreMissing);
}
}

View File

@ -17,7 +17,7 @@
* under the License.
*/
import org.apache.tools.ant.types.Path
esplugin {
description 'An easy, safe and fast scripting language for Elasticsearch'

View File

@ -24,13 +24,14 @@ import java.util.List;
import java.util.Objects;
/**
* Whitelist contains data structures designed to be used to generate a white-list of Java classes,
* Whitelist contains data structures designed to be used to generate a whitelist of Java classes,
* constructors, methods, and fields that can be used within a Painless script at both compile-time
* and run-time.
*
* A white-list consists of several pieces with {@link Struct}s as the top level. Each {@link Struct}
* will contain zero-to-many {@link Constructor}s, {@link Method}s, and {@link Field}s which are what
* will be available with a Painless script. See each individual white-list object for more detail.
* A whitelist consists of several pieces with {@link WhitelistClass}s as the top level. Each
* {@link WhitelistClass} will contain zero-to-many {@link WhitelistConstructor}s, {@link WhitelistMethod}s, and
* {@link WhitelistField}s which are what will be available with a Painless script. See each individual
* whitelist object for more detail.
*/
public final class Whitelist {
@ -54,166 +55,14 @@ public final class Whitelist {
public static final List<Whitelist> BASE_WHITELISTS =
Collections.singletonList(WhitelistLoader.loadFromResourceFiles(Whitelist.class, BASE_WHITELIST_FILES));
/**
* Struct represents the equivalent of a Java class in Painless complete with super classes,
* constructors, methods, and fields. In Painless a class is known as a struct primarily to avoid
* naming conflicts internally. There must be a one-to-one mapping of struct names to Java classes.
* Though, since multiple white-lists may be combined into a single white-list for a specific
* {@link org.elasticsearch.script.ScriptContext}, as long as multiple structs representing the same
* Java class have the same Painless type name and have legal constructor/method overloading they
* can be merged together.
*
* Structs in Painless allow for arity overloading for constructors and methods. Arity overloading
* means that multiple constructors are allowed for a single struct as long as they have a different
* number of parameter types, and multiples methods with the same name are allowed for a single struct
* as long as they have the same return type and a different number of parameter types.
*
* Structs will automatically extend other white-listed structs if the Java class they represent is a
* subclass of other structs including Java interfaces.
*/
public static final class Struct {
/** Information about where this struct was white-listed from. Can be used for error messages. */
public final String origin;
/** The Java class name this struct represents. */
public final String javaClassName;
/**
* Allow the Java class name to only be specified as the fully-qualified name.
*/
public final boolean onlyFQNJavaClassName;
/** The {@link List} of white-listed ({@link Constructor}s) available to this struct. */
public final List<Constructor> whitelistConstructors;
/** The {@link List} of white-listed ({@link Method}s) available to this struct. */
public final List<Method> whitelistMethods;
/** The {@link List} of white-listed ({@link Field}s) available to this struct. */
public final List<Field> whitelistFields;
/** Standard constructor. All values must be not {@code null}. */
public Struct(String origin, String javaClassName, boolean onlyFQNJavaClassName,
List<Constructor> whitelistConstructors, List<Method> whitelistMethods, List<Field> whitelistFields) {
this.origin = Objects.requireNonNull(origin);
this.javaClassName = Objects.requireNonNull(javaClassName);
this.onlyFQNJavaClassName = onlyFQNJavaClassName;
this.whitelistConstructors = Collections.unmodifiableList(Objects.requireNonNull(whitelistConstructors));
this.whitelistMethods = Collections.unmodifiableList(Objects.requireNonNull(whitelistMethods));
this.whitelistFields = Collections.unmodifiableList(Objects.requireNonNull(whitelistFields));
}
}
/**
* Constructor represents the equivalent of a Java constructor available as a white-listed struct
* constructor within Painless. Constructors for Painless structs may be accessed exactly as
* constructors for Java classes are using the 'new' keyword. Painless structs may have multiple
* constructors as long as they comply with arity overloading described for {@link Struct}.
*/
public static final class Constructor {
/** Information about where this constructor was white-listed from. Can be used for error messages. */
public final String origin;
/**
* A {@link List} of {@link String}s that are the Painless type names for the parameters of the
* constructor which can be used to look up the Java constructor through reflection.
*/
public final List<String> painlessParameterTypeNames;
/** Standard constructor. All values must be not {@code null}. */
public Constructor(String origin, List<String> painlessParameterTypeNames) {
this.origin = Objects.requireNonNull(origin);
this.painlessParameterTypeNames = Collections.unmodifiableList(Objects.requireNonNull(painlessParameterTypeNames));
}
}
/**
* Method represents the equivalent of a Java method available as a white-listed struct method
* within Painless. Methods for Painless structs may be accessed exactly as methods for Java classes
* are using the '.' operator on an existing struct variable/field. Painless structs may have multiple
* methods with the same name as long as they comply with arity overloading described for {@link Method}.
*
* Structs may also have additional methods that are not part of the Java class the struct represents -
* these are known as augmented methods. An augmented method can be added to a struct as a part of any
* Java class as long as the method is static and the first parameter of the method is the Java class
* represented by the struct. Note that the augmented method's parent Java class does not need to be
* white-listed.
*/
public static class Method {
/** Information about where this method was white-listed from. Can be used for error messages. */
public final String origin;
/**
* The Java class name for the owner of an augmented method. If the method is not augmented
* this should be {@code null}.
*/
public final String javaAugmentedClassName;
/** The Java method name used to look up the Java method through reflection. */
public final String javaMethodName;
/**
* The Painless type name for the return type of the method which can be used to look up the Java
* method through reflection.
*/
public final String painlessReturnTypeName;
/**
* A {@link List} of {@link String}s that are the Painless type names for the parameters of the
* method which can be used to look up the Java method through reflection.
*/
public final List<String> painlessParameterTypeNames;
/**
* Standard constructor. All values must be not {@code null} with the exception of jAugmentedClass;
* jAugmentedClass will be {@code null} unless the method is augmented as described in the class documentation.
*/
public Method(String origin, String javaAugmentedClassName, String javaMethodName,
String painlessReturnTypeName, List<String> painlessParameterTypeNames) {
this.origin = Objects.requireNonNull(origin);
this.javaAugmentedClassName = javaAugmentedClassName;
this.javaMethodName = javaMethodName;
this.painlessReturnTypeName = Objects.requireNonNull(painlessReturnTypeName);
this.painlessParameterTypeNames = Collections.unmodifiableList(Objects.requireNonNull(painlessParameterTypeNames));
}
}
/**
* Field represents the equivalent of a Java field available as a white-listed struct field
* within Painless. Fields for Painless structs may be accessed exactly as fields for Java classes
* are using the '.' operator on an existing struct variable/field.
*/
public static class Field {
/** Information about where this method was white-listed from. Can be used for error messages. */
public final String origin;
/** The Java field name used to look up the Java field through reflection. */
public final String javaFieldName;
/** The Painless type name for the field which can be used to look up the Java field through reflection. */
public final String painlessFieldTypeName;
/** Standard constructor. All values must be not {@code null}. */
public Field(String origin, String javaFieldName, String painlessFieldTypeName) {
this.origin = Objects.requireNonNull(origin);
this.javaFieldName = Objects.requireNonNull(javaFieldName);
this.painlessFieldTypeName = Objects.requireNonNull(painlessFieldTypeName);
}
}
/** The {@link ClassLoader} used to look up the white-listed Java classes, constructors, methods, and fields. */
/** The {@link ClassLoader} used to look up the whitelisted Java classes, constructors, methods, and fields. */
public final ClassLoader javaClassLoader;
/** The {@link List} of all the white-listed Painless structs. */
public final List<Struct> whitelistStructs;
/** The {@link List} of all the whitelisted Painless classes. */
public final List<WhitelistClass> whitelistStructs;
/** Standard constructor. All values must be not {@code null}. */
public Whitelist(ClassLoader javaClassLoader, List<Struct> whitelistStructs) {
public Whitelist(ClassLoader javaClassLoader, List<WhitelistClass> whitelistStructs) {
this.javaClassLoader = Objects.requireNonNull(javaClassLoader);
this.whitelistStructs = Collections.unmodifiableList(Objects.requireNonNull(whitelistStructs));
}

View File

@ -0,0 +1,76 @@
/*
* 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;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* Class represents the equivalent of a Java class in Painless complete with super classes,
* constructors, methods, and fields. There must be a one-to-one mapping of class names to Java
* classes. Though, since multiple whitelists may be combined into a single whitelist for a
* specific context, as long as multiple classes representing the same Java class have the same
* class name and have legal constructor/method overloading they can be merged together.
*
* Classes in Painless allow for arity overloading for constructors and methods. Arity overloading
* means that multiple constructors are allowed for a single class as long as they have a different
* number of parameters, and multiples methods with the same name are allowed for a single class
* as long as they have the same return type and a different number of parameters.
*
* Classes will automatically extend other whitelisted classes if the Java class they represent is a
* subclass of other classes including Java interfaces.
*/
public final class WhitelistClass {
/** Information about where this class was white-listed from. Can be used for error messages. */
public final String origin;
/** 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 onlyFQNJavaClassName;
/** The {@link List} of whitelisted ({@link WhitelistConstructor}s) available to this class. */
public final List<WhitelistConstructor> whitelistConstructors;
/** The {@link List} of whitelisted ({@link WhitelistMethod}s) available to this class. */
public final List<WhitelistMethod> whitelistMethods;
/** The {@link List} of whitelisted ({@link WhitelistField}s) available to this class. */
public final List<WhitelistField> whitelistFields;
/** Standard constructor. All values must be not {@code null}. */
public WhitelistClass(String origin, String javaClassName, boolean onlyFQNJavaClassName,
List<WhitelistConstructor> whitelistConstructors,
List<WhitelistMethod> whitelistMethods,
List<WhitelistField> whitelistFields) {
this.origin = Objects.requireNonNull(origin);
this.javaClassName = Objects.requireNonNull(javaClassName);
this.onlyFQNJavaClassName = onlyFQNJavaClassName;
this.whitelistConstructors = Collections.unmodifiableList(Objects.requireNonNull(whitelistConstructors));
this.whitelistMethods = Collections.unmodifiableList(Objects.requireNonNull(whitelistMethods));
this.whitelistFields = Collections.unmodifiableList(Objects.requireNonNull(whitelistFields));
}
}

View File

@ -0,0 +1,48 @@
/*
* 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;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* Constructor represents the equivalent of a Java constructor available as a whitelisted class
* constructor within Painless. Constructors for Painless classes may be accessed exactly as
* constructors for Java classes are using the 'new' keyword. Painless classes may have multiple
* constructors as long as they comply with arity overloading described for {@link WhitelistClass}.
*/
public final class WhitelistConstructor {
/** Information about where this constructor was whitelisted from. Can be used for error messages. */
public final String origin;
/**
* A {@link List} of {@link String}s that are the Painless type names for the parameters of the
* constructor which can be used to look up the Java constructor through reflection.
*/
public final List<String> painlessParameterTypeNames;
/** Standard constructor. All values must be not {@code null}. */
public WhitelistConstructor(String origin, List<String> painlessParameterTypeNames) {
this.origin = Objects.requireNonNull(origin);
this.painlessParameterTypeNames = Collections.unmodifiableList(Objects.requireNonNull(painlessParameterTypeNames));
}
}

View File

@ -0,0 +1,46 @@
/*
* 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;
import java.util.Objects;
/**
* Field represents the equivalent of a Java field available as a whitelisted class field
* within Painless. Fields for Painless classes may be accessed exactly as fields for Java classes
* are using the '.' operator on an existing class variable/field.
*/
public class WhitelistField {
/** Information about where this method was whitelisted from. Can be used for error messages. */
public final String origin;
/** The Java field name used to look up the Java field through reflection. */
public final String javaFieldName;
/** The Painless type name for the field which can be used to look up the Java field through reflection. */
public final String painlessFieldTypeName;
/** Standard constructor. All values must be not {@code null}. */
public WhitelistField(String origin, String javaFieldName, String painlessFieldTypeName) {
this.origin = Objects.requireNonNull(origin);
this.javaFieldName = Objects.requireNonNull(javaFieldName);
this.painlessFieldTypeName = Objects.requireNonNull(painlessFieldTypeName);
}
}

View File

@ -39,25 +39,25 @@ public final class WhitelistLoader {
* {@link String}s with a single {@link Class} to be be used to load the resources where each {@link String}
* is the path of a single text file. The {@link Class}'s {@link ClassLoader} will be used to lookup the Java
* reflection objects for each individual {@link Class}, {@link Constructor}, {@link Method}, and {@link Field}
* specified as part of the white-list in the text file.
* specified as part of the whitelist in the text file.
*
* A single pass is made through each file to collect all the information about each struct, constructor, method,
* and field. Most validation will be done at a later point after all white-lists have been gathered and their
* A single pass is made through each file to collect all the information about each class, constructor, method,
* and field. Most validation will be done at a later point after all whitelists have been gathered and their
* merging takes place.
*
* A painless type name is one of the following:
* <ul>
* <li> def - The Painless dynamic type which is automatically included without a need to be
* white-listed. </li>
* <li> fully-qualified Java type name - Any white-listed Java class will have the equivalent name as
* whitelisted. </li>
* <li> fully-qualified Java type name - Any whitelisted Java class will have the equivalent name as
* 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 'only_fqn' token during Painless struct parsing
* short type Java name may be excluded by using the 'only_fqn' token during Painless class parsing
* as described later. </li>
* </ul>
*
* The following can be parsed from each white-list text file:
* The following can be parsed from each whitelist text file:
* <ul>
* <li> Blank lines will be ignored by the parser. </li>
* <li> Comments may be created starting with a pound '#' symbol and end with a newline. These will
@ -71,19 +71,19 @@ public final class WhitelistLoader {
* <ul>
* <li> A constructor may be specified starting with an opening parenthesis, followed by a
* comma-delimited list of Painless type names corresponding to the type/class names for
* the equivalent Java parameter types (these must be white-listed as well), a closing
* the equivalent Java parameter types (these must be whitelisted as well), a closing
* parenthesis, and a newline. </li>
* <li> A method may be specified starting with a Painless type name for the return type,
* followed by the Java name of the method (which will also be the Painless name for the
* method), an opening parenthesis, a comma-delimited list of Painless type names
* corresponding to the type/class names for the equivalent Java parameter types
* (these must be white-listed as well), a closing parenthesis, and a newline. </li>
* (these must be whitelisted as well), a closing parenthesis, and a newline. </li>
* <li> An augmented method may be specified starting with a Painless type name for the return
* type, followed by the fully qualified Java name of the class the augmented method is
* part of (this class does not need to be white-listed), the Java name of the method
* part of (this class does not need to be whitelisted), the Java name of the method
* (which will also be the Painless name for the method), an opening parenthesis, a
* comma-delimited list of Painless type names corresponding to the type/class names
* for the equivalent Java parameter types (these must be white-listed as well), a closing
* for the equivalent Java parameter types (these must be whitelisted as well), a closing
* parenthesis, and a newline. </li>
* <li>A field may be specified starting with a Painless type name for the equivalent Java type
* of the field, followed by the Java name of the field (which all be the Painless name
@ -99,7 +99,7 @@ public final class WhitelistLoader {
* fully-qualified Java class name. Method argument types, method return types, and field types
* must be specified with Painless type names (def, fully-qualified, or short) as described earlier.
*
* The following example is used to create a single white-list text file:
* The following example is used to create a single whitelist text file:
*
* {@code
* # primitive types
@ -132,10 +132,10 @@ public final class WhitelistLoader {
* }
*/
public static Whitelist loadFromResourceFiles(Class<?> resource, String... filepaths) {
List<Whitelist.Struct> whitelistStructs = new ArrayList<>();
List<WhitelistClass> whitelistStructs = new ArrayList<>();
// Execute a single pass through the white-list text files. This will gather all the
// constructors, methods, augmented methods, and fields for each white-listed struct.
// Execute a single pass through the whitelist text files. This will gather all the
// constructors, methods, augmented methods, and fields for each whitelisted class.
for (String filepath : filepaths) {
String line;
int number = -1;
@ -146,9 +146,9 @@ public final class WhitelistLoader {
String whitelistStructOrigin = null;
String javaClassName = null;
boolean onlyFQNJavaClassName = false;
List<Whitelist.Constructor> whitelistConstructors = null;
List<Whitelist.Method> whitelistMethods = null;
List<Whitelist.Field> whitelistFields = null;
List<WhitelistConstructor> whitelistConstructors = null;
List<WhitelistMethod> whitelistMethods = null;
List<WhitelistField> whitelistFields = null;
while ((line = reader.readLine()) != null) {
number = reader.getLineNumber();
@ -159,13 +159,13 @@ public final class WhitelistLoader {
continue;
}
// Handle a new struct by resetting all the variables necessary to construct a new Whitelist.Struct for the white-list.
// Handle a new class by resetting all the variables necessary to construct a new WhitelistClass for the whitelist.
// Expects the following format: 'class' ID 'only_fqn'? '{' '\n'
if (line.startsWith("class ")) {
// Ensure the final token of the line is '{'.
if (line.endsWith("{") == false) {
throw new IllegalArgumentException(
"invalid struct definition: failed to parse class opening bracket [" + line + "]");
"invalid class definition: failed to parse class opening bracket [" + line + "]");
}
// Parse the Java class name.
@ -175,29 +175,29 @@ public final class WhitelistLoader {
if (tokens.length == 2 && "only_fqn".equals(tokens[1])) {
onlyFQNJavaClassName = true;
} else if (tokens.length != 1) {
throw new IllegalArgumentException("invalid struct definition: failed to parse class name [" + line + "]");
throw new IllegalArgumentException("invalid class definition: failed to parse class name [" + line + "]");
}
whitelistStructOrigin = "[" + filepath + "]:[" + number + "]";
javaClassName = tokens[0];
// Reset all the constructors, methods, and fields to support a new struct.
// Reset all the constructors, methods, and fields to support a new class.
whitelistConstructors = new ArrayList<>();
whitelistMethods = new ArrayList<>();
whitelistFields = new ArrayList<>();
// Handle the end of a struct, by creating a new Whitelist.Struct with all the previously gathered
// constructors, methods, augmented methods, and fields, and adding it to the list of white-listed structs.
// Handle the end of a class, by creating a new WhitelistClass with all the previously gathered
// constructors, methods, augmented methods, and fields, and adding it to the list of whitelisted classes.
// Expects the following format: '}' '\n'
} else if (line.equals("}")) {
if (javaClassName == null) {
throw new IllegalArgumentException("invalid struct definition: extraneous closing bracket");
throw new IllegalArgumentException("invalid class definition: extraneous closing bracket");
}
whitelistStructs.add(new Whitelist.Struct(whitelistStructOrigin, javaClassName, onlyFQNJavaClassName,
whitelistStructs.add(new WhitelistClass(whitelistStructOrigin, javaClassName, onlyFQNJavaClassName,
whitelistConstructors, whitelistMethods, whitelistFields));
// Set all the variables to null to ensure a new struct definition is found before other parsable values.
// Set all the variables to null to ensure a new class definition is found before other parsable values.
whitelistStructOrigin = null;
javaClassName = null;
onlyFQNJavaClassName = false;
@ -210,7 +210,7 @@ public final class WhitelistLoader {
// Mark the origin of this parsable object.
String origin = "[" + filepath + "]:[" + number + "]";
// Ensure we have a defined struct before adding any constructors, methods, augmented methods, or fields.
// Ensure we have a defined class before adding any constructors, methods, augmented methods, or fields.
if (javaClassName == null) {
throw new IllegalArgumentException("invalid object definition: expected a class name [" + line + "]");
}
@ -232,7 +232,7 @@ public final class WhitelistLoader {
tokens = new String[0];
}
whitelistConstructors.add(new Whitelist.Constructor(origin, Arrays.asList(tokens)));
whitelistConstructors.add(new WhitelistConstructor(origin, Arrays.asList(tokens)));
// Handle the case for a method or augmented method definition.
// Expects the following format: ID ID? ID '(' ( ID ( ',' ID )* )? ')' '\n'
@ -271,7 +271,7 @@ public final class WhitelistLoader {
tokens = new String[0];
}
whitelistMethods.add(new Whitelist.Method(origin, javaAugmentedClassName, javaMethodName,
whitelistMethods.add(new WhitelistMethod(origin, javaAugmentedClassName, javaMethodName,
painlessReturnTypeName, Arrays.asList(tokens)));
// Handle the case for a field definition.
@ -285,14 +285,14 @@ public final class WhitelistLoader {
throw new IllegalArgumentException("invalid field definition: unexpected format [" + line + "]");
}
whitelistFields.add(new Whitelist.Field(origin, tokens[1], tokens[0]));
whitelistFields.add(new WhitelistField(origin, tokens[1], tokens[0]));
}
}
}
// Ensure all structs end with a '}' token before the end of the file.
// Ensure all classes end with a '}' token before the end of the file.
if (javaClassName != null) {
throw new IllegalArgumentException("invalid struct definition: expected closing bracket");
throw new IllegalArgumentException("invalid class definition: expected closing bracket");
}
} catch (Exception exception) {
throw new RuntimeException("error in [" + filepath + "] at line [" + number + "]", exception);

View File

@ -0,0 +1,76 @@
/*
* 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;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* Method represents the equivalent of a Java method available as a whitelisted class method
* within Painless. Methods for Painless classes may be accessed exactly as methods for Java classes
* are using the '.' operator on an existing class variable/field. Painless classes may have multiple
* methods with the same name as long as they comply with arity overloading described for {@link WhitelistMethod}.
*
* Classes may also have additional methods that are not part of the Java class the class represents -
* these are known as augmented methods. An augmented method can be added to a class as a part of any
* Java class as long as the method is static and the first parameter of the method is the Java class
* represented by the class. Note that the augmented method's parent Java class does not need to be
* whitelisted.
*/
public class WhitelistMethod {
/** Information about where this method was whitelisted from. Can be used for error messages. */
public final String origin;
/**
* The Java class name for the owner of an augmented method. If the method is not augmented
* this should be {@code null}.
*/
public final String javaAugmentedClassName;
/** The Java method name used to look up the Java method through reflection. */
public final String javaMethodName;
/**
* The Painless type name for the return type of the method which can be used to look up the Java
* method through reflection.
*/
public final String painlessReturnTypeName;
/**
* A {@link List} of {@link String}s that are the Painless type names for the parameters of the
* method which can be used to look up the Java method through reflection.
*/
public final List<String> painlessParameterTypeNames;
/**
* Standard constructor. All values must be not {@code null} with the exception of jAugmentedClass;
* jAugmentedClass will be {@code null} unless the method is augmented as described in the class documentation.
*/
public WhitelistMethod(String origin, String javaAugmentedClassName, String javaMethodName,
String painlessReturnTypeName, List<String> painlessParameterTypeNames) {
this.origin = Objects.requireNonNull(origin);
this.javaAugmentedClassName = javaAugmentedClassName;
this.javaMethodName = javaMethodName;
this.painlessReturnTypeName = Objects.requireNonNull(painlessReturnTypeName);
this.painlessParameterTypeNames = Collections.unmodifiableList(Objects.requireNonNull(painlessParameterTypeNames));
}
}

View File

@ -19,8 +19,9 @@
package org.elasticsearch.painless;
import org.elasticsearch.painless.Definition.Cast;
import org.elasticsearch.painless.Definition.def;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.lookup.PainlessCast;
import org.elasticsearch.painless.lookup.PainlessLookup.def;
import java.util.Objects;
@ -30,7 +31,7 @@ import java.util.Objects;
*/
public final class AnalyzerCaster {
public static Cast getLegalCast(Location location, Class<?> actual, Class<?> expected, boolean explicit, boolean internal) {
public static PainlessCast getLegalCast(Location location, Class<?> actual, Class<?> expected, boolean explicit, boolean internal) {
Objects.requireNonNull(actual);
Objects.requireNonNull(expected);
@ -40,421 +41,421 @@ public final class AnalyzerCaster {
if (actual == def.class) {
if (expected == boolean.class) {
return Cast.unboxTo(def.class, Boolean.class, explicit, boolean.class);
return PainlessCast.unboxTo(def.class, Boolean.class, explicit, boolean.class);
} else if (expected == byte.class) {
return Cast.unboxTo(def.class, Byte.class, explicit, byte.class);
return PainlessCast.unboxTo(def.class, Byte.class, explicit, byte.class);
} else if (expected == short.class) {
return Cast.unboxTo(def.class, Short.class, explicit, short.class);
return PainlessCast.unboxTo(def.class, Short.class, explicit, short.class);
} else if (expected == char.class) {
return Cast.unboxTo(def.class, Character.class, explicit, char.class);
return PainlessCast.unboxTo(def.class, Character.class, explicit, char.class);
} else if (expected == int.class) {
return Cast.unboxTo(def.class, Integer.class, explicit, int.class);
return PainlessCast.unboxTo(def.class, Integer.class, explicit, int.class);
} else if (expected == long.class) {
return Cast.unboxTo(def.class, Long.class, explicit, long.class);
return PainlessCast.unboxTo(def.class, Long.class, explicit, long.class);
} else if (expected == float.class) {
return Cast.unboxTo(def.class, Float.class, explicit, float.class);
return PainlessCast.unboxTo(def.class, Float.class, explicit, float.class);
} else if (expected == double.class) {
return Cast.unboxTo(def.class, Double.class, explicit, double.class);
return PainlessCast.unboxTo(def.class, Double.class, explicit, double.class);
}
} else if (actual == Object.class) {
if (expected == byte.class && explicit && internal) {
return Cast.unboxTo(Object.class, Byte.class, true, byte.class);
return PainlessCast.unboxTo(Object.class, Byte.class, true, byte.class);
} else if (expected == short.class && explicit && internal) {
return Cast.unboxTo(Object.class, Short.class, true, short.class);
return PainlessCast.unboxTo(Object.class, Short.class, true, short.class);
} else if (expected == char.class && explicit && internal) {
return Cast.unboxTo(Object.class, Character.class, true, char.class);
return PainlessCast.unboxTo(Object.class, Character.class, true, char.class);
} else if (expected == int.class && explicit && internal) {
return Cast.unboxTo(Object.class, Integer.class, true, int.class);
return PainlessCast.unboxTo(Object.class, Integer.class, true, int.class);
} else if (expected == long.class && explicit && internal) {
return Cast.unboxTo(Object.class, Long.class, true, long.class);
return PainlessCast.unboxTo(Object.class, Long.class, true, long.class);
} else if (expected == float.class && explicit && internal) {
return Cast.unboxTo(Object.class, Float.class, true, float.class);
return PainlessCast.unboxTo(Object.class, Float.class, true, float.class);
} else if (expected == double.class && explicit && internal) {
return Cast.unboxTo(Object.class, Double.class, true, double.class);
return PainlessCast.unboxTo(Object.class, Double.class, true, double.class);
}
} else if (actual == Number.class) {
if (expected == byte.class && explicit && internal) {
return Cast.unboxTo(Number.class, Byte.class, true, byte.class);
return PainlessCast.unboxTo(Number.class, Byte.class, true, byte.class);
} else if (expected == short.class && explicit && internal) {
return Cast.unboxTo(Number.class, Short.class, true, short.class);
return PainlessCast.unboxTo(Number.class, Short.class, true, short.class);
} else if (expected == char.class && explicit && internal) {
return Cast.unboxTo(Number.class, Character.class, true, char.class);
return PainlessCast.unboxTo(Number.class, Character.class, true, char.class);
} else if (expected == int.class && explicit && internal) {
return Cast.unboxTo(Number.class, Integer.class, true, int.class);
return PainlessCast.unboxTo(Number.class, Integer.class, true, int.class);
} else if (expected == long.class && explicit && internal) {
return Cast.unboxTo(Number.class, Long.class, true, long.class);
return PainlessCast.unboxTo(Number.class, Long.class, true, long.class);
} else if (expected == float.class && explicit && internal) {
return Cast.unboxTo(Number.class, Float.class, true, float.class);
return PainlessCast.unboxTo(Number.class, Float.class, true, float.class);
} else if (expected == double.class && explicit && internal) {
return Cast.unboxTo(Number.class, Double.class, true, double.class);
return PainlessCast.unboxTo(Number.class, Double.class, true, double.class);
}
} else if (actual == String.class) {
if (expected == char.class && explicit) {
return Cast.standard(String.class, char.class, true);
return PainlessCast.standard(String.class, char.class, true);
}
} else if (actual == boolean.class) {
if (expected == def.class) {
return Cast.boxFrom(Boolean.class, def.class, explicit, boolean.class);
return PainlessCast.boxFrom(Boolean.class, def.class, explicit, boolean.class);
} else if (expected == Object.class && internal) {
return Cast.boxFrom(Boolean.class, Object.class, explicit, boolean.class);
return PainlessCast.boxFrom(Boolean.class, Object.class, explicit, boolean.class);
} else if (expected == Boolean.class && internal) {
return Cast.boxTo(boolean.class, boolean.class, explicit, boolean.class);
return PainlessCast.boxTo(boolean.class, boolean.class, explicit, boolean.class);
}
} else if (actual == byte.class) {
if (expected == def.class) {
return Cast.boxFrom(Byte.class, def.class, explicit, byte.class);
return PainlessCast.boxFrom(Byte.class, def.class, explicit, byte.class);
} else if (expected == Object.class && internal) {
return Cast.boxFrom(Byte.class, Object.class, explicit, byte.class);
return PainlessCast.boxFrom(Byte.class, Object.class, explicit, byte.class);
} else if (expected == Number.class && internal) {
return Cast.boxFrom(Byte.class, Number.class, explicit, byte.class);
return PainlessCast.boxFrom(Byte.class, Number.class, explicit, byte.class);
} else if (expected == short.class) {
return Cast.standard(byte.class, short.class, explicit);
return PainlessCast.standard(byte.class, short.class, explicit);
} else if (expected == char.class && explicit) {
return Cast.standard(byte.class, char.class, true);
return PainlessCast.standard(byte.class, char.class, true);
} else if (expected == int.class) {
return Cast.standard(byte.class, int.class, explicit);
return PainlessCast.standard(byte.class, int.class, explicit);
} else if (expected == long.class) {
return Cast.standard(byte.class, long.class, explicit);
return PainlessCast.standard(byte.class, long.class, explicit);
} else if (expected == float.class) {
return Cast.standard(byte.class, float.class, explicit);
return PainlessCast.standard(byte.class, float.class, explicit);
} else if (expected == double.class) {
return Cast.standard(byte.class, double.class, explicit);
return PainlessCast.standard(byte.class, double.class, explicit);
} else if (expected == Byte.class && internal) {
return Cast.boxTo(byte.class, byte.class, explicit, byte.class);
return PainlessCast.boxTo(byte.class, byte.class, explicit, byte.class);
} else if (expected == Short.class && internal) {
return Cast.boxTo(byte.class, short.class, explicit, short.class);
return PainlessCast.boxTo(byte.class, short.class, explicit, short.class);
} else if (expected == Character.class && explicit && internal) {
return Cast.boxTo(byte.class, char.class, true, char.class);
return PainlessCast.boxTo(byte.class, char.class, true, char.class);
} else if (expected == Integer.class && internal) {
return Cast.boxTo(byte.class, int.class, explicit, int.class);
return PainlessCast.boxTo(byte.class, int.class, explicit, int.class);
} else if (expected == Long.class && internal) {
return Cast.boxTo(byte.class, long.class, explicit, long.class);
return PainlessCast.boxTo(byte.class, long.class, explicit, long.class);
} else if (expected == Float.class && internal) {
return Cast.boxTo(byte.class, float.class, explicit, float.class);
return PainlessCast.boxTo(byte.class, float.class, explicit, float.class);
} else if (expected == Double.class && internal) {
return Cast.boxTo(byte.class, double.class, explicit, double.class);
return PainlessCast.boxTo(byte.class, double.class, explicit, double.class);
}
} else if (actual == short.class) {
if (expected == def.class) {
return Cast.boxFrom(Short.class, def.class, explicit, short.class);
return PainlessCast.boxFrom(Short.class, def.class, explicit, short.class);
} else if (expected == Object.class && internal) {
return Cast.boxFrom(Short.class, Object.class, explicit, short.class);
return PainlessCast.boxFrom(Short.class, Object.class, explicit, short.class);
} else if (expected == Number.class && internal) {
return Cast.boxFrom(Short.class, Number.class, explicit, short.class);
return PainlessCast.boxFrom(Short.class, Number.class, explicit, short.class);
} else if (expected == byte.class && explicit) {
return Cast.standard(short.class, byte.class, true);
return PainlessCast.standard(short.class, byte.class, true);
} else if (expected == char.class && explicit) {
return Cast.standard(short.class, char.class, true);
return PainlessCast.standard(short.class, char.class, true);
} else if (expected == int.class) {
return Cast.standard(short.class, int.class, explicit);
return PainlessCast.standard(short.class, int.class, explicit);
} else if (expected == long.class) {
return Cast.standard(short.class, long.class, explicit);
return PainlessCast.standard(short.class, long.class, explicit);
} else if (expected == float.class) {
return Cast.standard(short.class, float.class, explicit);
return PainlessCast.standard(short.class, float.class, explicit);
} else if (expected == double.class) {
return Cast.standard(short.class, double.class, explicit);
return PainlessCast.standard(short.class, double.class, explicit);
} else if (expected == Byte.class && explicit && internal) {
return Cast.boxTo(short.class, byte.class, true, byte.class);
return PainlessCast.boxTo(short.class, byte.class, true, byte.class);
} else if (expected == Short.class && internal) {
return Cast.boxTo(short.class, short.class, explicit, short.class);
return PainlessCast.boxTo(short.class, short.class, explicit, short.class);
} else if (expected == Character.class && explicit && internal) {
return Cast.boxTo(short.class, char.class, true, char.class);
return PainlessCast.boxTo(short.class, char.class, true, char.class);
} else if (expected == Integer.class && internal) {
return Cast.boxTo(short.class, int.class, explicit, int.class);
return PainlessCast.boxTo(short.class, int.class, explicit, int.class);
} else if (expected == Long.class && internal) {
return Cast.boxTo(short.class, long.class, explicit, long.class);
return PainlessCast.boxTo(short.class, long.class, explicit, long.class);
} else if (expected == Float.class && internal) {
return Cast.boxTo(short.class, float.class, explicit, float.class);
return PainlessCast.boxTo(short.class, float.class, explicit, float.class);
} else if (expected == Double.class && internal) {
return Cast.boxTo(short.class, double.class, explicit, double.class);
return PainlessCast.boxTo(short.class, double.class, explicit, double.class);
}
} else if (actual == char.class) {
if (expected == def.class) {
return Cast.boxFrom(Character.class, def.class, explicit, char.class);
return PainlessCast.boxFrom(Character.class, def.class, explicit, char.class);
} else if (expected == Object.class && internal) {
return Cast.boxFrom(Character.class, Object.class, explicit, char.class);
return PainlessCast.boxFrom(Character.class, Object.class, explicit, char.class);
} else if (expected == Number.class && internal) {
return Cast.boxFrom(Character.class, Number.class, explicit, char.class);
return PainlessCast.boxFrom(Character.class, Number.class, explicit, char.class);
} else if (expected == String.class) {
return Cast.standard(char.class, String.class, explicit);
return PainlessCast.standard(char.class, String.class, explicit);
} else if (expected == byte.class && explicit) {
return Cast.standard(char.class, byte.class, true);
return PainlessCast.standard(char.class, byte.class, true);
} else if (expected == short.class && explicit) {
return Cast.standard(char.class, short.class, true);
return PainlessCast.standard(char.class, short.class, true);
} else if (expected == int.class) {
return Cast.standard(char.class, int.class, explicit);
return PainlessCast.standard(char.class, int.class, explicit);
} else if (expected == long.class) {
return Cast.standard(char.class, long.class, explicit);
return PainlessCast.standard(char.class, long.class, explicit);
} else if (expected == float.class) {
return Cast.standard(char.class, float.class, explicit);
return PainlessCast.standard(char.class, float.class, explicit);
} else if (expected == double.class) {
return Cast.standard(char.class, double.class, explicit);
return PainlessCast.standard(char.class, double.class, explicit);
} else if (expected == Byte.class && explicit && internal) {
return Cast.boxTo(char.class, byte.class, true, byte.class);
return PainlessCast.boxTo(char.class, byte.class, true, byte.class);
} else if (expected == Short.class && internal) {
return Cast.boxTo(char.class, short.class, explicit, short.class);
return PainlessCast.boxTo(char.class, short.class, explicit, short.class);
} else if (expected == Character.class && internal) {
return Cast.boxTo(char.class, char.class, true, char.class);
return PainlessCast.boxTo(char.class, char.class, true, char.class);
} else if (expected == Integer.class && internal) {
return Cast.boxTo(char.class, int.class, explicit, int.class);
return PainlessCast.boxTo(char.class, int.class, explicit, int.class);
} else if (expected == Long.class && internal) {
return Cast.boxTo(char.class, long.class, explicit, long.class);
return PainlessCast.boxTo(char.class, long.class, explicit, long.class);
} else if (expected == Float.class && internal) {
return Cast.boxTo(char.class, float.class, explicit, float.class);
return PainlessCast.boxTo(char.class, float.class, explicit, float.class);
} else if (expected == Double.class && internal) {
return Cast.boxTo(char.class, double.class, explicit, double.class);
return PainlessCast.boxTo(char.class, double.class, explicit, double.class);
}
} else if (actual == int.class) {
if (expected == def.class) {
return Cast.boxFrom(Integer.class, def.class, explicit, int.class);
return PainlessCast.boxFrom(Integer.class, def.class, explicit, int.class);
} else if (expected == Object.class && internal) {
return Cast.boxFrom(Integer.class, Object.class, explicit, int.class);
return PainlessCast.boxFrom(Integer.class, Object.class, explicit, int.class);
} else if (expected == Number.class && internal) {
return Cast.boxFrom(Integer.class, Number.class, explicit, int.class);
return PainlessCast.boxFrom(Integer.class, Number.class, explicit, int.class);
} else if (expected == byte.class && explicit) {
return Cast.standard(int.class, byte.class, true);
return PainlessCast.standard(int.class, byte.class, true);
} else if (expected == char.class && explicit) {
return Cast.standard(int.class, char.class, true);
return PainlessCast.standard(int.class, char.class, true);
} else if (expected == short.class && explicit) {
return Cast.standard(int.class, short.class, true);
return PainlessCast.standard(int.class, short.class, true);
} else if (expected == long.class) {
return Cast.standard(int.class, long.class, explicit);
return PainlessCast.standard(int.class, long.class, explicit);
} else if (expected == float.class) {
return Cast.standard(int.class, float.class, explicit);
return PainlessCast.standard(int.class, float.class, explicit);
} else if (expected == double.class) {
return Cast.standard(int.class, double.class, explicit);
return PainlessCast.standard(int.class, double.class, explicit);
} else if (expected == Byte.class && explicit && internal) {
return Cast.boxTo(int.class, byte.class, true, byte.class);
return PainlessCast.boxTo(int.class, byte.class, true, byte.class);
} else if (expected == Short.class && explicit && internal) {
return Cast.boxTo(int.class, short.class, true, short.class);
return PainlessCast.boxTo(int.class, short.class, true, short.class);
} else if (expected == Character.class && explicit && internal) {
return Cast.boxTo(int.class, char.class, true, char.class);
return PainlessCast.boxTo(int.class, char.class, true, char.class);
} else if (expected == Integer.class && internal) {
return Cast.boxTo(int.class, int.class, explicit, int.class);
return PainlessCast.boxTo(int.class, int.class, explicit, int.class);
} else if (expected == Long.class && internal) {
return Cast.boxTo(int.class, long.class, explicit, long.class);
return PainlessCast.boxTo(int.class, long.class, explicit, long.class);
} else if (expected == Float.class && internal) {
return Cast.boxTo(int.class, float.class, explicit, float.class);
return PainlessCast.boxTo(int.class, float.class, explicit, float.class);
} else if (expected == Double.class && internal) {
return Cast.boxTo(int.class, double.class, explicit, double.class);
return PainlessCast.boxTo(int.class, double.class, explicit, double.class);
}
} else if (actual == long.class) {
if (expected == def.class) {
return Cast.boxFrom(Long.class, def.class, explicit, long.class);
return PainlessCast.boxFrom(Long.class, def.class, explicit, long.class);
} else if (expected == Object.class && internal) {
return Cast.boxFrom(Long.class, Object.class, explicit, long.class);
return PainlessCast.boxFrom(Long.class, Object.class, explicit, long.class);
} else if (expected == Number.class && internal) {
return Cast.boxFrom(Long.class, Number.class, explicit, long.class);
return PainlessCast.boxFrom(Long.class, Number.class, explicit, long.class);
} else if (expected == byte.class && explicit) {
return Cast.standard(long.class, byte.class, true);
return PainlessCast.standard(long.class, byte.class, true);
} else if (expected == char.class && explicit) {
return Cast.standard(long.class, char.class, true);
return PainlessCast.standard(long.class, char.class, true);
} else if (expected == short.class && explicit) {
return Cast.standard(long.class, short.class, true);
return PainlessCast.standard(long.class, short.class, true);
} else if (expected == int.class && explicit) {
return Cast.standard(long.class, int.class, true);
return PainlessCast.standard(long.class, int.class, true);
} else if (expected == float.class) {
return Cast.standard(long.class, float.class, explicit);
return PainlessCast.standard(long.class, float.class, explicit);
} else if (expected == double.class) {
return Cast.standard(long.class, double.class, explicit);
return PainlessCast.standard(long.class, double.class, explicit);
} else if (expected == Byte.class && explicit && internal) {
return Cast.boxTo(long.class, byte.class, true, byte.class);
return PainlessCast.boxTo(long.class, byte.class, true, byte.class);
} else if (expected == Short.class && explicit && internal) {
return Cast.boxTo(long.class, short.class, true, short.class);
return PainlessCast.boxTo(long.class, short.class, true, short.class);
} else if (expected == Character.class && explicit && internal) {
return Cast.boxTo(long.class, char.class, true, char.class);
return PainlessCast.boxTo(long.class, char.class, true, char.class);
} else if (expected == Integer.class && explicit && internal) {
return Cast.boxTo(long.class, int.class, true, int.class);
return PainlessCast.boxTo(long.class, int.class, true, int.class);
} else if (expected == Long.class && internal) {
return Cast.boxTo(long.class, long.class, explicit, long.class);
return PainlessCast.boxTo(long.class, long.class, explicit, long.class);
} else if (expected == Float.class && internal) {
return Cast.boxTo(long.class, float.class, explicit, float.class);
return PainlessCast.boxTo(long.class, float.class, explicit, float.class);
} else if (expected == Double.class && internal) {
return Cast.boxTo(long.class, double.class, explicit, double.class);
return PainlessCast.boxTo(long.class, double.class, explicit, double.class);
}
} else if (actual == float.class) {
if (expected == def.class) {
return Cast.boxFrom(Float.class, def.class, explicit, float.class);
return PainlessCast.boxFrom(Float.class, def.class, explicit, float.class);
} else if (expected == Object.class && internal) {
return Cast.boxFrom(Float.class, Object.class, explicit, float.class);
return PainlessCast.boxFrom(Float.class, Object.class, explicit, float.class);
} else if (expected == Number.class && internal) {
return Cast.boxFrom(Float.class, Number.class, explicit, float.class);
return PainlessCast.boxFrom(Float.class, Number.class, explicit, float.class);
} else if (expected == byte.class && explicit) {
return Cast.standard(float.class, byte.class, true);
return PainlessCast.standard(float.class, byte.class, true);
} else if (expected == char.class && explicit) {
return Cast.standard(float.class, char.class, true);
return PainlessCast.standard(float.class, char.class, true);
} else if (expected == short.class && explicit) {
return Cast.standard(float.class, short.class, true);
return PainlessCast.standard(float.class, short.class, true);
} else if (expected == int.class && explicit) {
return Cast.standard(float.class, int.class, true);
return PainlessCast.standard(float.class, int.class, true);
} else if (expected == long.class && explicit) {
return Cast.standard(float.class, long.class, true);
return PainlessCast.standard(float.class, long.class, true);
} else if (expected == double.class) {
return Cast.standard(float.class, double.class, explicit);
return PainlessCast.standard(float.class, double.class, explicit);
} else if (expected == Byte.class && explicit && internal) {
return Cast.boxTo(float.class, byte.class, true, byte.class);
return PainlessCast.boxTo(float.class, byte.class, true, byte.class);
} else if (expected == Short.class && explicit && internal) {
return Cast.boxTo(float.class, short.class, true, short.class);
return PainlessCast.boxTo(float.class, short.class, true, short.class);
} else if (expected == Character.class && explicit && internal) {
return Cast.boxTo(float.class, char.class, true, char.class);
return PainlessCast.boxTo(float.class, char.class, true, char.class);
} else if (expected == Integer.class && explicit && internal) {
return Cast.boxTo(float.class, int.class, true, int.class);
return PainlessCast.boxTo(float.class, int.class, true, int.class);
} else if (expected == Long.class && explicit && internal) {
return Cast.boxTo(float.class, long.class, true, long.class);
return PainlessCast.boxTo(float.class, long.class, true, long.class);
} else if (expected == Float.class && internal) {
return Cast.boxTo(float.class, float.class, explicit, float.class);
return PainlessCast.boxTo(float.class, float.class, explicit, float.class);
} else if (expected == Double.class && internal) {
return Cast.boxTo(float.class, double.class, explicit, double.class);
return PainlessCast.boxTo(float.class, double.class, explicit, double.class);
}
} else if (actual == double.class) {
if (expected == def.class) {
return Cast.boxFrom(Double.class, def.class, explicit, double.class);
return PainlessCast.boxFrom(Double.class, def.class, explicit, double.class);
} else if (expected == Object.class && internal) {
return Cast.boxFrom(Double.class, Object.class, explicit, double.class);
return PainlessCast.boxFrom(Double.class, Object.class, explicit, double.class);
} else if (expected == Number.class && internal) {
return Cast.boxFrom(Double.class, Number.class, explicit, double.class);
return PainlessCast.boxFrom(Double.class, Number.class, explicit, double.class);
} else if (expected == byte.class && explicit) {
return Cast.standard(double.class, byte.class, true);
return PainlessCast.standard(double.class, byte.class, true);
} else if (expected == char.class && explicit) {
return Cast.standard(double.class, char.class, true);
return PainlessCast.standard(double.class, char.class, true);
} else if (expected == short.class && explicit) {
return Cast.standard(double.class, short.class, true);
return PainlessCast.standard(double.class, short.class, true);
} else if (expected == int.class && explicit) {
return Cast.standard(double.class, int.class, true);
return PainlessCast.standard(double.class, int.class, true);
} else if (expected == long.class && explicit) {
return Cast.standard(double.class, long.class, true);
return PainlessCast.standard(double.class, long.class, true);
} else if (expected == float.class && explicit) {
return Cast.standard(double.class, float.class, true);
return PainlessCast.standard(double.class, float.class, true);
} else if (expected == Byte.class && explicit && internal) {
return Cast.boxTo(double.class, byte.class, true, byte.class);
return PainlessCast.boxTo(double.class, byte.class, true, byte.class);
} else if (expected == Short.class && explicit && internal) {
return Cast.boxTo(double.class, short.class, true, short.class);
return PainlessCast.boxTo(double.class, short.class, true, short.class);
} else if (expected == Character.class && explicit && internal) {
return Cast.boxTo(double.class, char.class, true, char.class);
return PainlessCast.boxTo(double.class, char.class, true, char.class);
} else if (expected == Integer.class && explicit && internal) {
return Cast.boxTo(double.class, int.class, true, int.class);
return PainlessCast.boxTo(double.class, int.class, true, int.class);
} else if (expected == Long.class && explicit && internal) {
return Cast.boxTo(double.class, long.class, true, long.class);
return PainlessCast.boxTo(double.class, long.class, true, long.class);
} else if (expected == Float.class && explicit && internal) {
return Cast.boxTo(double.class, float.class, true, float.class);
return PainlessCast.boxTo(double.class, float.class, true, float.class);
} else if (expected == Double.class && internal) {
return Cast.boxTo(double.class, double.class, explicit, double.class);
return PainlessCast.boxTo(double.class, double.class, explicit, double.class);
}
} else if (actual == Boolean.class) {
if (expected == boolean.class && internal) {
return Cast.unboxFrom(boolean.class, boolean.class, explicit, boolean.class);
return PainlessCast.unboxFrom(boolean.class, boolean.class, explicit, boolean.class);
}
} else if (actual == Byte.class) {
if (expected == byte.class && internal) {
return Cast.unboxFrom(byte.class, byte.class, explicit, byte.class);
return PainlessCast.unboxFrom(byte.class, byte.class, explicit, byte.class);
} else if (expected == short.class && internal) {
return Cast.unboxFrom(byte.class, short.class, explicit, byte.class);
return PainlessCast.unboxFrom(byte.class, short.class, explicit, byte.class);
} else if (expected == char.class && explicit && internal) {
return Cast.unboxFrom(byte.class, char.class, true, byte.class);
return PainlessCast.unboxFrom(byte.class, char.class, true, byte.class);
} else if (expected == int.class && internal) {
return Cast.unboxFrom(byte.class, int.class, explicit, byte.class);
return PainlessCast.unboxFrom(byte.class, int.class, explicit, byte.class);
} else if (expected == long.class && internal) {
return Cast.unboxFrom(byte.class, long.class, explicit, byte.class);
return PainlessCast.unboxFrom(byte.class, long.class, explicit, byte.class);
} else if (expected == float.class && internal) {
return Cast.unboxFrom(byte.class, float.class, explicit, byte.class);
return PainlessCast.unboxFrom(byte.class, float.class, explicit, byte.class);
} else if (expected == double.class && internal) {
return Cast.unboxFrom(byte.class, double.class, explicit, byte.class);
return PainlessCast.unboxFrom(byte.class, double.class, explicit, byte.class);
}
} else if (actual == Short.class) {
if (expected == byte.class && explicit && internal) {
return Cast.unboxFrom(short.class, byte.class, true, short.class);
return PainlessCast.unboxFrom(short.class, byte.class, true, short.class);
} else if (expected == short.class && internal) {
return Cast.unboxFrom(short.class, short.class, explicit, short.class);
return PainlessCast.unboxFrom(short.class, short.class, explicit, short.class);
} else if (expected == char.class && explicit && internal) {
return Cast.unboxFrom(short.class, char.class, true, short.class);
return PainlessCast.unboxFrom(short.class, char.class, true, short.class);
} else if (expected == int.class && internal) {
return Cast.unboxFrom(short.class, int.class, explicit, short.class);
return PainlessCast.unboxFrom(short.class, int.class, explicit, short.class);
} else if (expected == long.class && internal) {
return Cast.unboxFrom(short.class, long.class, explicit, short.class);
return PainlessCast.unboxFrom(short.class, long.class, explicit, short.class);
} else if (expected == float.class && internal) {
return Cast.unboxFrom(short.class, float.class, explicit, short.class);
return PainlessCast.unboxFrom(short.class, float.class, explicit, short.class);
} else if (expected == double.class && internal) {
return Cast.unboxFrom(short.class, double.class, explicit, short.class);
return PainlessCast.unboxFrom(short.class, double.class, explicit, short.class);
}
} else if (actual == Character.class) {
if (expected == byte.class && explicit && internal) {
return Cast.unboxFrom(char.class, byte.class, true, char.class);
return PainlessCast.unboxFrom(char.class, byte.class, true, char.class);
} else if (expected == short.class && explicit && internal) {
return Cast.unboxFrom(char.class, short.class, true, char.class);
return PainlessCast.unboxFrom(char.class, short.class, true, char.class);
} else if (expected == char.class && internal) {
return Cast.unboxFrom(char.class, char.class, explicit, char.class);
return PainlessCast.unboxFrom(char.class, char.class, explicit, char.class);
} else if (expected == int.class && internal) {
return Cast.unboxFrom(char.class, int.class, explicit, char.class);
return PainlessCast.unboxFrom(char.class, int.class, explicit, char.class);
} else if (expected == long.class && internal) {
return Cast.unboxFrom(char.class, long.class, explicit, char.class);
return PainlessCast.unboxFrom(char.class, long.class, explicit, char.class);
} else if (expected == float.class && internal) {
return Cast.unboxFrom(char.class, float.class, explicit, char.class);
return PainlessCast.unboxFrom(char.class, float.class, explicit, char.class);
} else if (expected == double.class && internal) {
return Cast.unboxFrom(char.class, double.class, explicit, char.class);
return PainlessCast.unboxFrom(char.class, double.class, explicit, char.class);
}
} else if (actual == Integer.class) {
if (expected == byte.class && explicit && internal) {
return Cast.unboxFrom(int.class, byte.class, true, int.class);
return PainlessCast.unboxFrom(int.class, byte.class, true, int.class);
} else if (expected == short.class && explicit && internal) {
return Cast.unboxFrom(int.class, short.class, true, int.class);
return PainlessCast.unboxFrom(int.class, short.class, true, int.class);
} else if (expected == char.class && explicit && internal) {
return Cast.unboxFrom(int.class, char.class, true, int.class);
return PainlessCast.unboxFrom(int.class, char.class, true, int.class);
} else if (expected == int.class && internal) {
return Cast.unboxFrom(int.class, int.class, explicit, int.class);
return PainlessCast.unboxFrom(int.class, int.class, explicit, int.class);
} else if (expected == long.class && internal) {
return Cast.unboxFrom(int.class, long.class, explicit, int.class);
return PainlessCast.unboxFrom(int.class, long.class, explicit, int.class);
} else if (expected == float.class && internal) {
return Cast.unboxFrom(int.class, float.class, explicit, int.class);
return PainlessCast.unboxFrom(int.class, float.class, explicit, int.class);
} else if (expected == double.class && internal) {
return Cast.unboxFrom(int.class, double.class, explicit, int.class);
return PainlessCast.unboxFrom(int.class, double.class, explicit, int.class);
}
} else if (actual == Long.class) {
if (expected == byte.class && explicit && internal) {
return Cast.unboxFrom(long.class, byte.class, true, long.class);
return PainlessCast.unboxFrom(long.class, byte.class, true, long.class);
} else if (expected == short.class && explicit && internal) {
return Cast.unboxFrom(long.class, short.class, true, long.class);
return PainlessCast.unboxFrom(long.class, short.class, true, long.class);
} else if (expected == char.class && explicit && internal) {
return Cast.unboxFrom(long.class, char.class, true, long.class);
return PainlessCast.unboxFrom(long.class, char.class, true, long.class);
} else if (expected == int.class && explicit && internal) {
return Cast.unboxFrom(long.class, int.class, true, long.class);
return PainlessCast.unboxFrom(long.class, int.class, true, long.class);
} else if (expected == long.class && internal) {
return Cast.unboxFrom(long.class, long.class, explicit, long.class);
return PainlessCast.unboxFrom(long.class, long.class, explicit, long.class);
} else if (expected == float.class && internal) {
return Cast.unboxFrom(long.class, float.class, explicit, long.class);
return PainlessCast.unboxFrom(long.class, float.class, explicit, long.class);
} else if (expected == double.class && internal) {
return Cast.unboxFrom(long.class, double.class, explicit, long.class);
return PainlessCast.unboxFrom(long.class, double.class, explicit, long.class);
}
} else if (actual == Float.class) {
if (expected == byte.class && explicit && internal) {
return Cast.unboxFrom(float.class, byte.class, true, float.class);
return PainlessCast.unboxFrom(float.class, byte.class, true, float.class);
} else if (expected == short.class && explicit && internal) {
return Cast.unboxFrom(float.class, short.class, true, float.class);
return PainlessCast.unboxFrom(float.class, short.class, true, float.class);
} else if (expected == char.class && explicit && internal) {
return Cast.unboxFrom(float.class, char.class, true, float.class);
return PainlessCast.unboxFrom(float.class, char.class, true, float.class);
} else if (expected == int.class && explicit && internal) {
return Cast.unboxFrom(float.class, int.class, true, float.class);
return PainlessCast.unboxFrom(float.class, int.class, true, float.class);
} else if (expected == long.class && explicit && internal) {
return Cast.unboxFrom(float.class, long.class, true, float.class);
return PainlessCast.unboxFrom(float.class, long.class, true, float.class);
} else if (expected == float.class && internal) {
return Cast.unboxFrom(float.class, float.class, explicit, float.class);
return PainlessCast.unboxFrom(float.class, float.class, explicit, float.class);
} else if (expected == double.class && internal) {
return Cast.unboxFrom(float.class, double.class, explicit, float.class);
return PainlessCast.unboxFrom(float.class, double.class, explicit, float.class);
}
} else if (actual == Double.class) {
if (expected == byte.class && explicit && internal) {
return Cast.unboxFrom(double.class, byte.class, true, double.class);
return PainlessCast.unboxFrom(double.class, byte.class, true, double.class);
} else if (expected == short.class && explicit && internal) {
return Cast.unboxFrom(double.class, short.class, true, double.class);
return PainlessCast.unboxFrom(double.class, short.class, true, double.class);
} else if (expected == char.class && explicit && internal) {
return Cast.unboxFrom(double.class, char.class, true, double.class);
return PainlessCast.unboxFrom(double.class, char.class, true, double.class);
} else if (expected == int.class && explicit && internal) {
return Cast.unboxFrom(double.class, int.class, true, double.class);
return PainlessCast.unboxFrom(double.class, int.class, true, double.class);
} else if (expected == long.class && explicit && internal) {
return Cast.unboxFrom(double.class, long.class, true, double.class);
return PainlessCast.unboxFrom(double.class, long.class, true, double.class);
} else if (expected == float.class && explicit && internal) {
return Cast.unboxFrom(double.class, float.class, true, double.class);
return PainlessCast.unboxFrom(double.class, float.class, true, double.class);
} else if (expected == double.class && internal) {
return Cast.unboxFrom(double.class, double.class, explicit, double.class);
return PainlessCast.unboxFrom(double.class, double.class, explicit, double.class);
}
}
@ -462,14 +463,14 @@ public final class AnalyzerCaster {
(actual != void.class && expected == def.class) ||
expected.isAssignableFrom(actual) ||
(actual.isAssignableFrom(expected) && explicit)) {
return Cast.standard(actual, expected, explicit);
return PainlessCast.standard(actual, expected, explicit);
} else {
throw location.createError(new ClassCastException(
"Cannot cast from [" + Definition.ClassToName(actual) + "] to [" + Definition.ClassToName(expected) + "]."));
"Cannot cast from [" + PainlessLookup.ClassToName(actual) + "] to [" + PainlessLookup.ClassToName(expected) + "]."));
}
}
public static Object constCast(Location location, Object constant, Cast cast) {
public static Object constCast(Location location, Object constant, PainlessCast cast) {
Class<?> fsort = cast.from;
Class<?> tsort = cast.to;

View File

@ -21,6 +21,7 @@ package org.elasticsearch.painless;
import org.elasticsearch.bootstrap.BootstrapInfo;
import org.elasticsearch.painless.antlr.Walker;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.node.SSource;
import org.elasticsearch.painless.spi.Whitelist;
import org.objectweb.asm.util.Printer;
@ -70,26 +71,26 @@ final class Compiler {
*/
static final class Loader extends SecureClassLoader {
private final AtomicInteger lambdaCounter = new AtomicInteger(0);
private final Definition definition;
private final PainlessLookup painlessLookup;
/**
* @param parent The parent ClassLoader.
*/
Loader(ClassLoader parent, Definition definition) {
Loader(ClassLoader parent, PainlessLookup painlessLookup) {
super(parent);
this.definition = definition;
this.painlessLookup = painlessLookup;
}
/**
* Will check to see if the {@link Class} has already been loaded when
* the {@link Definition} was initially created. Allows for {@link Whitelist}ed
* the {@link PainlessLookup} was initially created. Allows for {@link Whitelist}ed
* classes to be loaded from other modules/plugins without a direct relationship
* to the module's/plugin's {@link ClassLoader}.
*/
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> found = definition.getClassFromBinaryName(name);
Class<?> found = painlessLookup.getClassFromBinaryName(name);
return found != null ? found : super.findClass(name);
}
@ -135,10 +136,10 @@ final class Compiler {
/**
* Return a new {@link Loader} for a script using the
* {@link Compiler}'s specified {@link Definition}.
* {@link Compiler}'s specified {@link PainlessLookup}.
*/
public Loader createLoader(ClassLoader parent) {
return new Loader(parent, definition);
return new Loader(parent, painlessLookup);
}
/**
@ -149,16 +150,16 @@ final class Compiler {
/**
* The whitelist the script will use.
*/
private final Definition definition;
private final PainlessLookup painlessLookup;
/**
* Standard constructor.
* @param base The class/interface the script is guaranteed to derive/implement.
* @param definition The whitelist the script will use.
* @param painlessLookup The whitelist the script will use.
*/
Compiler(Class<?> base, Definition definition) {
Compiler(Class<?> base, PainlessLookup painlessLookup) {
this.base = base;
this.definition = definition;
this.painlessLookup = painlessLookup;
}
/**
@ -176,10 +177,10 @@ final class Compiler {
" plugin if a script longer than this length is a requirement.");
}
ScriptClassInfo scriptClassInfo = new ScriptClassInfo(definition, base);
SSource root = Walker.buildPainlessTree(scriptClassInfo, reserved, name, source, settings, definition,
ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, base);
SSource root = Walker.buildPainlessTree(scriptClassInfo, reserved, name, source, settings, painlessLookup,
null);
root.analyze(definition);
root.analyze(painlessLookup);
root.write();
try {
@ -187,7 +188,7 @@ final class Compiler {
clazz.getField("$NAME").set(null, name);
clazz.getField("$SOURCE").set(null, source);
clazz.getField("$STATEMENTS").set(null, root.getStatements());
clazz.getField("$DEFINITION").set(null, definition);
clazz.getField("$DEFINITION").set(null, painlessLookup);
return clazz.getConstructors()[0];
} catch (Exception exception) { // Catch everything to let the user know this is something caused internally.
@ -208,10 +209,10 @@ final class Compiler {
" plugin if a script longer than this length is a requirement.");
}
ScriptClassInfo scriptClassInfo = new ScriptClassInfo(definition, base);
SSource root = Walker.buildPainlessTree(scriptClassInfo, new MainMethodReserved(), name, source, settings, definition,
ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, base);
SSource root = Walker.buildPainlessTree(scriptClassInfo, new MainMethodReserved(), name, source, settings, painlessLookup,
debugStream);
root.analyze(definition);
root.analyze(painlessLookup);
root.write();
return root.getBytes();

View File

@ -19,13 +19,14 @@
package org.elasticsearch.painless;
import org.elasticsearch.painless.Definition.Method;
import org.elasticsearch.painless.Definition.Struct;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.lookup.PainlessClass;
import org.elasticsearch.painless.lookup.PainlessMethodKey;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.util.BitSet;
import java.util.Collections;
@ -60,14 +61,15 @@ public final class Def {
*/
@SuppressWarnings("unused") // getArrayLength() methods are are actually used, javac just does not know :)
private static final class ArrayLengthHelper {
private static final Lookup PRIV_LOOKUP = MethodHandles.lookup();
private static final MethodHandles.Lookup PRIVATE_METHOD_HANDLES_LOOKUP = MethodHandles.lookup();
private static final Map<Class<?>,MethodHandle> ARRAY_TYPE_MH_MAPPING = Collections.unmodifiableMap(
Stream.of(boolean[].class, byte[].class, short[].class, int[].class, long[].class,
char[].class, float[].class, double[].class, Object[].class)
.collect(Collectors.toMap(Function.identity(), type -> {
try {
return PRIV_LOOKUP.findStatic(PRIV_LOOKUP.lookupClass(), "getArrayLength", MethodType.methodType(int.class, type));
return PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(
PRIVATE_METHOD_HANDLES_LOOKUP.lookupClass(), "getArrayLength", MethodType.methodType(int.class, type));
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);
}
@ -116,17 +118,17 @@ public final class Def {
static final MethodHandle JAVA9_ARRAY_LENGTH_MH_FACTORY;
static {
final Lookup lookup = MethodHandles.publicLookup();
final MethodHandles.Lookup methodHandlesLookup = MethodHandles.publicLookup();
try {
MAP_GET = lookup.findVirtual(Map.class , "get", MethodType.methodType(Object.class, Object.class));
MAP_PUT = lookup.findVirtual(Map.class , "put", MethodType.methodType(Object.class, Object.class, Object.class));
LIST_GET = lookup.findVirtual(List.class, "get", MethodType.methodType(Object.class, int.class));
LIST_SET = lookup.findVirtual(List.class, "set", MethodType.methodType(Object.class, int.class, Object.class));
ITERATOR = lookup.findVirtual(Iterable.class, "iterator", MethodType.methodType(Iterator.class));
MAP_INDEX_NORMALIZE = lookup.findStatic(Def.class, "mapIndexNormalize",
MAP_GET = methodHandlesLookup.findVirtual(Map.class , "get", MethodType.methodType(Object.class, Object.class));
MAP_PUT = methodHandlesLookup.findVirtual(Map.class , "put", MethodType.methodType(Object.class, Object.class, Object.class));
LIST_GET = methodHandlesLookup.findVirtual(List.class, "get", MethodType.methodType(Object.class, int.class));
LIST_SET = methodHandlesLookup.findVirtual(List.class, "set", MethodType.methodType(Object.class, int.class, Object.class));
ITERATOR = methodHandlesLookup.findVirtual(Iterable.class, "iterator", MethodType.methodType(Iterator.class));
MAP_INDEX_NORMALIZE = methodHandlesLookup.findStatic(Def.class, "mapIndexNormalize",
MethodType.methodType(Object.class, Map.class, Object.class));
LIST_INDEX_NORMALIZE = lookup.findStatic(Def.class, "listIndexNormalize",
LIST_INDEX_NORMALIZE = methodHandlesLookup.findStatic(Def.class, "listIndexNormalize",
MethodType.methodType(int.class, List.class, int.class));
} catch (final ReflectiveOperationException roe) {
throw new AssertionError(roe);
@ -136,7 +138,7 @@ public final class Def {
// https://bugs.openjdk.java.net/browse/JDK-8156915
MethodHandle arrayLengthMHFactory;
try {
arrayLengthMHFactory = lookup.findStatic(MethodHandles.class, "arrayLength",
arrayLengthMHFactory = methodHandlesLookup.findStatic(MethodHandles.class, "arrayLength",
MethodType.methodType(MethodHandle.class, Class.class));
} catch (final ReflectiveOperationException roe) {
arrayLengthMHFactory = null;
@ -174,31 +176,31 @@ public final class Def {
* until it finds a matching whitelisted method. If one is not found, it throws an exception.
* Otherwise it returns the matching method.
* <p>
* @params definition the whitelist
* @params painlessLookup the whitelist
* @param receiverClass Class of the object to invoke the method on.
* @param name Name of the method.
* @param arity arity of method
* @return matching method to invoke. never returns null.
* @throws IllegalArgumentException if no matching whitelisted method was found.
*/
static Method lookupMethodInternal(Definition definition, Class<?> receiverClass, String name, int arity) {
Definition.MethodKey key = new Definition.MethodKey(name, arity);
static PainlessMethod lookupMethodInternal(PainlessLookup painlessLookup, Class<?> receiverClass, String name, int arity) {
PainlessMethodKey key = new PainlessMethodKey(name, arity);
// check whitelist for matching method
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
Struct struct = definition.getPainlessStructFromJavaClass(clazz);
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(clazz);
if (struct != null) {
Method method = struct.methods.get(key);
PainlessMethod method = struct.methods.get(key);
if (method != null) {
return method;
}
}
for (Class<?> iface : clazz.getInterfaces()) {
struct = definition.getPainlessStructFromJavaClass(iface);
struct = painlessLookup.getPainlessStructFromJavaClass(iface);
if (struct != null) {
Method method = struct.methods.get(key);
PainlessMethod method = struct.methods.get(key);
if (method != null) {
return method;
}
@ -220,8 +222,8 @@ public final class Def {
* until it finds a matching whitelisted method. If one is not found, it throws an exception.
* Otherwise it returns a handle to the matching method.
* <p>
* @param definition the whitelist
* @param lookup caller's lookup
* @param painlessLookup the whitelist
* @param methodHandlesLookup caller's lookup
* @param callSiteType callsite's type
* @param receiverClass Class of the object to invoke the method on.
* @param name Name of the method.
@ -230,13 +232,13 @@ public final class Def {
* @throws IllegalArgumentException if no matching whitelisted method was found.
* @throws Throwable if a method reference cannot be converted to an functional interface
*/
static MethodHandle lookupMethod(Definition definition, Lookup lookup, MethodType callSiteType,
Class<?> receiverClass, String name, Object args[]) throws Throwable {
static MethodHandle lookupMethod(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, MethodType callSiteType,
Class<?> receiverClass, String name, Object args[]) throws Throwable {
String recipeString = (String) args[0];
int numArguments = callSiteType.parameterCount();
// simple case: no lambdas
if (recipeString.isEmpty()) {
return lookupMethodInternal(definition, receiverClass, name, numArguments - 1).handle;
return lookupMethodInternal(painlessLookup, receiverClass, name, numArguments - 1).handle;
}
// convert recipe string to a bitset for convenience (the code below should be refactored...)
@ -259,7 +261,7 @@ public final class Def {
// lookup the method with the proper arity, then we know everything (e.g. interface types of parameters).
// based on these we can finally link any remaining lambdas that were deferred.
Method method = lookupMethodInternal(definition, receiverClass, name, arity);
PainlessMethod method = lookupMethodInternal(painlessLookup, receiverClass, name, arity);
MethodHandle handle = method.handle;
int replaced = 0;
@ -283,8 +285,8 @@ public final class Def {
if (signature.charAt(0) == 'S') {
// the implementation is strongly typed, now that we know the interface type,
// we have everything.
filter = lookupReferenceInternal(definition,
lookup,
filter = lookupReferenceInternal(painlessLookup,
methodHandlesLookup,
interfaceType,
type,
call,
@ -294,13 +296,13 @@ public final class Def {
// this is dynamically based on the receiver type (and cached separately, underneath
// this cache). It won't blow up since we never nest here (just references)
MethodType nestedType = MethodType.methodType(interfaceType, captures);
CallSite nested = DefBootstrap.bootstrap(definition,
lookup,
CallSite nested = DefBootstrap.bootstrap(painlessLookup,
methodHandlesLookup,
call,
nestedType,
0,
DefBootstrap.REFERENCE,
Definition.ClassToName(interfaceType));
PainlessLookup.ClassToName(interfaceType));
filter = nested.dynamicInvoker();
} else {
throw new AssertionError();
@ -322,37 +324,37 @@ public final class Def {
* This is just like LambdaMetaFactory, only with a dynamic type. The interface type is known,
* so we simply need to lookup the matching implementation method based on receiver type.
*/
static MethodHandle lookupReference(Definition definition, Lookup lookup, String interfaceClass,
Class<?> receiverClass, String name) throws Throwable {
Class<?> interfaceType = definition.getJavaClassFromPainlessType(interfaceClass);
Method interfaceMethod = definition.getPainlessStructFromJavaClass(interfaceType).functionalMethod;
static MethodHandle lookupReference(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, String interfaceClass,
Class<?> receiverClass, String name) throws Throwable {
Class<?> interfaceType = painlessLookup.getJavaClassFromPainlessType(interfaceClass);
PainlessMethod interfaceMethod = painlessLookup.getPainlessStructFromJavaClass(interfaceType).functionalMethod;
if (interfaceMethod == null) {
throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface");
}
int arity = interfaceMethod.arguments.size();
Method implMethod = lookupMethodInternal(definition, receiverClass, name, arity);
return lookupReferenceInternal(definition, lookup, interfaceType, implMethod.owner.name,
PainlessMethod implMethod = lookupMethodInternal(painlessLookup, receiverClass, name, arity);
return lookupReferenceInternal(painlessLookup, methodHandlesLookup, interfaceType, implMethod.owner.name,
implMethod.name, receiverClass);
}
/** Returns a method handle to an implementation of clazz, given method reference signature. */
private static MethodHandle lookupReferenceInternal(Definition definition, Lookup lookup,
Class<?> clazz, String type, String call, Class<?>... captures)
private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup,
Class<?> clazz, String type, String call, Class<?>... captures)
throws Throwable {
final FunctionRef ref;
if ("this".equals(type)) {
// user written method
Method interfaceMethod = definition.getPainlessStructFromJavaClass(clazz).functionalMethod;
PainlessMethod interfaceMethod = painlessLookup.getPainlessStructFromJavaClass(clazz).functionalMethod;
if (interfaceMethod == null) {
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
"to [" + Definition.ClassToName(clazz) + "], not a functional interface");
"to [" + PainlessLookup.ClassToName(clazz) + "], not a functional interface");
}
int arity = interfaceMethod.arguments.size() + captures.length;
final MethodHandle handle;
try {
MethodHandle accessor = lookup.findStaticGetter(lookup.lookupClass(),
getUserFunctionHandleFieldName(call, arity),
MethodHandle.class);
MethodHandle accessor = methodHandlesLookup.findStaticGetter(methodHandlesLookup.lookupClass(),
getUserFunctionHandleFieldName(call, arity),
MethodHandle.class);
handle = (MethodHandle)accessor.invokeExact();
} catch (NoSuchFieldException | IllegalAccessException e) {
// is it a synthetic method? If we generated the method ourselves, be more helpful. It can only fail
@ -366,10 +368,10 @@ public final class Def {
ref = new FunctionRef(clazz, interfaceMethod, call, handle.type(), captures.length);
} else {
// whitelist lookup
ref = new FunctionRef(definition, clazz, type, call, captures.length);
ref = new FunctionRef(painlessLookup, clazz, type, call, captures.length);
}
final CallSite callSite = LambdaBootstrap.lambdaBootstrap(
lookup,
methodHandlesLookup,
ref.interfaceMethodName,
ref.factoryMethodType,
ref.interfaceMethodType,
@ -407,16 +409,16 @@ public final class Def {
* until it finds a matching whitelisted getter. If one is not found, it throws an exception.
* Otherwise it returns a handle to the matching getter.
* <p>
* @param definition the whitelist
* @param painlessLookup the whitelist
* @param receiverClass Class of the object to retrieve the field from.
* @param name Name of the field.
* @return pointer to matching field. never returns null.
* @throws IllegalArgumentException if no matching whitelisted field was found.
*/
static MethodHandle lookupGetter(Definition definition, Class<?> receiverClass, String name) {
static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class<?> receiverClass, String name) {
// first try whitelist
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
Struct struct = definition.getPainlessStructFromJavaClass(clazz);
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(clazz);
if (struct != null) {
MethodHandle handle = struct.getters.get(name);
@ -426,7 +428,7 @@ public final class Def {
}
for (final Class<?> iface : clazz.getInterfaces()) {
struct = definition.getPainlessStructFromJavaClass(iface);
struct = painlessLookup.getPainlessStructFromJavaClass(iface);
if (struct != null) {
MethodHandle handle = struct.getters.get(name);
@ -478,16 +480,16 @@ public final class Def {
* until it finds a matching whitelisted setter. If one is not found, it throws an exception.
* Otherwise it returns a handle to the matching setter.
* <p>
* @param definition the whitelist
* @param painlessLookup the whitelist
* @param receiverClass Class of the object to retrieve the field from.
* @param name Name of the field.
* @return pointer to matching field. never returns null.
* @throws IllegalArgumentException if no matching whitelisted field was found.
*/
static MethodHandle lookupSetter(Definition definition, Class<?> receiverClass, String name) {
static MethodHandle lookupSetter(PainlessLookup painlessLookup, Class<?> receiverClass, String name) {
// first try whitelist
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
Struct struct = definition.getPainlessStructFromJavaClass(clazz);
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(clazz);
if (struct != null) {
MethodHandle handle = struct.setters.get(name);
@ -497,7 +499,7 @@ public final class Def {
}
for (final Class<?> iface : clazz.getInterfaces()) {
struct = definition.getPainlessStructFromJavaClass(iface);
struct = painlessLookup.getPainlessStructFromJavaClass(iface);
if (struct != null) {
MethodHandle handle = struct.setters.get(name);
@ -592,14 +594,15 @@ public final class Def {
*/
@SuppressWarnings("unused") // iterator() methods are are actually used, javac just does not know :)
private static final class ArrayIteratorHelper {
private static final Lookup PRIV_LOOKUP = MethodHandles.lookup();
private static final MethodHandles.Lookup PRIVATE_METHOD_HANDLES_LOOKUP = MethodHandles.lookup();
private static final Map<Class<?>,MethodHandle> ARRAY_TYPE_MH_MAPPING = Collections.unmodifiableMap(
Stream.of(boolean[].class, byte[].class, short[].class, int[].class, long[].class,
char[].class, float[].class, double[].class, Object[].class)
.collect(Collectors.toMap(Function.identity(), type -> {
try {
return PRIV_LOOKUP.findStatic(PRIV_LOOKUP.lookupClass(), "iterator", MethodType.methodType(Iterator.class, type));
return PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(
PRIVATE_METHOD_HANDLES_LOOKUP.lookupClass(), "iterator", MethodType.methodType(Iterator.class, type));
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);
}
@ -860,14 +863,14 @@ public final class Def {
*/
@SuppressWarnings("unused") // normalizeIndex() methods are are actually used, javac just does not know :)
private static final class ArrayIndexNormalizeHelper {
private static final Lookup PRIV_LOOKUP = MethodHandles.lookup();
private static final MethodHandles.Lookup PRIVATE_METHOD_HANDLES_LOOKUP = MethodHandles.lookup();
private static final Map<Class<?>,MethodHandle> ARRAY_TYPE_MH_MAPPING = Collections.unmodifiableMap(
Stream.of(boolean[].class, byte[].class, short[].class, int[].class, long[].class,
char[].class, float[].class, double[].class, Object[].class)
.collect(Collectors.toMap(Function.identity(), type -> {
try {
return PRIV_LOOKUP.findStatic(PRIV_LOOKUP.lookupClass(), "normalizeIndex",
return PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(PRIVATE_METHOD_HANDLES_LOOKUP.lookupClass(), "normalizeIndex",
MethodType.methodType(int.class, type, int.class));
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);

View File

@ -20,11 +20,11 @@
package org.elasticsearch.painless;
import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.painless.lookup.PainlessLookup;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;
import java.lang.invoke.WrongMethodTypeException;
@ -104,20 +104,21 @@ public final class DefBootstrap {
/** maximum number of types before we go megamorphic */
static final int MAX_DEPTH = 5;
private final Definition definition;
private final Lookup lookup;
private final PainlessLookup painlessLookup;
private final MethodHandles.Lookup methodHandlesLookup;
private final String name;
private final int flavor;
private final Object[] args;
int depth; // pkg-protected for testing
PIC(Definition definition, Lookup lookup, String name, MethodType type, int initialDepth, int flavor, Object[] args) {
PIC(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup,
String name, MethodType type, int initialDepth, int flavor, Object[] args) {
super(type);
if (type.parameterType(0) != Object.class) {
throw new BootstrapMethodError("The receiver type (1st arg) of invokedynamic descriptor must be Object.");
}
this.definition = definition;
this.lookup = lookup;
this.painlessLookup = painlessLookup;
this.methodHandlesLookup = methodHandlesLookup;
this.name = name;
this.flavor = flavor;
this.args = args;
@ -144,11 +145,11 @@ public final class DefBootstrap {
private MethodHandle lookup(int flavor, String name, Class<?> receiver) throws Throwable {
switch(flavor) {
case METHOD_CALL:
return Def.lookupMethod(definition, lookup, type(), receiver, name, args);
return Def.lookupMethod(painlessLookup, methodHandlesLookup, type(), receiver, name, args);
case LOAD:
return Def.lookupGetter(definition, receiver, name);
return Def.lookupGetter(painlessLookup, receiver, name);
case STORE:
return Def.lookupSetter(definition, receiver, name);
return Def.lookupSetter(painlessLookup, receiver, name);
case ARRAY_LOAD:
return Def.lookupArrayLoad(receiver);
case ARRAY_STORE:
@ -156,7 +157,7 @@ public final class DefBootstrap {
case ITERATOR:
return Def.lookupIterator(receiver);
case REFERENCE:
return Def.lookupReference(definition, lookup, (String) args[0], receiver, name);
return Def.lookupReference(painlessLookup, methodHandlesLookup, (String) args[0], receiver, name);
case INDEX_NORMALIZE:
return Def.lookupIndexNormalize(receiver);
default: throw new AssertionError();
@ -216,17 +217,17 @@ public final class DefBootstrap {
private static final MethodHandle FALLBACK;
private static final MethodHandle MEGAMORPHIC_LOOKUP;
static {
final Lookup lookup = MethodHandles.lookup();
final Lookup publicLookup = MethodHandles.publicLookup();
final MethodHandles.Lookup methodHandlesLookup = MethodHandles.lookup();
final MethodHandles.Lookup publicMethodHandlesLookup = MethodHandles.publicLookup();
try {
CHECK_CLASS = lookup.findStatic(lookup.lookupClass(), "checkClass",
MethodType.methodType(boolean.class, Class.class, Object.class));
FALLBACK = lookup.findVirtual(lookup.lookupClass(), "fallback",
CHECK_CLASS = methodHandlesLookup.findStatic(methodHandlesLookup.lookupClass(), "checkClass",
MethodType.methodType(boolean.class, Class.class, Object.class));
FALLBACK = methodHandlesLookup.findVirtual(methodHandlesLookup.lookupClass(), "fallback",
MethodType.methodType(Object.class, Object[].class));
MethodHandle mh = publicLookup.findVirtual(ClassValue.class, "get",
MethodHandle mh = publicMethodHandlesLookup.findVirtual(ClassValue.class, "get",
MethodType.methodType(Object.class, Class.class));
mh = MethodHandles.filterArguments(mh, 1,
publicLookup.findVirtual(Object.class, "getClass", MethodType.methodType(Class.class)));
publicMethodHandlesLookup.findVirtual(Object.class, "getClass", MethodType.methodType(Class.class)));
MEGAMORPHIC_LOOKUP = mh.asType(mh.type().changeReturnType(MethodHandle.class));
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);
@ -402,16 +403,16 @@ public final class DefBootstrap {
private static final MethodHandle CHECK_BOTH;
private static final MethodHandle FALLBACK;
static {
final Lookup lookup = MethodHandles.lookup();
final MethodHandles.Lookup methodHandlesLookup = MethodHandles.lookup();
try {
CHECK_LHS = lookup.findStatic(lookup.lookupClass(), "checkLHS",
MethodType.methodType(boolean.class, Class.class, Object.class));
CHECK_RHS = lookup.findStatic(lookup.lookupClass(), "checkRHS",
MethodType.methodType(boolean.class, Class.class, Class.class, Object.class, Object.class));
CHECK_BOTH = lookup.findStatic(lookup.lookupClass(), "checkBoth",
MethodType.methodType(boolean.class, Class.class, Class.class, Object.class, Object.class));
FALLBACK = lookup.findVirtual(lookup.lookupClass(), "fallback",
MethodType.methodType(Object.class, Object[].class));
CHECK_LHS = methodHandlesLookup.findStatic(methodHandlesLookup.lookupClass(), "checkLHS",
MethodType.methodType(boolean.class, Class.class, Object.class));
CHECK_RHS = methodHandlesLookup.findStatic(methodHandlesLookup.lookupClass(), "checkRHS",
MethodType.methodType(boolean.class, Class.class, Class.class, Object.class, Object.class));
CHECK_BOTH = methodHandlesLookup.findStatic(methodHandlesLookup.lookupClass(), "checkBoth",
MethodType.methodType(boolean.class, Class.class, Class.class, Object.class, Object.class));
FALLBACK = methodHandlesLookup.findVirtual(methodHandlesLookup.lookupClass(), "fallback",
MethodType.methodType(Object.class, Object[].class));
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);
}
@ -427,12 +428,12 @@ public final class DefBootstrap {
* <li>{@code flavor}: type of dynamic call it is (and which part of whitelist to look at).
* <li>{@code args}: flavor-specific args.
* </ul>
* And we take the {@link Definition} used to compile the script for whitelist checking.
* And we take the {@link PainlessLookup} used to compile the script for whitelist checking.
* <p>
* see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokedynamic
*/
public static CallSite bootstrap(Definition definition, Lookup lookup, String name, MethodType type, int initialDepth, int flavor,
Object... args) {
public static CallSite bootstrap(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, String name,
MethodType type, int initialDepth, int flavor, Object... args) {
// validate arguments
switch(flavor) {
// "function-call" like things get a polymorphic cache
@ -451,7 +452,7 @@ public final class DefBootstrap {
if (args.length != numLambdas + 1) {
throw new BootstrapMethodError("Illegal number of parameters: expected " + numLambdas + " references");
}
return new PIC(definition, lookup, name, type, initialDepth, flavor, args);
return new PIC(painlessLookup, methodHandlesLookup, name, type, initialDepth, flavor, args);
case LOAD:
case STORE:
case ARRAY_LOAD:
@ -461,7 +462,7 @@ public final class DefBootstrap {
if (args.length > 0) {
throw new BootstrapMethodError("Illegal static bootstrap parameters for flavor: " + flavor);
}
return new PIC(definition, lookup, name, type, initialDepth, flavor, args);
return new PIC(painlessLookup, methodHandlesLookup, name, type, initialDepth, flavor, args);
case REFERENCE:
if (args.length != 1) {
throw new BootstrapMethodError("Invalid number of parameters for reference call");
@ -469,7 +470,7 @@ public final class DefBootstrap {
if (args[0] instanceof String == false) {
throw new BootstrapMethodError("Illegal parameter for reference call: " + args[0]);
}
return new PIC(definition, lookup, name, type, initialDepth, flavor, args);
return new PIC(painlessLookup, methodHandlesLookup, name, type, initialDepth, flavor, args);
// operators get monomorphic cache, with a generic impl for a fallback
case UNARY_OPERATOR:

View File

@ -21,7 +21,6 @@ package org.elasticsearch.painless;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.util.Collections;
import java.util.HashMap;
@ -1070,7 +1069,7 @@ public class DefMath {
}
}
private static final Lookup PRIV_LOOKUP = MethodHandles.lookup();
private static final MethodHandles.Lookup PRIVATE_METHOD_HANDLES_LOOKUP = MethodHandles.lookup();
private static final Map<Class<?>,Map<String,MethodHandle>> TYPE_OP_MAPPING = Collections.unmodifiableMap(
Stream.of(boolean.class, int.class, long.class, float.class, double.class, Object.class)
@ -1081,26 +1080,26 @@ public class DefMath {
MethodType binary = MethodType.methodType(type, type, type);
MethodType comparison = MethodType.methodType(boolean.class, type, type);
MethodType shift = MethodType.methodType(type, type, long.class);
Class<?> clazz = PRIV_LOOKUP.lookupClass();
map.put("not", PRIV_LOOKUP.findStatic(clazz, "not", unary));
map.put("neg", PRIV_LOOKUP.findStatic(clazz, "neg", unary));
map.put("plus", PRIV_LOOKUP.findStatic(clazz, "plus", unary));
map.put("mul", PRIV_LOOKUP.findStatic(clazz, "mul", binary));
map.put("div", PRIV_LOOKUP.findStatic(clazz, "div", binary));
map.put("rem", PRIV_LOOKUP.findStatic(clazz, "rem", binary));
map.put("add", PRIV_LOOKUP.findStatic(clazz, "add", binary));
map.put("sub", PRIV_LOOKUP.findStatic(clazz, "sub", binary));
map.put("and", PRIV_LOOKUP.findStatic(clazz, "and", binary));
map.put("or", PRIV_LOOKUP.findStatic(clazz, "or", binary));
map.put("xor", PRIV_LOOKUP.findStatic(clazz, "xor", binary));
map.put("eq", PRIV_LOOKUP.findStatic(clazz, "eq", comparison));
map.put("lt", PRIV_LOOKUP.findStatic(clazz, "lt", comparison));
map.put("lte", PRIV_LOOKUP.findStatic(clazz, "lte", comparison));
map.put("gt", PRIV_LOOKUP.findStatic(clazz, "gt", comparison));
map.put("gte", PRIV_LOOKUP.findStatic(clazz, "gte", comparison));
map.put("lsh", PRIV_LOOKUP.findStatic(clazz, "lsh", shift));
map.put("rsh", PRIV_LOOKUP.findStatic(clazz, "rsh", shift));
map.put("ush", PRIV_LOOKUP.findStatic(clazz, "ush", shift));
Class<?> clazz = PRIVATE_METHOD_HANDLES_LOOKUP.lookupClass();
map.put("not", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "not", unary));
map.put("neg", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "neg", unary));
map.put("plus", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "plus", unary));
map.put("mul", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "mul", binary));
map.put("div", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "div", binary));
map.put("rem", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "rem", binary));
map.put("add", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "add", binary));
map.put("sub", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "sub", binary));
map.put("and", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "and", binary));
map.put("or", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "or", binary));
map.put("xor", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "xor", binary));
map.put("eq", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "eq", comparison));
map.put("lt", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "lt", comparison));
map.put("lte", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "lte", comparison));
map.put("gt", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "gt", comparison));
map.put("gte", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "gte", comparison));
map.put("lsh", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "lsh", shift));
map.put("rsh", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "rsh", shift));
map.put("ush", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "ush", shift));
return map;
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);
@ -1188,14 +1187,14 @@ public class DefMath {
private static final MethodHandle DYNAMIC_CAST;
private static final MethodHandle DYNAMIC_RECEIVER_CAST;
static {
final Lookup lookup = MethodHandles.lookup();
final MethodHandles.Lookup methodHandlesLookup = MethodHandles.lookup();
try {
DYNAMIC_CAST = lookup.findStatic(lookup.lookupClass(),
"dynamicCast",
MethodType.methodType(Object.class, Class.class, Object.class));
DYNAMIC_RECEIVER_CAST = lookup.findStatic(lookup.lookupClass(),
"dynamicReceiverCast",
MethodType.methodType(Object.class, Object.class, Object.class));
DYNAMIC_CAST = methodHandlesLookup.findStatic(methodHandlesLookup.lookupClass(),
"dynamicCast",
MethodType.methodType(Object.class, Class.class, Object.class));
DYNAMIC_RECEIVER_CAST = methodHandlesLookup.findStatic(methodHandlesLookup.lookupClass(),
"dynamicReceiverCast",
MethodType.methodType(Object.class, Object.class, Object.class));
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);
}

View File

@ -19,7 +19,10 @@
package org.elasticsearch.painless;
import org.elasticsearch.painless.Definition.Method;
import org.elasticsearch.painless.lookup.PainlessClass;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.lookup.PainlessMethodKey;
import org.objectweb.asm.Type;
import java.lang.invoke.MethodType;
@ -55,9 +58,9 @@ public class FunctionRef {
public final MethodType delegateMethodType;
/** interface method */
public final Method interfaceMethod;
public final PainlessMethod interfaceMethod;
/** delegate method */
public final Method delegateMethod;
public final PainlessMethod delegateMethod;
/** factory method type descriptor */
public final String factoryDescriptor;
@ -71,15 +74,15 @@ public class FunctionRef {
/**
* Creates a new FunctionRef, which will resolve {@code type::call} from the whitelist.
* @param definition the whitelist against which this script is being compiled
* @param painlessLookup the whitelist against which this script is being compiled
* @param expected functional interface type to implement.
* @param type the left hand side of a method reference expression
* @param call the right hand side of a method reference expression
* @param numCaptures number of captured arguments
*/
public FunctionRef(Definition definition, Class<?> expected, String type, String call, int numCaptures) {
this(expected, definition.getPainlessStructFromJavaClass(expected).functionalMethod,
lookup(definition, expected, type, call, numCaptures > 0), numCaptures);
public FunctionRef(PainlessLookup painlessLookup, Class<?> expected, String type, String call, int numCaptures) {
this(expected, painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod,
lookup(painlessLookup, expected, type, call, numCaptures > 0), numCaptures);
}
/**
@ -89,7 +92,7 @@ public class FunctionRef {
* @param delegateMethod implementation method
* @param numCaptures number of captured arguments
*/
public FunctionRef(Class<?> expected, Method interfaceMethod, Method delegateMethod, int numCaptures) {
public FunctionRef(Class<?> expected, PainlessMethod interfaceMethod, PainlessMethod delegateMethod, int numCaptures) {
MethodType delegateMethodType = delegateMethod.getMethodType();
interfaceMethodName = interfaceMethod.name;
@ -135,7 +138,7 @@ public class FunctionRef {
* It is for runtime use only.
*/
public FunctionRef(Class<?> expected,
Method interfaceMethod, String delegateMethodName, MethodType delegateMethodType, int numCaptures) {
PainlessMethod interfaceMethod, String delegateMethodName, MethodType delegateMethodType, int numCaptures) {
interfaceMethodName = interfaceMethod.name;
factoryMethodType = MethodType.methodType(expected,
delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount()));
@ -158,25 +161,25 @@ public class FunctionRef {
/**
* Looks up {@code type::call} from the whitelist, and returns a matching method.
*/
private static Definition.Method lookup(Definition definition, Class<?> expected,
String type, String call, boolean receiverCaptured) {
private static PainlessMethod lookup(PainlessLookup painlessLookup, Class<?> expected,
String type, String call, boolean receiverCaptured) {
// check its really a functional interface
// for e.g. Comparable
Method method = definition.getPainlessStructFromJavaClass(expected).functionalMethod;
PainlessMethod method = painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod;
if (method == null) {
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
"to [" + Definition.ClassToName(expected) + "], not a functional interface");
"to [" + PainlessLookup.ClassToName(expected) + "], not a functional interface");
}
// lookup requested method
Definition.Struct struct = definition.getPainlessStructFromJavaClass(definition.getJavaClassFromPainlessType(type));
final Definition.Method impl;
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(painlessLookup.getJavaClassFromPainlessType(type));
final PainlessMethod impl;
// ctor ref
if ("new".equals(call)) {
impl = struct.constructors.get(new Definition.MethodKey("<init>", method.arguments.size()));
impl = struct.constructors.get(new PainlessMethodKey("<init>", method.arguments.size()));
} else {
// look for a static impl first
Definition.Method staticImpl = struct.staticMethods.get(new Definition.MethodKey(call, method.arguments.size()));
PainlessMethod staticImpl = struct.staticMethods.get(new PainlessMethodKey(call, method.arguments.size()));
if (staticImpl == null) {
// otherwise a virtual impl
final int arity;
@ -187,7 +190,7 @@ public class FunctionRef {
// receiver passed
arity = method.arguments.size() - 1;
}
impl = struct.methods.get(new Definition.MethodKey(call, arity));
impl = struct.methods.get(new PainlessMethodKey(call, arity));
} else {
impl = staticImpl;
}

View File

@ -19,8 +19,9 @@
package org.elasticsearch.painless;
import org.elasticsearch.painless.Definition.Method;
import org.elasticsearch.painless.Definition.MethodKey;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.lookup.PainlessMethodKey;
import org.elasticsearch.painless.ScriptClassInfo.MethodArgument;
import java.util.Arrays;
@ -59,7 +60,7 @@ public final class Locals {
*/
public static Locals newLambdaScope(Locals programScope, Class<?> returnType, List<Parameter> parameters,
int captureCount, int maxLoopCounter) {
Locals locals = new Locals(programScope, programScope.definition, returnType, KEYWORDS);
Locals locals = new Locals(programScope, programScope.painlessLookup, returnType, KEYWORDS);
for (int i = 0; i < parameters.size(); i++) {
Parameter parameter = parameters.get(i);
// TODO: allow non-captures to be r/w:
@ -78,7 +79,7 @@ public final class Locals {
/** Creates a new function scope inside the current scope */
public static Locals newFunctionScope(Locals programScope, Class<?> returnType, List<Parameter> parameters, int maxLoopCounter) {
Locals locals = new Locals(programScope, programScope.definition, returnType, KEYWORDS);
Locals locals = new Locals(programScope, programScope.painlessLookup, returnType, KEYWORDS);
for (Parameter parameter : parameters) {
locals.addVariable(parameter.location, parameter.clazz, parameter.name, false);
}
@ -92,7 +93,7 @@ public final class Locals {
/** Creates a new main method scope */
public static Locals newMainMethodScope(ScriptClassInfo scriptClassInfo, Locals programScope, int maxLoopCounter) {
Locals locals = new Locals(
programScope, programScope.definition, scriptClassInfo.getExecuteMethodReturnType(), KEYWORDS);
programScope, programScope.painlessLookup, scriptClassInfo.getExecuteMethodReturnType(), KEYWORDS);
// This reference. Internal use only.
locals.defineVariable(null, Object.class, THIS, true);
@ -109,9 +110,9 @@ public final class Locals {
}
/** Creates a new program scope: the list of methods. It is the parent for all methods */
public static Locals newProgramScope(Definition definition, Collection<Method> methods) {
Locals locals = new Locals(null, definition, null, null);
for (Method method : methods) {
public static Locals newProgramScope(PainlessLookup painlessLookup, Collection<PainlessMethod> methods) {
Locals locals = new Locals(null, painlessLookup, null, null);
for (PainlessMethod method : methods) {
locals.addMethod(method);
}
return locals;
@ -142,8 +143,8 @@ public final class Locals {
}
/** Looks up a method. Returns null if the method does not exist. */
public Method getMethod(MethodKey key) {
Method method = lookupMethod(key);
public PainlessMethod getMethod(PainlessMethodKey key) {
PainlessMethod method = lookupMethod(key);
if (method != null) {
return method;
}
@ -179,14 +180,14 @@ public final class Locals {
}
/** Whitelist against which this script is being compiled. */
public Definition getDefinition() {
return definition;
public PainlessLookup getPainlessLookup() {
return painlessLookup;
}
///// private impl
/** Whitelist against which this script is being compiled. */
private final Definition definition;
private final PainlessLookup painlessLookup;
// parent scope
private final Locals parent;
// return type of this scope
@ -198,21 +199,21 @@ public final class Locals {
// variable name -> variable
private Map<String,Variable> variables;
// method name+arity -> methods
private Map<MethodKey,Method> methods;
private Map<PainlessMethodKey,PainlessMethod> methods;
/**
* Create a new Locals
*/
private Locals(Locals parent) {
this(parent, parent.definition, parent.returnType, parent.keywords);
this(parent, parent.painlessLookup, parent.returnType, parent.keywords);
}
/**
* Create a new Locals with specified return type
*/
private Locals(Locals parent, Definition definition, Class<?> returnType, Set<String> keywords) {
private Locals(Locals parent, PainlessLookup painlessLookup, Class<?> returnType, Set<String> keywords) {
this.parent = parent;
this.definition = definition;
this.painlessLookup = painlessLookup;
this.returnType = returnType;
this.keywords = keywords;
if (parent == null) {
@ -236,7 +237,7 @@ public final class Locals {
}
/** Looks up a method at this scope only. Returns null if the method does not exist. */
private Method lookupMethod(MethodKey key) {
private PainlessMethod lookupMethod(PainlessMethodKey key) {
if (methods == null) {
return null;
}
@ -255,11 +256,11 @@ public final class Locals {
return variable;
}
private void addMethod(Method method) {
private void addMethod(PainlessMethod method) {
if (methods == null) {
methods = new HashMap<>();
}
methods.put(new MethodKey(method.name, method.arguments.size()), method);
methods.put(new PainlessMethodKey(method.name, method.arguments.size()), method);
// TODO: check result
}
@ -291,7 +292,7 @@ public final class Locals {
@Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append("Variable[type=").append(Definition.ClassToName(clazz));
b.append("Variable[type=").append(PainlessLookup.ClassToName(clazz));
b.append(",name=").append(name);
b.append(",slot=").append(slot);
if (readonly) {

View File

@ -19,8 +19,8 @@
package org.elasticsearch.painless;
import org.elasticsearch.painless.Definition.Cast;
import org.elasticsearch.painless.Definition.def;
import org.elasticsearch.painless.lookup.PainlessCast;
import org.elasticsearch.painless.lookup.PainlessLookup.def;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
@ -130,7 +130,7 @@ public final class MethodWriter extends GeneratorAdapter {
mark(end);
}
public void writeCast(Cast cast) {
public void writeCast(PainlessCast cast) {
if (cast != null) {
if (cast.from == char.class && cast.to == String.class) {
invokeStatic(UTILITY_TYPE, CHAR_TO_STRING);

View File

@ -20,6 +20,8 @@
package org.elasticsearch.painless;
import org.elasticsearch.painless.api.Debug;
import org.elasticsearch.painless.lookup.PainlessClass;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.script.ScriptException;
import java.util.List;
@ -46,7 +48,7 @@ public class PainlessExplainError extends Error {
/**
* Headers to be added to the {@link ScriptException} for structured rendering.
*/
public Map<String, List<String>> getHeaders(Definition definition) {
public Map<String, List<String>> getHeaders(PainlessLookup painlessLookup) {
Map<String, List<String>> headers = new TreeMap<>();
String toString = "null";
String javaClassName = null;
@ -54,7 +56,7 @@ public class PainlessExplainError extends Error {
if (objectToExplain != null) {
toString = objectToExplain.toString();
javaClassName = objectToExplain.getClass().getName();
Definition.Struct struct = definition.getPainlessStructFromJavaClass(objectToExplain.getClass());
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(objectToExplain.getClass());
if (struct != null) {
painlessClassName = struct.name;
}

View File

@ -24,6 +24,7 @@ import org.elasticsearch.SpecialPermission;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.painless.Compiler.Loader;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.spi.Whitelist;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptContext;
@ -101,9 +102,9 @@ public final class PainlessScriptEngine extends AbstractComponent implements Scr
for (Map.Entry<ScriptContext<?>, List<Whitelist>> entry : contexts.entrySet()) {
ScriptContext<?> context = entry.getKey();
if (context.instanceClazz.equals(SearchScript.class) || context.instanceClazz.equals(ExecutableScript.class)) {
contextsToCompilers.put(context, new Compiler(GenericElasticsearchScript.class, new Definition(entry.getValue())));
contextsToCompilers.put(context, new Compiler(GenericElasticsearchScript.class, new PainlessLookup(entry.getValue())));
} else {
contextsToCompilers.put(context, new Compiler(context.instanceClazz, new Definition(entry.getValue())));
contextsToCompilers.put(context, new Compiler(context.instanceClazz, new PainlessLookup(entry.getValue())));
}
}

View File

@ -19,6 +19,8 @@
package org.elasticsearch.painless;
import org.elasticsearch.painless.lookup.PainlessLookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@ -42,7 +44,7 @@ public class ScriptClassInfo {
private final List<org.objectweb.asm.commons.Method> getMethods;
private final List<Class<?>> getReturns;
public ScriptClassInfo(Definition definition, Class<?> baseClass) {
public ScriptClassInfo(PainlessLookup painlessLookup, Class<?> baseClass) {
this.baseClass = baseClass;
// Find the main method and the uses$argName methods
@ -68,8 +70,9 @@ public class ScriptClassInfo {
}
if (m.getName().startsWith("get") && m.getName().equals("getClass") == false && Modifier.isStatic(m.getModifiers()) == false) {
getReturns.add(
definitionTypeForClass(definition, m.getReturnType(), componentType -> "[" + m.getName() + "] has unknown return type ["
+ componentType.getName() + "]. Painless can only support getters with return types that are whitelisted."));
definitionTypeForClass(painlessLookup, m.getReturnType(), componentType -> "[" + m.getName() + "] has unknown return " +
"type [" + componentType.getName() + "]. Painless can only support getters with return types that are " +
"whitelisted."));
getMethods.add(new org.objectweb.asm.commons.Method(m.getName(),
MethodType.methodType(m.getReturnType()).toMethodDescriptorString()));
@ -78,7 +81,7 @@ public class ScriptClassInfo {
}
MethodType methodType = MethodType.methodType(executeMethod.getReturnType(), executeMethod.getParameterTypes());
this.executeMethod = new org.objectweb.asm.commons.Method(executeMethod.getName(), methodType.toMethodDescriptorString());
executeMethodReturnType = definitionTypeForClass(definition, executeMethod.getReturnType(),
executeMethodReturnType = definitionTypeForClass(painlessLookup, executeMethod.getReturnType(),
componentType -> "Painless can only implement execute methods returning a whitelisted type but [" + baseClass.getName()
+ "#execute] returns [" + componentType.getName() + "] which isn't whitelisted.");
@ -91,7 +94,7 @@ public class ScriptClassInfo {
+ baseClass.getName() + "#execute] takes [1] argument.");
}
for (int arg = 0; arg < types.length; arg++) {
arguments.add(methodArgument(definition, types[arg], argumentNamesConstant[arg]));
arguments.add(methodArgument(painlessLookup, types[arg], argumentNamesConstant[arg]));
}
this.executeArguments = unmodifiableList(arguments);
this.needsMethods = unmodifiableList(needsMethods);
@ -171,22 +174,22 @@ public class ScriptClassInfo {
}
}
private MethodArgument methodArgument(Definition definition, Class<?> clazz, String argName) {
Class<?> defClass = definitionTypeForClass(definition, clazz, componentType -> "[" + argName + "] is of unknown type ["
private MethodArgument methodArgument(PainlessLookup painlessLookup, Class<?> clazz, String argName) {
Class<?> defClass = definitionTypeForClass(painlessLookup, clazz, componentType -> "[" + argName + "] is of unknown type ["
+ componentType.getName() + ". Painless interfaces can only accept arguments that are of whitelisted types.");
return new MethodArgument(defClass, argName);
}
private static Class<?> definitionTypeForClass(Definition definition, Class<?> type,
Function<Class<?>, String> unknownErrorMessageSource) {
type = Definition.ObjectClassTodefClass(type);
private static Class<?> definitionTypeForClass(PainlessLookup painlessLookup, Class<?> type,
Function<Class<?>, String> unknownErrorMessageSource) {
type = PainlessLookup.ObjectClassTodefClass(type);
Class<?> componentType = type;
while (componentType.isArray()) {
componentType = componentType.getComponentType();
}
if (definition.getPainlessStructFromJavaClass(componentType) == null) {
if (painlessLookup.getPainlessStructFromJavaClass(componentType) == null) {
throw new IllegalArgumentException(unknownErrorMessageSource.apply(componentType));
}

View File

@ -20,6 +20,7 @@
package org.elasticsearch.painless;
import org.elasticsearch.painless.api.Augmentation;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.script.ScriptException;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
@ -74,12 +75,12 @@ public final class WriterConstants {
public static final Type STACK_OVERFLOW_ERROR_TYPE = Type.getType(StackOverflowError.class);
public static final Type EXCEPTION_TYPE = Type.getType(Exception.class);
public static final Type PAINLESS_EXPLAIN_ERROR_TYPE = Type.getType(PainlessExplainError.class);
public static final Method PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD = getAsmMethod(Map.class, "getHeaders", Definition.class);
public static final Method PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD = getAsmMethod(Map.class, "getHeaders", PainlessLookup.class);
public static final Type OBJECT_TYPE = Type.getType(Object.class);
public static final Type BITSET_TYPE = Type.getType(BitSet.class);
public static final Type DEFINITION_TYPE = Type.getType(Definition.class);
public static final Type DEFINITION_TYPE = Type.getType(PainlessLookup.class);
public static final Type COLLECTIONS_TYPE = Type.getType(Collections.class);
public static final Method EMPTY_MAP_METHOD = getAsmMethod(Map.class, "emptyMap");
@ -103,10 +104,10 @@ public final class WriterConstants {
public static final Type AUGMENTATION_TYPE = Type.getType(Augmentation.class);
/**
* A Method instance for {@linkplain Pattern#compile}. This isn't available from Definition because we intentionally don't add it there
* so that the script can't create regexes without this syntax. Essentially, our static regex syntax has a monopoly on building regexes
* because it can do it statically. This is both faster and prevents the script from doing something super slow like building a regex
* per time it is run.
* A Method instance for {@linkplain Pattern#compile}. This isn't available from PainlessLookup because we intentionally don't add it
* there so that the script can't create regexes without this syntax. Essentially, our static regex syntax has a monopoly on building
* regexes because it can do it statically. This is both faster and prevents the script from doing something super slow like building a
* regex per time it is run.
*/
public static final Method PATTERN_COMPILE = getAsmMethod(Pattern.class, "compile", String.class, int.class);
public static final Method PATTERN_MATCHER = getAsmMethod(Matcher.class, "matcher", CharSequence.class);
@ -118,7 +119,7 @@ public final class WriterConstants {
static final Handle DEF_BOOTSTRAP_HANDLE = new Handle(Opcodes.H_INVOKESTATIC, CLASS_TYPE.getInternalName(), "$bootstrapDef",
DEF_BOOTSTRAP_METHOD.getDescriptor(), false);
public static final Type DEF_BOOTSTRAP_DELEGATE_TYPE = Type.getType(DefBootstrap.class);
public static final Method DEF_BOOTSTRAP_DELEGATE_METHOD = getAsmMethod(CallSite.class, "bootstrap", Definition.class,
public static final Method DEF_BOOTSTRAP_DELEGATE_METHOD = getAsmMethod(CallSite.class, "bootstrap", PainlessLookup.class,
MethodHandles.Lookup.class, String.class, MethodType.class, int.class, int.class, Object[].class);
public static final Type DEF_UTIL_TYPE = Type.getType(Def.class);

View File

@ -23,7 +23,7 @@ import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.LexerNoViableAltException;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.misc.Interval;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.Location;
/**
@ -39,14 +39,14 @@ import org.elasticsearch.painless.Location;
*/
final class EnhancedPainlessLexer extends PainlessLexer {
private final String sourceName;
private final Definition definition;
private final PainlessLookup painlessLookup;
private Token current = null;
EnhancedPainlessLexer(CharStream charStream, String sourceName, Definition definition) {
EnhancedPainlessLexer(CharStream charStream, String sourceName, PainlessLookup painlessLookup) {
super(charStream);
this.sourceName = sourceName;
this.definition = definition;
this.painlessLookup = painlessLookup;
}
@Override
@ -75,7 +75,7 @@ final class EnhancedPainlessLexer extends PainlessLexer {
@Override
protected boolean isType(String name) {
return definition.isSimplePainlessType(name);
return painlessLookup.isSimplePainlessType(name);
}
@Override

View File

@ -29,7 +29,7 @@ import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.atn.PredictionMode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.elasticsearch.painless.CompilerSettings;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.Operation;
@ -174,9 +174,9 @@ import java.util.List;
public final class Walker extends PainlessParserBaseVisitor<ANode> {
public static SSource buildPainlessTree(ScriptClassInfo mainMethod, MainMethodReserved reserved, String sourceName,
String sourceText, CompilerSettings settings, Definition definition,
String sourceText, CompilerSettings settings, PainlessLookup painlessLookup,
Printer debugStream) {
return new Walker(mainMethod, reserved, sourceName, sourceText, settings, definition, debugStream).source;
return new Walker(mainMethod, reserved, sourceName, sourceText, settings, painlessLookup, debugStream).source;
}
private final ScriptClassInfo scriptClassInfo;
@ -185,14 +185,14 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
private final Printer debugStream;
private final String sourceName;
private final String sourceText;
private final Definition definition;
private final PainlessLookup painlessLookup;
private final Deque<Reserved> reserved = new ArrayDeque<>();
private final Globals globals;
private int syntheticCounter = 0;
private Walker(ScriptClassInfo scriptClassInfo, MainMethodReserved reserved, String sourceName, String sourceText,
CompilerSettings settings, Definition definition, Printer debugStream) {
CompilerSettings settings, PainlessLookup painlessLookup, Printer debugStream) {
this.scriptClassInfo = scriptClassInfo;
this.reserved.push(reserved);
this.debugStream = debugStream;
@ -200,13 +200,13 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
this.sourceName = Location.computeSourceName(sourceName);
this.sourceText = sourceText;
this.globals = new Globals(new BitSet(sourceText.length()));
this.definition = definition;
this.painlessLookup = painlessLookup;
this.source = (SSource)visit(buildAntlrTree(sourceText));
}
private SourceContext buildAntlrTree(String source) {
ANTLRInputStream stream = new ANTLRInputStream(source);
PainlessLexer lexer = new EnhancedPainlessLexer(stream, sourceName, definition);
PainlessLexer lexer = new EnhancedPainlessLexer(stream, sourceName, painlessLookup);
PainlessParser parser = new PainlessParser(new CommonTokenStream(lexer));
ParserErrorStrategy strategy = new ParserErrorStrategy(sourceName);

View File

@ -0,0 +1,67 @@
/*
* 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.lookup;
public class PainlessCast {
/** Create a standard cast with no boxing/unboxing. */
public static PainlessCast standard(Class<?> from, Class<?> to, boolean explicit) {
return new PainlessCast(from, to, explicit, null, null, null, null);
}
/** Create a cast where the from type will be unboxed, and then the cast will be performed. */
public static PainlessCast unboxFrom(Class<?> from, Class<?> to, boolean explicit, Class<?> unboxFrom) {
return new PainlessCast(from, to, explicit, unboxFrom, null, null, null);
}
/** Create a cast where the to type will be unboxed, and then the cast will be performed. */
public static PainlessCast unboxTo(Class<?> from, Class<?> to, boolean explicit, Class<?> unboxTo) {
return new PainlessCast(from, to, explicit, null, unboxTo, null, null);
}
/** Create a cast where the from type will be boxed, and then the cast will be performed. */
public static PainlessCast boxFrom(Class<?> from, Class<?> to, boolean explicit, Class<?> boxFrom) {
return new PainlessCast(from, to, explicit, null, null, boxFrom, null);
}
/** Create a cast where the to type will be boxed, and then the cast will be performed. */
public static PainlessCast boxTo(Class<?> from, Class<?> to, boolean explicit, Class<?> boxTo) {
return new PainlessCast(from, to, explicit, null, null, null, boxTo);
}
public final Class<?> from;
public final Class<?> to;
public final boolean explicit;
public final Class<?> unboxFrom;
public final Class<?> unboxTo;
public final Class<?> boxFrom;
public final Class<?> boxTo;
private PainlessCast(Class<?> from, Class<?> to, boolean explicit,
Class<?> unboxFrom, Class<?> unboxTo, Class<?> boxFrom, Class<?> boxTo) {
this.from = from;
this.to = to;
this.explicit = explicit;
this.unboxFrom = unboxFrom;
this.unboxTo = unboxTo;
this.boxFrom = boxFrom;
this.boxTo = boxTo;
}
}

View File

@ -0,0 +1,103 @@
/*
* 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.lookup;
import java.lang.invoke.MethodHandle;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public final class PainlessClass {
public final String name;
public final Class<?> clazz;
public final org.objectweb.asm.Type type;
public final Map<PainlessMethodKey, PainlessMethod> constructors;
public final Map<PainlessMethodKey, PainlessMethod> staticMethods;
public final Map<PainlessMethodKey, PainlessMethod> methods;
public final Map<String, PainlessField> staticMembers;
public final Map<String, PainlessField> members;
public final Map<String, MethodHandle> getters;
public final Map<String, MethodHandle> setters;
public final PainlessMethod functionalMethod;
PainlessClass(String name, Class<?> clazz, org.objectweb.asm.Type type) {
this.name = name;
this.clazz = clazz;
this.type = type;
constructors = new HashMap<>();
staticMethods = new HashMap<>();
methods = new HashMap<>();
staticMembers = new HashMap<>();
members = new HashMap<>();
getters = new HashMap<>();
setters = new HashMap<>();
functionalMethod = null;
}
private PainlessClass(PainlessClass struct, PainlessMethod functionalMethod) {
name = struct.name;
clazz = struct.clazz;
type = struct.type;
constructors = Collections.unmodifiableMap(struct.constructors);
staticMethods = Collections.unmodifiableMap(struct.staticMethods);
methods = Collections.unmodifiableMap(struct.methods);
staticMembers = Collections.unmodifiableMap(struct.staticMembers);
members = Collections.unmodifiableMap(struct.members);
getters = Collections.unmodifiableMap(struct.getters);
setters = Collections.unmodifiableMap(struct.setters);
this.functionalMethod = functionalMethod;
}
public PainlessClass freeze(PainlessMethod functionalMethod) {
return new PainlessClass(this, functionalMethod);
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null || getClass() != object.getClass()) {
return false;
}
PainlessClass struct = (PainlessClass)object;
return name.equals(struct.name);
}
@Override
public int hashCode() {
return name.hashCode();
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.lookup;
import java.lang.invoke.MethodHandle;
public final class PainlessField {
public final String name;
public final PainlessClass owner;
public final Class<?> clazz;
public final String javaName;
public final int modifiers;
public final MethodHandle getter;
public final MethodHandle setter;
PainlessField(String name, String javaName, PainlessClass owner, Class<?> clazz, int modifiers,
MethodHandle getter, MethodHandle setter) {
this.name = name;
this.javaName = javaName;
this.owner = owner;
this.clazz = clazz;
this.modifiers = modifiers;
this.getter = getter;
this.setter = setter;
}
}

View File

@ -17,15 +17,17 @@
* under the License.
*/
package org.elasticsearch.painless;
package org.elasticsearch.painless.lookup;
import org.elasticsearch.painless.spi.Whitelist;
import org.objectweb.asm.Opcodes;
import org.elasticsearch.painless.spi.WhitelistClass;
import org.elasticsearch.painless.spi.WhitelistConstructor;
import org.elasticsearch.painless.spi.WhitelistField;
import org.elasticsearch.painless.spi.WhitelistMethod;
import org.objectweb.asm.Type;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
@ -34,7 +36,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Stack;
import java.util.regex.Pattern;
@ -42,10 +43,10 @@ import java.util.regex.Pattern;
* The entire API for Painless. Also used as a whitelist for checking for legal
* methods and fields during at both compile-time and runtime.
*/
public final class Definition {
public final class PainlessLookup {
private static final Map<String, Method> methodCache = new HashMap<>();
private static final Map<String, Field> fieldCache = new HashMap<>();
private static final Map<String, PainlessMethod> methodCache = new HashMap<>();
private static final Map<String, PainlessField> fieldCache = new HashMap<>();
private static final Pattern TYPE_NAME_PATTERN = Pattern.compile("^[_a-zA-Z][._a-zA-Z0-9]*$");
@ -56,306 +57,6 @@ public final class Definition {
}
}
public static class Method {
public final String name;
public final Struct owner;
public final Class<?> augmentation;
public final Class<?> rtn;
public final List<Class<?>> arguments;
public final org.objectweb.asm.commons.Method method;
public final int modifiers;
public final MethodHandle handle;
public Method(String name, Struct owner, Class<?> augmentation, Class<?> rtn, List<Class<?>> arguments,
org.objectweb.asm.commons.Method method, int modifiers, MethodHandle handle) {
this.name = name;
this.augmentation = augmentation;
this.owner = owner;
this.rtn = rtn;
this.arguments = Collections.unmodifiableList(arguments);
this.method = method;
this.modifiers = modifiers;
this.handle = handle;
}
/**
* Returns MethodType for this method.
* <p>
* This works even for user-defined Methods (where the MethodHandle is null).
*/
public MethodType getMethodType() {
// we have a methodhandle already (e.g. whitelisted class)
// just return its type
if (handle != null) {
return handle.type();
}
// otherwise compute it
final Class<?> params[];
final Class<?> returnValue;
if (augmentation != null) {
// static method disguised as virtual/interface method
params = new Class<?>[1 + arguments.size()];
params[0] = augmentation;
for (int i = 0; i < arguments.size(); i++) {
params[i + 1] = defClassToObjectClass(arguments.get(i));
}
returnValue = defClassToObjectClass(rtn);
} else if (Modifier.isStatic(modifiers)) {
// static method: straightforward copy
params = new Class<?>[arguments.size()];
for (int i = 0; i < arguments.size(); i++) {
params[i] = defClassToObjectClass(arguments.get(i));
}
returnValue = defClassToObjectClass(rtn);
} else if ("<init>".equals(name)) {
// constructor: returns the owner class
params = new Class<?>[arguments.size()];
for (int i = 0; i < arguments.size(); i++) {
params[i] = defClassToObjectClass(arguments.get(i));
}
returnValue = owner.clazz;
} else {
// virtual/interface method: add receiver class
params = new Class<?>[1 + arguments.size()];
params[0] = owner.clazz;
for (int i = 0; i < arguments.size(); i++) {
params[i + 1] = defClassToObjectClass(arguments.get(i));
}
returnValue = defClassToObjectClass(rtn);
}
return MethodType.methodType(returnValue, params);
}
public void write(MethodWriter writer) {
final org.objectweb.asm.Type type;
final Class<?> clazz;
if (augmentation != null) {
assert java.lang.reflect.Modifier.isStatic(modifiers);
clazz = augmentation;
type = org.objectweb.asm.Type.getType(augmentation);
} else {
clazz = owner.clazz;
type = owner.type;
}
if (java.lang.reflect.Modifier.isStatic(modifiers)) {
// invokeStatic assumes that the owner class is not an interface, so this is a
// special case for interfaces where the interface method boolean needs to be set to
// true to reference the appropriate class constant when calling a static interface
// method since java 8 did not check, but java 9 and 10 do
if (java.lang.reflect.Modifier.isInterface(clazz.getModifiers())) {
writer.visitMethodInsn(Opcodes.INVOKESTATIC,
type.getInternalName(), name, getMethodType().toMethodDescriptorString(), true);
} else {
writer.invokeStatic(type, method);
}
} else if (java.lang.reflect.Modifier.isInterface(clazz.getModifiers())) {
writer.invokeInterface(type, method);
} else {
writer.invokeVirtual(type, method);
}
}
}
public static final class Field {
public final String name;
public final Struct owner;
public final Class<?> clazz;
public final String javaName;
public final int modifiers;
private final MethodHandle getter;
private final MethodHandle setter;
private Field(String name, String javaName, Struct owner, Class<?> clazz, int modifiers, MethodHandle getter, MethodHandle setter) {
this.name = name;
this.javaName = javaName;
this.owner = owner;
this.clazz = clazz;
this.modifiers = modifiers;
this.getter = getter;
this.setter = setter;
}
}
// TODO: instead of hashing on this, we could have a 'next' pointer in Method itself, but it would make code more complex
// please do *NOT* under any circumstances change this to be the crappy Tuple from elasticsearch!
/**
* Key for looking up a method.
* <p>
* Methods are keyed on both name and arity, and can be overloaded once per arity.
* This allows signatures such as {@code String.indexOf(String) vs String.indexOf(String, int)}.
* <p>
* It is less flexible than full signature overloading where types can differ too, but
* better than just the name, and overloading types adds complexity to users, too.
*/
public static final class MethodKey {
public final String name;
public final int arity;
/**
* Create a new lookup key
* @param name name of the method
* @param arity number of parameters
*/
public MethodKey(String name, int arity) {
this.name = Objects.requireNonNull(name);
this.arity = arity;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + arity;
result = prime * result + name.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
MethodKey other = (MethodKey) obj;
if (arity != other.arity) return false;
if (!name.equals(other.name)) return false;
return true;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(name);
sb.append('/');
sb.append(arity);
return sb.toString();
}
}
public static final class Struct {
public final String name;
public final Class<?> clazz;
public final org.objectweb.asm.Type type;
public final Map<MethodKey, Method> constructors;
public final Map<MethodKey, Method> staticMethods;
public final Map<MethodKey, Method> methods;
public final Map<String, Field> staticMembers;
public final Map<String, Field> members;
public final Map<String, MethodHandle> getters;
public final Map<String, MethodHandle> setters;
public final Method functionalMethod;
private Struct(String name, Class<?> clazz, org.objectweb.asm.Type type) {
this.name = name;
this.clazz = clazz;
this.type = type;
constructors = new HashMap<>();
staticMethods = new HashMap<>();
methods = new HashMap<>();
staticMembers = new HashMap<>();
members = new HashMap<>();
getters = new HashMap<>();
setters = new HashMap<>();
functionalMethod = null;
}
private Struct(Struct struct, Method functionalMethod) {
name = struct.name;
clazz = struct.clazz;
type = struct.type;
constructors = Collections.unmodifiableMap(struct.constructors);
staticMethods = Collections.unmodifiableMap(struct.staticMethods);
methods = Collections.unmodifiableMap(struct.methods);
staticMembers = Collections.unmodifiableMap(struct.staticMembers);
members = Collections.unmodifiableMap(struct.members);
getters = Collections.unmodifiableMap(struct.getters);
setters = Collections.unmodifiableMap(struct.setters);
this.functionalMethod = functionalMethod;
}
private Struct freeze(Method functionalMethod) {
return new Struct(this, functionalMethod);
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null || getClass() != object.getClass()) {
return false;
}
Struct struct = (Struct)object;
return name.equals(struct.name);
}
@Override
public int hashCode() {
return name.hashCode();
}
}
public static class Cast {
/** Create a standard cast with no boxing/unboxing. */
public static Cast standard(Class<?> from, Class<?> to, boolean explicit) {
return new Cast(from, to, explicit, null, null, null, null);
}
/** Create a cast where the from type will be unboxed, and then the cast will be performed. */
public static Cast unboxFrom(Class<?> from, Class<?> to, boolean explicit, Class<?> unboxFrom) {
return new Cast(from, to, explicit, unboxFrom, null, null, null);
}
/** Create a cast where the to type will be unboxed, and then the cast will be performed. */
public static Cast unboxTo(Class<?> from, Class<?> to, boolean explicit, Class<?> unboxTo) {
return new Cast(from, to, explicit, null, unboxTo, null, null);
}
/** Create a cast where the from type will be boxed, and then the cast will be performed. */
public static Cast boxFrom(Class<?> from, Class<?> to, boolean explicit, Class<?> boxFrom) {
return new Cast(from, to, explicit, null, null, boxFrom, null);
}
/** Create a cast where the to type will be boxed, and then the cast will be performed. */
public static Cast boxTo(Class<?> from, Class<?> to, boolean explicit, Class<?> boxTo) {
return new Cast(from, to, explicit, null, null, null, boxTo);
}
public final Class<?> from;
public final Class<?> to;
public final boolean explicit;
public final Class<?> unboxFrom;
public final Class<?> unboxTo;
public final Class<?> boxFrom;
public final Class<?> boxTo;
private Cast(Class<?> from, Class<?> to, boolean explicit, Class<?> unboxFrom, Class<?> unboxTo, Class<?> boxFrom, Class<?> boxTo) {
this.from = from;
this.to = to;
this.explicit = explicit;
this.unboxFrom = unboxFrom;
this.unboxTo = unboxTo;
this.boxFrom = boxFrom;
this.boxTo = boxTo;
}
}
public static Class<?> getBoxedType(Class<?> clazz) {
if (clazz == boolean.class) {
return Boolean.class;
@ -520,29 +221,29 @@ public final class Definition {
return structName + fieldName + typeName;
}
public Collection<Struct> getStructs() {
public Collection<PainlessClass> getStructs() {
return javaClassesToPainlessStructs.values();
}
private final Map<String, Class<?>> painlessTypesToJavaClasses;
private final Map<Class<?>, Struct> javaClassesToPainlessStructs;
private final Map<Class<?>, PainlessClass> javaClassesToPainlessStructs;
public Definition(List<Whitelist> whitelists) {
public PainlessLookup(List<Whitelist> whitelists) {
painlessTypesToJavaClasses = new HashMap<>();
javaClassesToPainlessStructs = new HashMap<>();
String origin = null;
painlessTypesToJavaClasses.put("def", def.class);
javaClassesToPainlessStructs.put(def.class, new Struct("def", Object.class, Type.getType(Object.class)));
javaClassesToPainlessStructs.put(def.class, new PainlessClass("def", Object.class, Type.getType(Object.class)));
try {
// first iteration collects all the Painless type names that
// are used for validation during the second iteration
for (Whitelist whitelist : whitelists) {
for (Whitelist.Struct whitelistStruct : whitelist.whitelistStructs) {
for (WhitelistClass whitelistStruct : whitelist.whitelistStructs) {
String painlessTypeName = whitelistStruct.javaClassName.replace('$', '.');
Struct painlessStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(painlessTypeName));
PainlessClass painlessStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(painlessTypeName));
if (painlessStruct != null && painlessStruct.clazz.getName().equals(whitelistStruct.javaClassName) == false) {
throw new IllegalArgumentException("struct [" + painlessStruct.name + "] cannot represent multiple classes " +
@ -561,20 +262,20 @@ public final class Definition {
// be available in Painless along with validating they exist and all their types have
// been white-listed during the first iteration
for (Whitelist whitelist : whitelists) {
for (Whitelist.Struct whitelistStruct : whitelist.whitelistStructs) {
for (WhitelistClass whitelistStruct : whitelist.whitelistStructs) {
String painlessTypeName = whitelistStruct.javaClassName.replace('$', '.');
for (Whitelist.Constructor whitelistConstructor : whitelistStruct.whitelistConstructors) {
for (WhitelistConstructor whitelistConstructor : whitelistStruct.whitelistConstructors) {
origin = whitelistConstructor.origin;
addConstructor(painlessTypeName, whitelistConstructor);
}
for (Whitelist.Method whitelistMethod : whitelistStruct.whitelistMethods) {
for (WhitelistMethod whitelistMethod : whitelistStruct.whitelistMethods) {
origin = whitelistMethod.origin;
addMethod(whitelist.javaClassLoader, painlessTypeName, whitelistMethod);
}
for (Whitelist.Field whitelistField : whitelistStruct.whitelistFields) {
for (WhitelistField whitelistField : whitelistStruct.whitelistFields) {
origin = whitelistField.origin;
addField(painlessTypeName, whitelistField);
}
@ -587,7 +288,7 @@ public final class Definition {
// goes through each Painless struct and determines the inheritance list,
// and then adds all inherited types to the Painless struct's whitelist
for (Class<?> javaClass : javaClassesToPainlessStructs.keySet()) {
Struct painlessStruct = javaClassesToPainlessStructs.get(javaClass);
PainlessClass painlessStruct = javaClassesToPainlessStructs.get(javaClass);
List<String> painlessSuperStructs = new ArrayList<>();
Class<?> javaSuperClass = painlessStruct.clazz.getSuperclass();
@ -598,7 +299,7 @@ public final class Definition {
// adds super classes to the inheritance list
if (javaSuperClass != null && javaSuperClass.isInterface() == false) {
while (javaSuperClass != null) {
Struct painlessSuperStruct = javaClassesToPainlessStructs.get(javaSuperClass);
PainlessClass painlessSuperStruct = javaClassesToPainlessStructs.get(javaSuperClass);
if (painlessSuperStruct != null) {
painlessSuperStructs.add(painlessSuperStruct.name);
@ -614,7 +315,7 @@ public final class Definition {
Class<?> javaInterfaceLookup = javaInteraceLookups.pop();
for (Class<?> javaSuperInterface : javaInterfaceLookup.getInterfaces()) {
Struct painlessInterfaceStruct = javaClassesToPainlessStructs.get(javaSuperInterface);
PainlessClass painlessInterfaceStruct = javaClassesToPainlessStructs.get(javaSuperInterface);
if (painlessInterfaceStruct != null) {
String painlessInterfaceStructName = painlessInterfaceStruct.name;
@ -635,7 +336,7 @@ public final class Definition {
// copies methods and fields from Object into interface types
if (painlessStruct.clazz.isInterface() || (def.class.getSimpleName()).equals(painlessStruct.name)) {
Struct painlessObjectStruct = javaClassesToPainlessStructs.get(Object.class);
PainlessClass painlessObjectStruct = javaClassesToPainlessStructs.get(Object.class);
if (painlessObjectStruct != null) {
copyStruct(painlessStruct.name, Collections.singletonList(painlessObjectStruct.name));
@ -644,17 +345,17 @@ public final class Definition {
}
// precompute runtime classes
for (Struct painlessStruct : javaClassesToPainlessStructs.values()) {
for (PainlessClass painlessStruct : javaClassesToPainlessStructs.values()) {
addRuntimeClass(painlessStruct);
}
// copy all structs to make them unmodifiable for outside users:
for (Map.Entry<Class<?>,Struct> entry : javaClassesToPainlessStructs.entrySet()) {
for (Map.Entry<Class<?>,PainlessClass> entry : javaClassesToPainlessStructs.entrySet()) {
entry.setValue(entry.getValue().freeze(computeFunctionalInterfaceMethod(entry.getValue())));
}
}
private void addStruct(ClassLoader whitelistClassLoader, Whitelist.Struct whitelistStruct) {
private void addStruct(ClassLoader whitelistClassLoader, WhitelistClass whitelistStruct) {
String painlessTypeName = whitelistStruct.javaClassName.replace('$', '.');
String importedPainlessTypeName = painlessTypeName;
@ -688,10 +389,10 @@ public final class Definition {
}
}
Struct existingStruct = javaClassesToPainlessStructs.get(javaClass);
PainlessClass existingStruct = javaClassesToPainlessStructs.get(javaClass);
if (existingStruct == null) {
Struct struct = new Struct(painlessTypeName, javaClass, org.objectweb.asm.Type.getType(javaClass));
PainlessClass struct = new PainlessClass(painlessTypeName, javaClass, org.objectweb.asm.Type.getType(javaClass));
painlessTypesToJavaClasses.put(painlessTypeName, javaClass);
javaClassesToPainlessStructs.put(javaClass, struct);
} else if (existingStruct.clazz.equals(javaClass) == false) {
@ -725,8 +426,8 @@ public final class Definition {
}
}
private void addConstructor(String ownerStructName, Whitelist.Constructor whitelistConstructor) {
Struct ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName));
private void addConstructor(String ownerStructName, WhitelistConstructor whitelistConstructor) {
PainlessClass ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName));
if (ownerStruct == null) {
throw new IllegalArgumentException("owner struct [" + ownerStructName + "] not defined for constructor with " +
@ -760,8 +461,8 @@ public final class Definition {
" with constructor parameters " + whitelistConstructor.painlessParameterTypeNames, exception);
}
MethodKey painlessMethodKey = new MethodKey("<init>", whitelistConstructor.painlessParameterTypeNames.size());
Method painlessConstructor = ownerStruct.constructors.get(painlessMethodKey);
PainlessMethodKey painlessMethodKey = new PainlessMethodKey("<init>", whitelistConstructor.painlessParameterTypeNames.size());
PainlessMethod painlessConstructor = ownerStruct.constructors.get(painlessMethodKey);
if (painlessConstructor == null) {
org.objectweb.asm.commons.Method asmConstructor = org.objectweb.asm.commons.Method.getMethod(javaConstructor);
@ -775,7 +476,7 @@ public final class Definition {
}
painlessConstructor = methodCache.computeIfAbsent(buildMethodCacheKey(ownerStruct.name, "<init>", painlessParametersTypes),
key -> new Method("<init>", ownerStruct, null, void.class, painlessParametersTypes,
key -> new PainlessMethod("<init>", ownerStruct, null, void.class, painlessParametersTypes,
asmConstructor, javaConstructor.getModifiers(), javaHandle));
ownerStruct.constructors.put(painlessMethodKey, painlessConstructor);
} else if (painlessConstructor.arguments.equals(painlessParametersTypes) == false){
@ -785,8 +486,8 @@ public final class Definition {
}
}
private void addMethod(ClassLoader whitelistClassLoader, String ownerStructName, Whitelist.Method whitelistMethod) {
Struct ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName));
private void addMethod(ClassLoader whitelistClassLoader, String ownerStructName, WhitelistMethod whitelistMethod) {
PainlessClass ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName));
if (ownerStruct == null) {
throw new IllegalArgumentException("owner struct [" + ownerStructName + "] not defined for method with " +
@ -864,10 +565,11 @@ public final class Definition {
"and parameters " + whitelistMethod.painlessParameterTypeNames);
}
MethodKey painlessMethodKey = new MethodKey(whitelistMethod.javaMethodName, whitelistMethod.painlessParameterTypeNames.size());
PainlessMethodKey painlessMethodKey =
new PainlessMethodKey(whitelistMethod.javaMethodName, whitelistMethod.painlessParameterTypeNames.size());
if (javaAugmentedClass == null && Modifier.isStatic(javaMethod.getModifiers())) {
Method painlessMethod = ownerStruct.staticMethods.get(painlessMethodKey);
PainlessMethod painlessMethod = ownerStruct.staticMethods.get(painlessMethodKey);
if (painlessMethod == null) {
org.objectweb.asm.commons.Method asmMethod = org.objectweb.asm.commons.Method.getMethod(javaMethod);
@ -882,8 +584,8 @@ public final class Definition {
painlessMethod = methodCache.computeIfAbsent(
buildMethodCacheKey(ownerStruct.name, whitelistMethod.javaMethodName, painlessParametersTypes),
key -> new Method(whitelistMethod.javaMethodName, ownerStruct, null, painlessReturnClass, painlessParametersTypes,
asmMethod, javaMethod.getModifiers(), javaMethodHandle));
key -> new PainlessMethod(whitelistMethod.javaMethodName, ownerStruct, null, painlessReturnClass,
painlessParametersTypes, asmMethod, javaMethod.getModifiers(), javaMethodHandle));
ownerStruct.staticMethods.put(painlessMethodKey, painlessMethod);
} else if ((painlessMethod.name.equals(whitelistMethod.javaMethodName) && painlessMethod.rtn == painlessReturnClass &&
painlessMethod.arguments.equals(painlessParametersTypes)) == false) {
@ -893,7 +595,7 @@ public final class Definition {
"and parameters " + painlessParametersTypes + " and " + painlessMethod.arguments);
}
} else {
Method painlessMethod = ownerStruct.methods.get(painlessMethodKey);
PainlessMethod painlessMethod = ownerStruct.methods.get(painlessMethodKey);
if (painlessMethod == null) {
org.objectweb.asm.commons.Method asmMethod = org.objectweb.asm.commons.Method.getMethod(javaMethod);
@ -908,7 +610,7 @@ public final class Definition {
painlessMethod = methodCache.computeIfAbsent(
buildMethodCacheKey(ownerStruct.name, whitelistMethod.javaMethodName, painlessParametersTypes),
key -> new Method(whitelistMethod.javaMethodName, ownerStruct, javaAugmentedClass, painlessReturnClass,
key -> new PainlessMethod(whitelistMethod.javaMethodName, ownerStruct, javaAugmentedClass, painlessReturnClass,
painlessParametersTypes, asmMethod, javaMethod.getModifiers(), javaMethodHandle));
ownerStruct.methods.put(painlessMethodKey, painlessMethod);
} else if ((painlessMethod.name.equals(whitelistMethod.javaMethodName) && painlessMethod.rtn.equals(painlessReturnClass) &&
@ -921,8 +623,8 @@ public final class Definition {
}
}
private void addField(String ownerStructName, Whitelist.Field whitelistField) {
Struct ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName));
private void addField(String ownerStructName, WhitelistField whitelistField) {
PainlessClass ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName));
if (ownerStruct == null) {
throw new IllegalArgumentException("owner struct [" + ownerStructName + "] not defined for method with " +
@ -958,12 +660,12 @@ public final class Definition {
"with owner struct [" + ownerStruct.name + "] is not final");
}
Field painlessField = ownerStruct.staticMembers.get(whitelistField.javaFieldName);
PainlessField painlessField = ownerStruct.staticMembers.get(whitelistField.javaFieldName);
if (painlessField == null) {
painlessField = fieldCache.computeIfAbsent(
buildFieldCacheKey(ownerStruct.name, whitelistField.javaFieldName, painlessFieldClass.getName()),
key -> new Field(whitelistField.javaFieldName, javaField.getName(),
key -> new PainlessField(whitelistField.javaFieldName, javaField.getName(),
ownerStruct, painlessFieldClass, javaField.getModifiers(), null, null));
ownerStruct.staticMembers.put(whitelistField.javaFieldName, painlessField);
} else if (painlessField.clazz != painlessFieldClass) {
@ -987,12 +689,12 @@ public final class Definition {
" not found for class [" + ownerStruct.clazz.getName() + "].");
}
Field painlessField = ownerStruct.members.get(whitelistField.javaFieldName);
PainlessField painlessField = ownerStruct.members.get(whitelistField.javaFieldName);
if (painlessField == null) {
painlessField = fieldCache.computeIfAbsent(
buildFieldCacheKey(ownerStruct.name, whitelistField.javaFieldName, painlessFieldClass.getName()),
key -> new Field(whitelistField.javaFieldName, javaField.getName(),
key -> new PainlessField(whitelistField.javaFieldName, javaField.getName(),
ownerStruct, painlessFieldClass, javaField.getModifiers(), javaMethodHandleGetter, javaMethodHandleSetter));
ownerStruct.members.put(whitelistField.javaFieldName, painlessField);
} else if (painlessField.clazz != painlessFieldClass) {
@ -1003,14 +705,14 @@ public final class Definition {
}
private void copyStruct(String struct, List<String> children) {
final Struct owner = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(struct));
final PainlessClass owner = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(struct));
if (owner == null) {
throw new IllegalArgumentException("Owner struct [" + struct + "] not defined for copy.");
}
for (int count = 0; count < children.size(); ++count) {
final Struct child = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(children.get(count)));
final PainlessClass child = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(children.get(count)));
if (child == null) {
throw new IllegalArgumentException("Child struct [" + children.get(count) + "]" +
@ -1022,9 +724,9 @@ public final class Definition {
" is not a super type of owner struct [" + owner.name + "] in copy.");
}
for (Map.Entry<MethodKey,Method> kvPair : child.methods.entrySet()) {
MethodKey methodKey = kvPair.getKey();
Method method = kvPair.getValue();
for (Map.Entry<PainlessMethodKey,PainlessMethod> kvPair : child.methods.entrySet()) {
PainlessMethodKey methodKey = kvPair.getKey();
PainlessMethod method = kvPair.getValue();
if (owner.methods.get(methodKey) == null) {
// TODO: some of these are no longer valid or outright don't work
// TODO: since classes may not come from the Painless classloader
@ -1076,10 +778,10 @@ public final class Definition {
}
}
for (Field field : child.members.values()) {
for (PainlessField field : child.members.values()) {
if (owner.members.get(field.name) == null) {
owner.members.put(field.name,
new Field(field.name, field.javaName, owner, field.clazz, field.modifiers, field.getter, field.setter));
new PainlessField(field.name, field.javaName, owner, field.clazz, field.modifiers, field.getter, field.setter));
}
}
}
@ -1088,11 +790,11 @@ public final class Definition {
/**
* Precomputes a more efficient structure for dynamic method/field access.
*/
private void addRuntimeClass(final Struct struct) {
private void addRuntimeClass(final PainlessClass struct) {
// add all getters/setters
for (Map.Entry<MethodKey, Method> method : struct.methods.entrySet()) {
for (Map.Entry<PainlessMethodKey, PainlessMethod> method : struct.methods.entrySet()) {
String name = method.getKey().name;
Method m = method.getValue();
PainlessMethod m = method.getValue();
if (m.arguments.size() == 0 &&
name.startsWith("get") &&
@ -1124,14 +826,14 @@ public final class Definition {
}
// add all members
for (Map.Entry<String, Field> member : struct.members.entrySet()) {
for (Map.Entry<String, PainlessField> member : struct.members.entrySet()) {
struct.getters.put(member.getKey(), member.getValue().getter);
struct.setters.put(member.getKey(), member.getValue().setter);
}
}
/** computes the functional interface method for a class, or returns null */
private Method computeFunctionalInterfaceMethod(Struct clazz) {
private PainlessMethod computeFunctionalInterfaceMethod(PainlessClass clazz) {
if (!clazz.clazz.isInterface()) {
return null;
}
@ -1166,7 +868,7 @@ public final class Definition {
}
// inspect the one method found from the reflection API, it should match the whitelist!
java.lang.reflect.Method oneMethod = methods.get(0);
Method painless = clazz.methods.get(new Definition.MethodKey(oneMethod.getName(), oneMethod.getParameterCount()));
PainlessMethod painless = clazz.methods.get(new PainlessMethodKey(oneMethod.getName(), oneMethod.getParameterCount()));
if (painless == null || painless.method.equals(org.objectweb.asm.commons.Method.getMethod(oneMethod)) == false) {
throw new IllegalArgumentException("Class: " + clazz.name + " is functional but the functional " +
"method is not whitelisted!");
@ -1178,7 +880,7 @@ public final class Definition {
return painlessTypesToJavaClasses.containsKey(painlessType);
}
public Struct getPainlessStructFromJavaClass(Class<?> clazz) {
public PainlessClass getPainlessStructFromJavaClass(Class<?> clazz) {
return javaClassesToPainlessStructs.get(clazz);
}

View File

@ -0,0 +1,130 @@
/*
* 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.lookup;
import org.elasticsearch.painless.MethodWriter;
import org.objectweb.asm.Opcodes;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.List;
public class PainlessMethod {
public final String name;
public final PainlessClass owner;
public final Class<?> augmentation;
public final Class<?> rtn;
public final List<Class<?>> arguments;
public final org.objectweb.asm.commons.Method method;
public final int modifiers;
public final MethodHandle handle;
public PainlessMethod(String name, PainlessClass owner, Class<?> augmentation, Class<?> rtn, List<Class<?>> arguments,
org.objectweb.asm.commons.Method method, int modifiers, MethodHandle handle) {
this.name = name;
this.augmentation = augmentation;
this.owner = owner;
this.rtn = rtn;
this.arguments = Collections.unmodifiableList(arguments);
this.method = method;
this.modifiers = modifiers;
this.handle = handle;
}
/**
* Returns MethodType for this method.
* <p>
* This works even for user-defined Methods (where the MethodHandle is null).
*/
public MethodType getMethodType() {
// we have a methodhandle already (e.g. whitelisted class)
// just return its type
if (handle != null) {
return handle.type();
}
// otherwise compute it
final Class<?> params[];
final Class<?> returnValue;
if (augmentation != null) {
// static method disguised as virtual/interface method
params = new Class<?>[1 + arguments.size()];
params[0] = augmentation;
for (int i = 0; i < arguments.size(); i++) {
params[i + 1] = PainlessLookup.defClassToObjectClass(arguments.get(i));
}
returnValue = PainlessLookup.defClassToObjectClass(rtn);
} else if (Modifier.isStatic(modifiers)) {
// static method: straightforward copy
params = new Class<?>[arguments.size()];
for (int i = 0; i < arguments.size(); i++) {
params[i] = PainlessLookup.defClassToObjectClass(arguments.get(i));
}
returnValue = PainlessLookup.defClassToObjectClass(rtn);
} else if ("<init>".equals(name)) {
// constructor: returns the owner class
params = new Class<?>[arguments.size()];
for (int i = 0; i < arguments.size(); i++) {
params[i] = PainlessLookup.defClassToObjectClass(arguments.get(i));
}
returnValue = owner.clazz;
} else {
// virtual/interface method: add receiver class
params = new Class<?>[1 + arguments.size()];
params[0] = owner.clazz;
for (int i = 0; i < arguments.size(); i++) {
params[i + 1] = PainlessLookup.defClassToObjectClass(arguments.get(i));
}
returnValue = PainlessLookup.defClassToObjectClass(rtn);
}
return MethodType.methodType(returnValue, params);
}
public void write(MethodWriter writer) {
final org.objectweb.asm.Type type;
final Class<?> clazz;
if (augmentation != null) {
assert Modifier.isStatic(modifiers);
clazz = augmentation;
type = org.objectweb.asm.Type.getType(augmentation);
} else {
clazz = owner.clazz;
type = owner.type;
}
if (Modifier.isStatic(modifiers)) {
// invokeStatic assumes that the owner class is not an interface, so this is a
// special case for interfaces where the interface method boolean needs to be set to
// true to reference the appropriate class constant when calling a static interface
// method since java 8 did not check, but java 9 and 10 do
if (Modifier.isInterface(clazz.getModifiers())) {
writer.visitMethodInsn(Opcodes.INVOKESTATIC,
type.getInternalName(), name, getMethodType().toMethodDescriptorString(), true);
} else {
writer.invokeStatic(type, method);
}
} else if (Modifier.isInterface(clazz.getModifiers())) {
writer.invokeInterface(type, method);
} else {
writer.invokeVirtual(type, method);
}
}
}

View File

@ -0,0 +1,75 @@
/*
* 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.lookup;
import java.util.Objects;
/**
* Key for looking up a method.
* <p>
* Methods are keyed on both name and arity, and can be overloaded once per arity.
* This allows signatures such as {@code String.indexOf(String) vs String.indexOf(String, int)}.
* <p>
* It is less flexible than full signature overloading where types can differ too, but
* better than just the name, and overloading types adds complexity to users, too.
*/
public final class PainlessMethodKey {
public final String name;
public final int arity;
/**
* Create a new lookup key
* @param name name of the method
* @param arity number of parameters
*/
public PainlessMethodKey(String name, int arity) {
this.name = Objects.requireNonNull(name);
this.arity = arity;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + arity;
result = prime * result + name.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
PainlessMethodKey other = (PainlessMethodKey) obj;
if (arity != other.arity) return false;
if (!name.equals(other.name)) return false;
return true;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(name);
sb.append('/');
sb.append(arity);
return sb.toString();
}
}

View File

@ -20,8 +20,8 @@
package org.elasticsearch.painless.node;
import org.elasticsearch.painless.AnalyzerCaster;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.Definition.Cast;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.lookup.PainlessCast;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
@ -118,7 +118,7 @@ public abstract class AExpression extends ANode {
* @return The new child node for the parent node calling this method.
*/
AExpression cast(Locals locals) {
Cast cast = AnalyzerCaster.getLegalCast(location, actual, expected, explicit, internal);
PainlessCast cast = AnalyzerCaster.getLegalCast(location, actual, expected, explicit, internal);
if (cast == null) {
if (constant == null || this instanceof EConstant) {
@ -157,7 +157,7 @@ public abstract class AExpression extends ANode {
return ecast;
} else {
if (Definition.isConstantType(expected)) {
if (PainlessLookup.isConstantType(expected)) {
// For the case where a cast is required, a constant is set,
// and the constant can be immediately cast to the expected type.
// An EConstant replaces this node with the constant cast appropriately

View File

@ -22,8 +22,8 @@ package org.elasticsearch.painless.node;
import org.elasticsearch.painless.AnalyzerCaster;
import org.elasticsearch.painless.DefBootstrap;
import org.elasticsearch.painless.Definition.Cast;
import org.elasticsearch.painless.Definition.def;
import org.elasticsearch.painless.lookup.PainlessCast;
import org.elasticsearch.painless.lookup.PainlessLookup.def;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
@ -49,8 +49,8 @@ public final class EAssignment extends AExpression {
private boolean cat = false;
private Class<?> promote = null;
private Class<?> shiftDistance; // for shifts, the RHS is promoted independently
private Cast there = null;
private Cast back = null;
private PainlessCast there = null;
private PainlessCast back = null;
public EAssignment(Location location, AExpression lhs, AExpression rhs, boolean pre, boolean post, Operation operation) {
super(location);

View File

@ -21,8 +21,8 @@ package org.elasticsearch.painless.node;
import org.elasticsearch.painless.AnalyzerCaster;
import org.elasticsearch.painless.DefBootstrap;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.Definition.def;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.lookup.PainlessLookup.def;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
@ -106,7 +106,7 @@ public final class EBinary extends AExpression {
if (promote == null) {
throw createError(new ClassCastException("Cannot apply multiply [*] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
actual = promote;
@ -148,7 +148,7 @@ public final class EBinary extends AExpression {
if (promote == null) {
throw createError(new ClassCastException("Cannot apply divide [/] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
actual = promote;
@ -195,7 +195,7 @@ public final class EBinary extends AExpression {
if (promote == null) {
throw createError(new ClassCastException("Cannot apply remainder [%] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
actual = promote;
@ -242,7 +242,7 @@ public final class EBinary extends AExpression {
if (promote == null) {
throw createError(new ClassCastException("Cannot apply add [+] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
actual = promote;
@ -300,7 +300,7 @@ public final class EBinary extends AExpression {
if (promote == null) {
throw createError(new ClassCastException("Cannot apply subtract [-] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
actual = promote;
@ -358,7 +358,7 @@ public final class EBinary extends AExpression {
if (lhspromote == null || rhspromote == null) {
throw createError(new ClassCastException("Cannot apply left shift [<<] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
actual = promote = lhspromote;
@ -405,7 +405,7 @@ public final class EBinary extends AExpression {
if (lhspromote == null || rhspromote == null) {
throw createError(new ClassCastException("Cannot apply right shift [>>] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
actual = promote = lhspromote;
@ -455,7 +455,7 @@ public final class EBinary extends AExpression {
if (lhspromote == null || rhspromote == null) {
throw createError(new ClassCastException("Cannot apply unsigned shift [>>>] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
if (lhspromote == def.class || rhspromote == def.class) {
@ -498,7 +498,7 @@ public final class EBinary extends AExpression {
if (promote == null) {
throw createError(new ClassCastException("Cannot apply and [&] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
actual = promote;
@ -537,7 +537,7 @@ public final class EBinary extends AExpression {
if (promote == null) {
throw createError(new ClassCastException("Cannot apply xor [^] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
actual = promote;
@ -577,7 +577,7 @@ public final class EBinary extends AExpression {
if (promote == null) {
throw createError(new ClassCastException("Cannot apply or [|] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
actual = promote;

View File

@ -19,8 +19,8 @@
package org.elasticsearch.painless.node;
import org.elasticsearch.painless.Definition.Method;
import org.elasticsearch.painless.Definition.MethodKey;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.lookup.PainlessMethodKey;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
@ -40,7 +40,7 @@ public final class ECallLocal extends AExpression {
private final String name;
private final List<AExpression> arguments;
private Method method = null;
private PainlessMethod method = null;
public ECallLocal(Location location, String name, List<AExpression> arguments) {
super(location);
@ -58,7 +58,7 @@ public final class ECallLocal extends AExpression {
@Override
void analyze(Locals locals) {
MethodKey methodKey = new MethodKey(name, arguments.size());
PainlessMethodKey methodKey = new PainlessMethodKey(name, arguments.size());
method = locals.getMethod(methodKey);
if (method == null) {

View File

@ -21,8 +21,8 @@ package org.elasticsearch.painless.node;
import org.elasticsearch.painless.AnalyzerCaster;
import org.elasticsearch.painless.DefBootstrap;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.Definition.def;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.lookup.PainlessLookup.def;
import org.elasticsearch.painless.FunctionRef;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
@ -69,7 +69,7 @@ public final class ECapturingFunctionRef extends AExpression implements ILambda
defPointer = "D" + variable + "." + call + ",1";
} else {
// typed implementation
defPointer = "S" + Definition.ClassToName(captured.clazz) + "." + call + ",1";
defPointer = "S" + PainlessLookup.ClassToName(captured.clazz) + "." + call + ",1";
}
actual = String.class;
} else {
@ -77,7 +77,7 @@ public final class ECapturingFunctionRef extends AExpression implements ILambda
// static case
if (captured.clazz != def.class) {
try {
ref = new FunctionRef(locals.getDefinition(), expected, Definition.ClassToName(captured.clazz), call, 1);
ref = new FunctionRef(locals.getPainlessLookup(), expected, PainlessLookup.ClassToName(captured.clazz), call, 1);
// check casts between the interface method and the delegate method are legal
for (int i = 0; i < ref.interfaceMethod.arguments.size(); ++i) {
@ -109,7 +109,7 @@ public final class ECapturingFunctionRef extends AExpression implements ILambda
// typed interface, dynamic implementation
writer.visitVarInsn(MethodWriter.getType(captured.clazz).getOpcode(Opcodes.ILOAD), captured.getSlot());
Type methodType = Type.getMethodType(MethodWriter.getType(expected), MethodWriter.getType(captured.clazz));
writer.invokeDefCall(call, methodType, DefBootstrap.REFERENCE, Definition.ClassToName(expected));
writer.invokeDefCall(call, methodType, DefBootstrap.REFERENCE, PainlessLookup.ClassToName(expected));
} else {
// typed interface, typed implementation
writer.visitVarInsn(MethodWriter.getType(captured.clazz).getOpcode(Opcodes.ILOAD), captured.getSlot());

View File

@ -19,8 +19,8 @@
package org.elasticsearch.painless.node;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.Definition.Cast;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.lookup.PainlessCast;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
@ -35,9 +35,9 @@ import java.util.Set;
final class ECast extends AExpression {
private AExpression child;
private final Cast cast;
private final PainlessCast cast;
ECast(Location location, AExpression child, Cast cast) {
ECast(Location location, AExpression child, PainlessCast cast) {
super(location);
this.child = Objects.requireNonNull(child);
@ -63,6 +63,6 @@ final class ECast extends AExpression {
@Override
public String toString() {
return singleLineToString(Definition.ClassToName(cast.to), child);
return singleLineToString(PainlessLookup.ClassToName(cast.to), child);
}
}

View File

@ -21,8 +21,8 @@ package org.elasticsearch.painless.node;
import org.elasticsearch.painless.AnalyzerCaster;
import org.elasticsearch.painless.DefBootstrap;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.Definition.def;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.lookup.PainlessLookup.def;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
@ -93,7 +93,7 @@ public final class EComp extends AExpression {
if (promotedType == null) {
throw createError(new ClassCastException("Cannot apply equals [==] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
if (promotedType == def.class) {
@ -142,7 +142,7 @@ public final class EComp extends AExpression {
if (promotedType == null) {
throw createError(new ClassCastException("Cannot apply reference equals [===] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
left.expected = promotedType;
@ -182,7 +182,7 @@ public final class EComp extends AExpression {
if (promotedType == null) {
throw createError(new ClassCastException("Cannot apply not equals [!=] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
if (promotedType == def.class) {
@ -231,7 +231,7 @@ public final class EComp extends AExpression {
if (promotedType == null) {
throw createError(new ClassCastException("Cannot apply reference not equals [!==] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
left.expected = promotedType;
@ -271,7 +271,7 @@ public final class EComp extends AExpression {
if (promotedType == null) {
throw createError(new ClassCastException("Cannot apply greater than or equals [>=] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
if (promotedType == def.class) {
@ -310,7 +310,7 @@ public final class EComp extends AExpression {
if (promotedType == null) {
throw createError(new ClassCastException("Cannot apply greater than [>] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
if (promotedType == def.class) {
@ -349,7 +349,7 @@ public final class EComp extends AExpression {
if (promotedType == null) {
throw createError(new ClassCastException("Cannot apply less than or equals [<=] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
if (promotedType == def.class) {
@ -388,7 +388,7 @@ public final class EComp extends AExpression {
if (promotedType == null) {
throw createError(new ClassCastException("Cannot apply less than [>=] to types " +
"[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "]."));
"[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "]."));
}
if (promotedType == def.class) {

View File

@ -50,7 +50,7 @@ public final class EExplicit extends AExpression {
@Override
void analyze(Locals locals) {
try {
actual = locals.getDefinition().getJavaClassFromPainlessType(type);
actual = locals.getPainlessLookup().getJavaClassFromPainlessType(type);
} catch (IllegalArgumentException exception) {
throw createError(new IllegalArgumentException("Not a type [" + type + "]."));
}

View File

@ -20,9 +20,9 @@
package org.elasticsearch.painless.node;
import org.elasticsearch.painless.AnalyzerCaster;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.Definition.Method;
import org.elasticsearch.painless.Definition.MethodKey;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.lookup.PainlessMethodKey;
import org.elasticsearch.painless.FunctionRef;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
@ -66,15 +66,15 @@ public final class EFunctionRef extends AExpression implements ILambda {
try {
if ("this".equals(type)) {
// user's own function
Method interfaceMethod = locals.getDefinition().getPainlessStructFromJavaClass(expected).functionalMethod;
PainlessMethod interfaceMethod = locals.getPainlessLookup().getPainlessStructFromJavaClass(expected).functionalMethod;
if (interfaceMethod == null) {
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
"to [" + Definition.ClassToName(expected) + "], not a functional interface");
"to [" + PainlessLookup.ClassToName(expected) + "], not a functional interface");
}
Method delegateMethod = locals.getMethod(new MethodKey(call, interfaceMethod.arguments.size()));
PainlessMethod delegateMethod = locals.getMethod(new PainlessMethodKey(call, interfaceMethod.arguments.size()));
if (delegateMethod == null) {
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
"to [" + Definition.ClassToName(expected) + "], function not found");
"to [" + PainlessLookup.ClassToName(expected) + "], function not found");
}
ref = new FunctionRef(expected, interfaceMethod, delegateMethod, 0);
@ -90,7 +90,7 @@ public final class EFunctionRef extends AExpression implements ILambda {
}
} else {
// whitelist lookup
ref = new FunctionRef(locals.getDefinition(), expected, type, call, 0);
ref = new FunctionRef(locals.getPainlessLookup(), expected, type, call, 0);
}
} catch (IllegalArgumentException e) {

View File

@ -19,7 +19,7 @@
package org.elasticsearch.painless.node;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
@ -58,13 +58,13 @@ public final class EInstanceof extends AExpression {
// ensure the specified type is part of the definition
try {
clazz = locals.getDefinition().getJavaClassFromPainlessType(this.type);
clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type);
} catch (IllegalArgumentException exception) {
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
}
// map to wrapped type for primitive types
resolvedType = clazz.isPrimitive() ? Definition.getBoxedType(clazz) : Definition.defClassToObjectClass(clazz);
resolvedType = clazz.isPrimitive() ? PainlessLookup.getBoxedType(clazz) : PainlessLookup.defClassToObjectClass(clazz);
// analyze and cast the expression
expression.analyze(locals);
@ -75,7 +75,7 @@ public final class EInstanceof extends AExpression {
primitiveExpression = expression.actual.isPrimitive();
// map to wrapped type for primitive types
expressionType = expression.actual.isPrimitive() ?
Definition.getBoxedType(expression.actual) : Definition.defClassToObjectClass(clazz);
PainlessLookup.getBoxedType(expression.actual) : PainlessLookup.defClassToObjectClass(clazz);
actual = boolean.class;
}

View File

@ -20,9 +20,9 @@
package org.elasticsearch.painless.node;
import org.elasticsearch.painless.AnalyzerCaster;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.Definition.Method;
import org.elasticsearch.painless.Definition.def;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.lookup.PainlessLookup.def;
import org.elasticsearch.painless.FunctionRef;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
@ -103,7 +103,7 @@ public final class ELambda extends AExpression implements ILambda {
void analyze(Locals locals) {
Class<?> returnType;
List<String> actualParamTypeStrs;
Method interfaceMethod;
PainlessMethod interfaceMethod;
// inspect the target first, set interface method if we know it.
if (expected == null) {
interfaceMethod = null;
@ -120,15 +120,15 @@ public final class ELambda extends AExpression implements ILambda {
}
} else {
// we know the method statically, infer return type and any unknown/def types
interfaceMethod = locals.getDefinition().getPainlessStructFromJavaClass(expected).functionalMethod;
interfaceMethod = locals.getPainlessLookup().getPainlessStructFromJavaClass(expected).functionalMethod;
if (interfaceMethod == null) {
throw createError(new IllegalArgumentException("Cannot pass lambda to [" + Definition.ClassToName(expected) +
throw createError(new IllegalArgumentException("Cannot pass lambda to [" + PainlessLookup.ClassToName(expected) +
"], not a functional interface"));
}
// check arity before we manipulate parameters
if (interfaceMethod.arguments.size() != paramTypeStrs.size())
throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.name +
"] in [" + Definition.ClassToName(expected) + "]");
"] in [" + PainlessLookup.ClassToName(expected) + "]");
// for method invocation, its allowed to ignore the return value
if (interfaceMethod.rtn == void.class) {
returnType = def.class;
@ -140,7 +140,7 @@ public final class ELambda extends AExpression implements ILambda {
for (int i = 0; i < paramTypeStrs.size(); i++) {
String paramType = paramTypeStrs.get(i);
if (paramType == null) {
actualParamTypeStrs.add(Definition.ClassToName(interfaceMethod.arguments.get(i)));
actualParamTypeStrs.add(PainlessLookup.ClassToName(interfaceMethod.arguments.get(i)));
} else {
actualParamTypeStrs.add(paramType);
}
@ -162,16 +162,16 @@ public final class ELambda extends AExpression implements ILambda {
List<String> paramTypes = new ArrayList<>(captures.size() + actualParamTypeStrs.size());
List<String> paramNames = new ArrayList<>(captures.size() + paramNameStrs.size());
for (Variable var : captures) {
paramTypes.add(Definition.ClassToName(var.clazz));
paramTypes.add(PainlessLookup.ClassToName(var.clazz));
paramNames.add(var.name);
}
paramTypes.addAll(actualParamTypeStrs);
paramNames.addAll(paramNameStrs);
// desugar lambda body into a synthetic method
desugared = new SFunction(reserved, location, Definition.ClassToName(returnType), name,
desugared = new SFunction(reserved, location, PainlessLookup.ClassToName(returnType), name,
paramTypes, paramNames, statements, true);
desugared.generateSignature(locals.getDefinition());
desugared.generateSignature(locals.getPainlessLookup());
desugared.analyze(Locals.newLambdaScope(locals.getProgramScope(), returnType,
desugared.parameters, captures.size(), reserved.getMaxLoopCounter()));

View File

@ -19,9 +19,9 @@
package org.elasticsearch.painless.node;
import org.elasticsearch.painless.Definition.Method;
import org.elasticsearch.painless.Definition.MethodKey;
import org.elasticsearch.painless.Definition.def;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.lookup.PainlessMethodKey;
import org.elasticsearch.painless.lookup.PainlessLookup.def;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
@ -37,8 +37,8 @@ import java.util.Set;
public final class EListInit extends AExpression {
private final List<AExpression> values;
private Method constructor = null;
private Method method = null;
private PainlessMethod constructor = null;
private PainlessMethod method = null;
public EListInit(Location location, List<AExpression> values) {
super(location);
@ -61,13 +61,14 @@ public final class EListInit extends AExpression {
actual = ArrayList.class;
constructor = locals.getDefinition().getPainlessStructFromJavaClass(actual).constructors.get(new MethodKey("<init>", 0));
constructor =
locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get(new PainlessMethodKey("<init>", 0));
if (constructor == null) {
throw createError(new IllegalStateException("Illegal tree structure."));
}
method = locals.getDefinition().getPainlessStructFromJavaClass(actual).methods.get(new MethodKey("add", 1));
method = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).methods.get(new PainlessMethodKey("add", 1));
if (method == null) {
throw createError(new IllegalStateException("Illegal tree structure."));

View File

@ -19,9 +19,9 @@
package org.elasticsearch.painless.node;
import org.elasticsearch.painless.Definition.Method;
import org.elasticsearch.painless.Definition.MethodKey;
import org.elasticsearch.painless.Definition.def;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.lookup.PainlessMethodKey;
import org.elasticsearch.painless.lookup.PainlessLookup.def;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
@ -38,8 +38,8 @@ public final class EMapInit extends AExpression {
private final List<AExpression> keys;
private final List<AExpression> values;
private Method constructor = null;
private Method method = null;
private PainlessMethod constructor = null;
private PainlessMethod method = null;
public EMapInit(Location location, List<AExpression> keys, List<AExpression> values) {
super(location);
@ -67,13 +67,14 @@ public final class EMapInit extends AExpression {
actual = HashMap.class;
constructor = locals.getDefinition().getPainlessStructFromJavaClass(actual).constructors.get(new MethodKey("<init>", 0));
constructor =
locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get(new PainlessMethodKey("<init>", 0));
if (constructor == null) {
throw createError(new IllegalStateException("Illegal tree structure."));
}
method = locals.getDefinition().getPainlessStructFromJavaClass(actual).methods.get(new MethodKey("put", 2));
method = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).methods.get(new PainlessMethodKey("put", 2));
if (method == null) {
throw createError(new IllegalStateException("Illegal tree structure."));

View File

@ -61,7 +61,7 @@ public final class ENewArray extends AExpression {
Class<?> clazz;
try {
clazz = locals.getDefinition().getJavaClassFromPainlessType(this.type);
clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type);
} catch (IllegalArgumentException exception) {
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
}

View File

@ -19,13 +19,13 @@
package org.elasticsearch.painless.node;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.Definition.Method;
import org.elasticsearch.painless.Definition.Struct;
import org.elasticsearch.painless.lookup.PainlessMethod;
import org.elasticsearch.painless.lookup.PainlessClass;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.MethodWriter;
import org.elasticsearch.painless.lookup.PainlessMethodKey;
import java.util.List;
import java.util.Objects;
@ -39,7 +39,7 @@ public final class ENewObj extends AExpression {
private final String type;
private final List<AExpression> arguments;
private Method constructor;
private PainlessMethod constructor;
public ENewObj(Location location, String type, List<AExpression> arguments) {
super(location);
@ -58,13 +58,13 @@ public final class ENewObj extends AExpression {
@Override
void analyze(Locals locals) {
try {
actual = locals.getDefinition().getJavaClassFromPainlessType(this.type);
actual = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type);
} catch (IllegalArgumentException exception) {
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
}
Struct struct = locals.getDefinition().getPainlessStructFromJavaClass(actual);
constructor = struct.constructors.get(new Definition.MethodKey("<init>", arguments.size()));
PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual);
constructor = struct.constructors.get(new PainlessMethodKey("<init>", arguments.size()));
if (constructor != null) {
Class<?>[] types = new Class<?>[constructor.arguments.size()];

View File

@ -19,7 +19,7 @@
package org.elasticsearch.painless.node;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
@ -53,7 +53,7 @@ public final class ENull extends AExpression {
if (expected != null) {
if (expected.isPrimitive()) {
throw createError(new IllegalArgumentException(
"Cannot cast null to a primitive type [" + Definition.ClassToName(expected) + "]."));
"Cannot cast null to a primitive type [" + PainlessLookup.ClassToName(expected) + "]."));
}
actual = expected;

View File

@ -48,7 +48,7 @@ public final class EStatic extends AExpression {
@Override
void analyze(Locals locals) {
try {
actual = locals.getDefinition().getJavaClassFromPainlessType(type);
actual = locals.getPainlessLookup().getJavaClassFromPainlessType(type);
} catch (IllegalArgumentException exception) {
throw createError(new IllegalArgumentException("Not a type [" + type + "]."));
}

View File

@ -21,8 +21,8 @@ package org.elasticsearch.painless.node;
import org.elasticsearch.painless.AnalyzerCaster;
import org.elasticsearch.painless.DefBootstrap;
import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.Definition.def;
import org.elasticsearch.painless.lookup.PainlessLookup;
import org.elasticsearch.painless.lookup.PainlessLookup.def;
import org.elasticsearch.painless.Globals;
import org.elasticsearch.painless.Locals;
import org.elasticsearch.painless.Location;
@ -93,7 +93,7 @@ public final class EUnary extends AExpression {
promote = AnalyzerCaster.promoteNumeric(child.actual, false);
if (promote == null) {
throw createError(new ClassCastException("Cannot apply not [~] to type [" + Definition.ClassToName(child.actual) + "]."));
throw createError(new ClassCastException("Cannot apply not [~] to type [" + PainlessLookup.ClassToName(child.actual) + "]."));
}
child.expected = promote;
@ -122,7 +122,8 @@ public final class EUnary extends AExpression {
promote = AnalyzerCaster.promoteNumeric(child.actual, true);
if (promote == null) {
throw createError(new ClassCastException("Cannot apply positive [+] to type [" + Definition.ClassToName(child.actual) + "]."));
throw createError(
new ClassCastException("Cannot apply positive [+] to type [" + PainlessLookup.ClassToName(child.actual) + "]."));
}
child.expected = promote;
@ -155,7 +156,8 @@ public final class EUnary extends AExpression {
promote = AnalyzerCaster.promoteNumeric(child.actual, true);
if (promote == null) {
throw createError(new ClassCastException("Cannot apply negative [-] to type [" + Definition.ClassToName(child.actual) + "]."));
throw createError(
new ClassCastException("Cannot apply negative [-] to type [" + PainlessLookup.ClassToName(child.actual) + "]."));
}
child.expected = promote;

Some files were not shown because too many files have changed in this diff Show More