Skip to content

Commit e641fcc

Browse files
authored
Rest HL client: Add get license action (#32438)
Rest HL client: Add get license action Continues to use String instead of a more complex License class to hold the license text similarly to put license. Relates #29827
1 parent 615aa85 commit e641fcc

File tree

11 files changed

+284
-31
lines changed

11 files changed

+284
-31
lines changed

client/rest-high-level/src/main/java/org/elasticsearch/client/LicenseClient.java

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,25 @@
1919

2020
package org.elasticsearch.client;
2121

22+
import org.apache.http.HttpEntity;
2223
import org.elasticsearch.action.ActionListener;
24+
import org.elasticsearch.common.Strings;
25+
import org.elasticsearch.common.io.Streams;
26+
import org.elasticsearch.common.xcontent.DeprecationHandler;
27+
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
28+
import org.elasticsearch.common.xcontent.XContentBuilder;
29+
import org.elasticsearch.common.xcontent.XContentFactory;
30+
import org.elasticsearch.common.xcontent.XContentParser;
31+
import org.elasticsearch.common.xcontent.XContentType;
32+
import org.elasticsearch.protocol.xpack.license.GetLicenseRequest;
33+
import org.elasticsearch.protocol.xpack.license.GetLicenseResponse;
2334
import org.elasticsearch.protocol.xpack.license.PutLicenseRequest;
2435
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
2536

2637
import java.io.IOException;
38+
import java.io.InputStream;
39+
import java.io.InputStreamReader;
40+
import java.nio.charset.StandardCharsets;
2741

2842
import static java.util.Collections.emptySet;
2943

@@ -54,7 +68,7 @@ public PutLicenseResponse putLicense(PutLicenseRequest request, RequestOptions o
5468
}
5569

5670
/**
57-
* Asynchronously updates license for the cluster cluster.
71+
* Asynchronously updates license for the cluster.
5872
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
5973
* @param listener the listener to be notified upon request completion
6074
*/
@@ -63,4 +77,59 @@ public void putLicenseAsync(PutLicenseRequest request, RequestOptions options, A
6377
PutLicenseResponse::fromXContent, listener, emptySet());
6478
}
6579

80+
/**
81+
* Returns the current license for the cluster.
82+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
83+
* @return the response
84+
* @throws IOException in case there is a problem sending the request or parsing back the response
85+
*/
86+
public GetLicenseResponse getLicense(GetLicenseRequest request, RequestOptions options) throws IOException {
87+
return restHighLevelClient.performRequest(request, RequestConverters::getLicense, options,
88+
response -> new GetLicenseResponse(convertResponseToJson(response)), emptySet());
89+
}
90+
91+
/**
92+
* Asynchronously returns the current license for the cluster cluster.
93+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
94+
* @param listener the listener to be notified upon request completion
95+
*/
96+
public void getLicenseAsync(GetLicenseRequest request, RequestOptions options, ActionListener<GetLicenseResponse> listener) {
97+
restHighLevelClient.performRequestAsync(request, RequestConverters::getLicense, options,
98+
response -> new GetLicenseResponse(convertResponseToJson(response)), listener, emptySet());
99+
}
100+
101+
102+
/**
103+
* Converts an entire response into a json sting
104+
*
105+
* This is useful for responses that we don't parse on the client side, but instead work as string
106+
* such as in case of the license JSON
107+
*/
108+
static String convertResponseToJson(Response response) throws IOException {
109+
HttpEntity entity = response.getEntity();
110+
if (entity == null) {
111+
throw new IllegalStateException("Response body expected but not returned");
112+
}
113+
if (entity.getContentType() == null) {
114+
throw new IllegalStateException("Elasticsearch didn't return the [Content-Type] header, unable to parse response body");
115+
}
116+
XContentType xContentType = XContentType.fromMediaTypeOrFormat(entity.getContentType().getValue());
117+
if (xContentType == null) {
118+
throw new IllegalStateException("Unsupported Content-Type: " + entity.getContentType().getValue());
119+
}
120+
if (xContentType == XContentType.JSON) {
121+
// No changes is required
122+
return Streams.copyToString(new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8));
123+
} else {
124+
// Need to convert into JSON
125+
try (InputStream stream = response.getEntity().getContent();
126+
XContentParser parser = XContentFactory.xContent(xContentType).createParser(NamedXContentRegistry.EMPTY,
127+
DeprecationHandler.THROW_UNSUPPORTED_OPERATION, stream)) {
128+
parser.nextToken();
129+
XContentBuilder builder = XContentFactory.jsonBuilder();
130+
builder.copyCurrentStructure(parser);
131+
return Strings.toString(builder);
132+
}
133+
}
134+
}
66135
}

client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,11 @@
107107
import org.elasticsearch.index.VersionType;
108108
import org.elasticsearch.index.rankeval.RankEvalRequest;
109109
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
110+
import org.elasticsearch.protocol.xpack.license.GetLicenseRequest;
111+
import org.elasticsearch.protocol.xpack.license.PutLicenseRequest;
110112
import org.elasticsearch.protocol.xpack.watcher.DeleteWatchRequest;
111113
import org.elasticsearch.protocol.xpack.watcher.PutWatchRequest;
112114
import org.elasticsearch.protocol.xpack.XPackUsageRequest;
113-
import org.elasticsearch.protocol.xpack.license.PutLicenseRequest;
114115
import org.elasticsearch.rest.action.search.RestSearchAction;
115116
import org.elasticsearch.script.mustache.MultiSearchTemplateRequest;
116117
import org.elasticsearch.script.mustache.SearchTemplateRequest;
@@ -1154,7 +1155,11 @@ static Request xpackUsage(XPackUsageRequest usageRequest) {
11541155
}
11551156

11561157
static Request putLicense(PutLicenseRequest putLicenseRequest) {
1157-
Request request = new Request(HttpPut.METHOD_NAME, "/_xpack/license");
1158+
String endpoint = new EndpointBuilder()
1159+
.addPathPartAsIs("_xpack")
1160+
.addPathPartAsIs("license")
1161+
.build();
1162+
Request request = new Request(HttpPut.METHOD_NAME, endpoint);
11581163
Params parameters = new Params(request);
11591164
parameters.withTimeout(putLicenseRequest.timeout());
11601165
parameters.withMasterTimeout(putLicenseRequest.masterNodeTimeout());
@@ -1165,6 +1170,18 @@ static Request putLicense(PutLicenseRequest putLicenseRequest) {
11651170
return request;
11661171
}
11671172

1173+
1174+
static Request getLicense(GetLicenseRequest getLicenseRequest) {
1175+
String endpoint = new EndpointBuilder()
1176+
.addPathPartAsIs("_xpack")
1177+
.addPathPartAsIs("license")
1178+
.build();
1179+
Request request = new Request(HttpGet.METHOD_NAME, endpoint);
1180+
Params parameters = new Params(request);
1181+
parameters.withLocal(getLicenseRequest.local());
1182+
return request;
1183+
}
1184+
11681185
private static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType) throws IOException {
11691186
BytesRef source = XContentHelper.toXContent(toXContent, xContentType, false).toBytesRef();
11701187
return new ByteArrayEntity(source.bytes, source.offset, source.length, createContentType(xContentType));

client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/LicensingDocumentationIT.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
2626
import org.elasticsearch.client.RequestOptions;
2727
import org.elasticsearch.client.RestHighLevelClient;
28+
import org.elasticsearch.protocol.xpack.license.GetLicenseRequest;
29+
import org.elasticsearch.protocol.xpack.license.GetLicenseResponse;
2830
import org.elasticsearch.protocol.xpack.license.LicensesStatus;
2931
import org.elasticsearch.protocol.xpack.license.PutLicenseRequest;
3032
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
@@ -33,6 +35,8 @@
3335
import java.util.concurrent.CountDownLatch;
3436
import java.util.concurrent.TimeUnit;
3537

38+
import static org.hamcrest.Matchers.containsString;
39+
import static org.hamcrest.Matchers.endsWith;
3640
import static org.hamcrest.Matchers.hasSize;
3741
import static org.hamcrest.Matchers.not;
3842
import static org.hamcrest.Matchers.startsWith;
@@ -105,4 +109,62 @@ public void onFailure(Exception e) {
105109
assertTrue(latch.await(30L, TimeUnit.SECONDS));
106110
}
107111
}
112+
113+
public void testGetLicense() throws Exception {
114+
RestHighLevelClient client = highLevelClient();
115+
{
116+
//tag::get-license-execute
117+
GetLicenseRequest request = new GetLicenseRequest();
118+
119+
GetLicenseResponse response = client.license().getLicense(request, RequestOptions.DEFAULT);
120+
//end::get-license-execute
121+
122+
//tag::get-license-response
123+
String currentLicense = response.getLicenseDefinition(); // <1>
124+
//end::get-license-response
125+
126+
assertThat(currentLicense, containsString("trial"));
127+
assertThat(currentLicense, containsString("client_rest-high-level_integTestCluster"));
128+
}
129+
{
130+
GetLicenseRequest request = new GetLicenseRequest();
131+
// tag::get-license-execute-listener
132+
ActionListener<GetLicenseResponse> listener = new ActionListener<GetLicenseResponse>() {
133+
@Override
134+
public void onResponse(GetLicenseResponse indexResponse) {
135+
// <1>
136+
}
137+
138+
@Override
139+
public void onFailure(Exception e) {
140+
// <2>
141+
}
142+
};
143+
// end::get-license-execute-listener
144+
145+
// Replace the empty listener by a blocking listener in test
146+
final CountDownLatch latch = new CountDownLatch(1);
147+
listener = new LatchedActionListener<>(listener, latch);
148+
149+
// tag::get-license-execute-async
150+
client.license().getLicenseAsync(
151+
request, RequestOptions.DEFAULT, listener); // <1>
152+
// end::get-license-execute-async
153+
154+
assertTrue(latch.await(30L, TimeUnit.SECONDS));
155+
}
156+
{
157+
GetLicenseRequest request = new GetLicenseRequest();
158+
RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
159+
// Make sure that it still works in other formats
160+
builder.addHeader("Accept", randomFrom("application/smile", "application/cbor"));
161+
RequestOptions options = builder.build();
162+
GetLicenseResponse response = client.license().getLicense(request, options);
163+
String currentLicense = response.getLicenseDefinition();
164+
assertThat(currentLicense, startsWith("{"));
165+
assertThat(currentLicense, containsString("trial"));
166+
assertThat(currentLicense, containsString("client_rest-high-level_integTestCluster"));
167+
assertThat(currentLicense, endsWith("}"));
168+
}
169+
}
108170
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
[[java-rest-high-get-license]]
2+
=== Get License
3+
4+
[[java-rest-high-get-license-execution]]
5+
==== Execution
6+
7+
The license can be added or updated using the `getLicense()` method:
8+
9+
["source","java",subs="attributes,callouts,macros"]
10+
--------------------------------------------------
11+
include-tagged::{doc-tests}/LicensingDocumentationIT.java[get-license-execute]
12+
--------------------------------------------------
13+
14+
[[java-rest-high-get-license-response]]
15+
==== Response
16+
17+
The returned `GetLicenseResponse` contains the license in the JSON format.
18+
19+
["source","java",subs="attributes,callouts,macros"]
20+
--------------------------------------------------
21+
include-tagged::{doc-tests}/LicensingDocumentationIT.java[get-license-response]
22+
--------------------------------------------------
23+
<1> The text of the license.
24+
25+
[[java-rest-high-get-license-async]]
26+
==== Asynchronous Execution
27+
28+
This request can be executed asynchronously:
29+
30+
["source","java",subs="attributes,callouts,macros"]
31+
--------------------------------------------------
32+
include-tagged::{doc-tests}/LicensingDocumentationIT.java[get-license-execute-async]
33+
--------------------------------------------------
34+
<1> The `GetLicenseRequest` to execute and the `ActionListener` to use when
35+
the execution completes
36+
37+
The asynchronous method does not block and returns immediately. Once it is
38+
completed the `ActionListener` is called back using the `onResponse` method
39+
if the execution successfully completed or using the `onFailure` method if
40+
it failed.
41+
42+
A typical listener for `GetLicenseResponse` looks like:
43+
44+
["source","java",subs="attributes,callouts,macros"]
45+
--------------------------------------------------
46+
include-tagged::{doc-tests}/LicensingDocumentationIT.java[get-license-execute-listener]
47+
--------------------------------------------------
48+
<1> Called when the execution is successfully completed. The response is
49+
provided as an argument
50+
<2> Called in case of failure. The raised exception is provided as an argument

x-pack/plugin/core/src/main/java/org/elasticsearch/license/GetLicenseRequest.java

Lines changed: 0 additions & 28 deletions
This file was deleted.

x-pack/plugin/core/src/main/java/org/elasticsearch/license/GetLicenseRequestBuilder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import org.elasticsearch.action.support.master.MasterNodeReadOperationRequestBuilder;
99
import org.elasticsearch.client.ElasticsearchClient;
10+
import org.elasticsearch.protocol.xpack.license.GetLicenseRequest;
1011

1112
public class GetLicenseRequestBuilder extends MasterNodeReadOperationRequestBuilder<GetLicenseRequest, GetLicenseResponse,
1213
GetLicenseRequestBuilder> {

x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicensingClient.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import org.elasticsearch.action.ActionListener;
99
import org.elasticsearch.client.ElasticsearchClient;
10+
import org.elasticsearch.protocol.xpack.license.GetLicenseRequest;
1011
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
1112

1213
public class LicensingClient {

x-pack/plugin/core/src/main/java/org/elasticsearch/license/RestGetLicenseAction.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.elasticsearch.common.settings.Settings;
1010
import org.elasticsearch.common.xcontent.ToXContent;
1111
import org.elasticsearch.common.xcontent.XContentBuilder;
12+
import org.elasticsearch.protocol.xpack.license.GetLicenseRequest;
1213
import org.elasticsearch.rest.BytesRestResponse;
1314
import org.elasticsearch.rest.RestController;
1415
import org.elasticsearch.rest.RestRequest;

x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportGetLicenseAction.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.elasticsearch.cluster.service.ClusterService;
1717
import org.elasticsearch.common.inject.Inject;
1818
import org.elasticsearch.common.settings.Settings;
19+
import org.elasticsearch.protocol.xpack.license.GetLicenseRequest;
1920
import org.elasticsearch.threadpool.ThreadPool;
2021
import org.elasticsearch.transport.TransportService;
2122

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.protocol.xpack.license;
20+
21+
import org.elasticsearch.action.ActionRequestValidationException;
22+
import org.elasticsearch.action.support.master.MasterNodeReadRequest;
23+
import org.elasticsearch.common.io.stream.StreamInput;
24+
25+
import java.io.IOException;
26+
27+
28+
public class GetLicenseRequest extends MasterNodeReadRequest<GetLicenseRequest> {
29+
30+
public GetLicenseRequest() {
31+
}
32+
33+
public GetLicenseRequest(StreamInput in) throws IOException {
34+
super(in);
35+
}
36+
37+
@Override
38+
public ActionRequestValidationException validate() {
39+
return null;
40+
}
41+
}

0 commit comments

Comments
 (0)