Skip to content

Commit 9364982

Browse files
committed
[FEATURE] Get rid of the dependency on OpenSearch core
Signed-off-by: Andriy Redko <[email protected]>
1 parent 52679c3 commit 9364982

17 files changed

+3752
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.client.transport.httpclient5;
10+
11+
import java.util.AbstractMap;
12+
import java.util.ArrayList;
13+
import java.util.Collection;
14+
import java.util.Collections;
15+
import java.util.List;
16+
import java.util.Locale;
17+
import java.util.Map;
18+
import java.util.Objects;
19+
import java.util.Map.Entry;
20+
import java.util.function.Function;
21+
import java.util.stream.Collectors;
22+
23+
import org.apache.hc.client5.http.config.RequestConfig;
24+
import org.apache.hc.core5.http.Header;
25+
import org.apache.hc.core5.http.message.BasicHeader;
26+
import org.apache.hc.core5.http.nio.AsyncResponseConsumer;
27+
import org.opensearch.client.transport.TransportOptions;
28+
import org.opensearch.client.transport.Version;
29+
30+
public class ApacheHttpClient5Options implements TransportOptions {
31+
private static final String USER_AGENT = "User-Agent";
32+
33+
/**
34+
* Default request options.
35+
*/
36+
public static final ApacheHttpClient5Options DEFAULT = new Builder(
37+
Collections.emptyList(),
38+
HttpAsyncResponseConsumerFactory.HeapBufferedResponseConsumerFactory.DEFAULT,
39+
null,
40+
null
41+
).build();
42+
43+
private final List<Header> headers;
44+
private final HttpAsyncResponseConsumerFactory httpAsyncResponseConsumerFactory;
45+
private final WarningsHandler warningsHandler;
46+
private final RequestConfig requestConfig;
47+
48+
private ApacheHttpClient5Options(Builder builder) {
49+
this.headers = Collections.unmodifiableList(new ArrayList<>(builder.headers));
50+
this.httpAsyncResponseConsumerFactory = builder.httpAsyncResponseConsumerFactory;
51+
this.warningsHandler = builder.warningsHandler;
52+
this.requestConfig = builder.requestConfig;
53+
}
54+
55+
public HttpAsyncResponseConsumerFactory getHttpAsyncResponseConsumerFactory() {
56+
return httpAsyncResponseConsumerFactory;
57+
}
58+
59+
public WarningsHandler getWarningsHandler() {
60+
return warningsHandler;
61+
}
62+
63+
public RequestConfig getRequestConfig() {
64+
return requestConfig;
65+
}
66+
67+
@Override
68+
public Collection<Entry<String, String>> headers() {
69+
return headers.stream()
70+
.map(h -> new AbstractMap.SimpleImmutableEntry<>(h.getName(), h.getValue()))
71+
.collect(Collectors.toList());
72+
}
73+
74+
@Override
75+
public Map<String, String> queryParameters() {
76+
return null;
77+
}
78+
79+
@Override
80+
public Function<List<String>, Boolean> onWarnings() {
81+
if (warningsHandler == null) {
82+
return null;
83+
} else {
84+
return warnings -> warningsHandler.warningsShouldFailRequest(warnings);
85+
}
86+
}
87+
88+
@Override
89+
public Builder toBuilder() {
90+
return new Builder(headers, httpAsyncResponseConsumerFactory, warningsHandler, requestConfig);
91+
}
92+
93+
public static class Builder implements TransportOptions.Builder {
94+
private final List<Header> headers;
95+
private HttpAsyncResponseConsumerFactory httpAsyncResponseConsumerFactory;
96+
private WarningsHandler warningsHandler;
97+
private RequestConfig requestConfig;
98+
99+
private Builder(Builder builder) {
100+
this(builder.headers, builder.httpAsyncResponseConsumerFactory,
101+
builder.warningsHandler, builder.requestConfig);
102+
}
103+
104+
private Builder(
105+
List<Header> headers,
106+
HttpAsyncResponseConsumerFactory httpAsyncResponseConsumerFactory,
107+
WarningsHandler warningsHandler,
108+
RequestConfig requestConfig
109+
) {
110+
this.headers = new ArrayList<>(headers);
111+
this.httpAsyncResponseConsumerFactory = httpAsyncResponseConsumerFactory;
112+
this.warningsHandler = warningsHandler;
113+
this.requestConfig = requestConfig;
114+
}
115+
116+
/**
117+
* Add the provided header to the request.
118+
*
119+
* @param name the header name
120+
* @param value the header value
121+
* @throws NullPointerException if {@code name} or {@code value} is null.
122+
*/
123+
@Override
124+
public Builder addHeader(String name, String value) {
125+
Objects.requireNonNull(name, "header name cannot be null");
126+
Objects.requireNonNull(value, "header value cannot be null");
127+
this.headers.add(new ReqHeader(name, value));
128+
return this;
129+
}
130+
131+
@Override
132+
public TransportOptions.Builder setParameter(String name, String value) {
133+
return this;
134+
}
135+
136+
/**
137+
* Called if there are warnings to determine if those warnings should fail the request.
138+
*/
139+
@Override
140+
public TransportOptions.Builder onWarnings(Function<List<String>, Boolean> listener) {
141+
if (listener == null) {
142+
setWarningsHandler(null);
143+
} else {
144+
setWarningsHandler(w -> {
145+
if (w != null && !w.isEmpty()) {
146+
return listener.apply(w);
147+
} else {
148+
return false;
149+
}
150+
});
151+
}
152+
153+
return this;
154+
}
155+
156+
/**
157+
* Set the {@link HttpAsyncResponseConsumerFactory} used to create one
158+
* {@link AsyncResponseConsumer} callback per retry. Controls how the
159+
* response body gets streamed from a non-blocking HTTP connection on the
160+
* client side.
161+
*
162+
* @param httpAsyncResponseConsumerFactory factory for creating {@link AsyncResponseConsumer}.
163+
* @throws NullPointerException if {@code httpAsyncResponseConsumerFactory} is null.
164+
*/
165+
public void setHttpAsyncResponseConsumerFactory(HttpAsyncResponseConsumerFactory httpAsyncResponseConsumerFactory) {
166+
this.httpAsyncResponseConsumerFactory = Objects.requireNonNull(
167+
httpAsyncResponseConsumerFactory,
168+
"httpAsyncResponseConsumerFactory cannot be null"
169+
);
170+
}
171+
172+
/**
173+
* How this request should handle warnings. If null (the default) then
174+
* this request will default to the behavior dictacted by
175+
* `setStrictDeprecationMode`.
176+
* <p>
177+
* This can be set to {@link WarningsHandler#PERMISSIVE} if the client
178+
* should ignore all warnings which is the same behavior as setting
179+
* strictDeprecationMode to true. It can be set to
180+
* {@link WarningsHandler#STRICT} if the client should fail if there are
181+
* any warnings which is the same behavior as settings
182+
* strictDeprecationMode to false.
183+
* <p>
184+
* It can also be set to a custom implementation of
185+
* {@linkplain WarningsHandler} to permit only certain warnings or to
186+
* fail the request if the warnings returned don't
187+
* <strong>exactly</strong> match some set.
188+
*
189+
* @param warningsHandler the {@link WarningsHandler} to be used
190+
*/
191+
public void setWarningsHandler(WarningsHandler warningsHandler) {
192+
this.warningsHandler = warningsHandler;
193+
}
194+
195+
/**
196+
* set RequestConfig, which can set socketTimeout, connectTimeout
197+
* and so on by request
198+
* @param requestConfig http client RequestConfig
199+
* @return Builder
200+
*/
201+
public Builder setRequestConfig(RequestConfig requestConfig) {
202+
this.requestConfig = requestConfig;
203+
return this;
204+
}
205+
206+
@Override
207+
public ApacheHttpClient5Options build() {
208+
return new ApacheHttpClient5Options(this);
209+
}
210+
}
211+
212+
static ApacheHttpClient5Options initialOptions() {
213+
String ua = String.format(
214+
Locale.ROOT,
215+
"opensearch-java/%s (Java/%s)",
216+
Version.VERSION == null ? "Unknown" : Version.VERSION.toString(),
217+
System.getProperty("java.version")
218+
);
219+
220+
return new ApacheHttpClient5Options(
221+
DEFAULT.toBuilder()
222+
.addHeader(USER_AGENT, ua)
223+
.addHeader("Accept", ApacheHttpClient5Transport.JsonContentType.toString())
224+
);
225+
}
226+
227+
static ApacheHttpClient5Options of(TransportOptions options) {
228+
if (options instanceof ApacheHttpClient5Options) {
229+
return (ApacheHttpClient5Options)options;
230+
231+
} else {
232+
final Builder builder = new Builder(DEFAULT.toBuilder());
233+
options.headers().forEach(h -> builder.addHeader(h.getKey(), h.getValue()));
234+
options.queryParameters().forEach(builder::setParameter);
235+
builder.onWarnings(options.onWarnings());
236+
return builder.build();
237+
}
238+
}
239+
240+
/**
241+
* Custom implementation of {@link BasicHeader} that overrides equals and
242+
* hashCode so it is easier to test equality of {@link ApacheHttpClient5Options}.
243+
*/
244+
static final class ReqHeader extends BasicHeader {
245+
ReqHeader(String name, String value) {
246+
super(name, value);
247+
}
248+
249+
@Override
250+
public boolean equals(Object other) {
251+
if (this == other) {
252+
return true;
253+
}
254+
if (other instanceof ReqHeader) {
255+
Header otherHeader = (Header) other;
256+
return Objects.equals(getName(), otherHeader.getName()) && Objects.equals(getValue(), otherHeader.getValue());
257+
}
258+
return false;
259+
}
260+
261+
@Override
262+
public int hashCode() {
263+
return Objects.hash(getName(), getValue());
264+
}
265+
}
266+
}

0 commit comments

Comments
 (0)