Skip to content

Commit 5cec601

Browse files
authored
Merge pull request #427 from weaviate/v6-custom-truststore
v6: Pass custom TrustStore to WeaviateClient
2 parents 032d30a + 972e89f commit 5cec601

13 files changed

Lines changed: 454 additions & 22 deletions

File tree

pom.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@
9090
<groupId>io.grpc</groupId>
9191
<artifactId>grpc-netty-shaded</artifactId>
9292
<version>${grpc-netty-shaded.version}</version>
93-
<scope>runtime</scope>
9493
</dependency>
9594
<dependency>
9695
<groupId>io.grpc</groupId>

src/main/java/io/weaviate/client6/v1/api/Config.java

Lines changed: 136 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import java.util.Map;
66
import java.util.function.Function;
77

8+
import javax.net.ssl.TrustManagerFactory;
9+
810
import io.weaviate.client6.v1.internal.ObjectBuilder;
911
import io.weaviate.client6.v1.internal.TokenProvider;
1012
import io.weaviate.client6.v1.internal.grpc.GrpcChannelOptions;
@@ -17,7 +19,8 @@ public record Config(
1719
String grpcHost,
1820
int grpcPort,
1921
Map<String, String> headers,
20-
TokenProvider tokenProvider) {
22+
TokenProvider tokenProvider,
23+
TrustManagerFactory trustManagerFactory) {
2124

2225
public static Config of(Function<Custom, ObjectBuilder<Config>> fn) {
2326
return fn.apply(new Custom()).build();
@@ -31,15 +34,16 @@ private Config(Builder<?> builder) {
3134
builder.grpcHost,
3235
builder.grpcPort,
3336
builder.headers,
34-
builder.tokenProvider);
37+
builder.tokenProvider,
38+
builder.trustManagerFactory);
3539
}
3640

37-
public RestTransportOptions restTransportOptions() {
38-
return new RestTransportOptions(scheme, httpHost, httpPort, headers, tokenProvider);
41+
RestTransportOptions restTransportOptions() {
42+
return new RestTransportOptions(scheme, httpHost, httpPort, headers, tokenProvider, trustManagerFactory);
3943
}
4044

41-
public GrpcChannelOptions grpcTransportOptions() {
42-
return new GrpcChannelOptions(scheme, grpcHost, grpcPort, headers, tokenProvider);
45+
GrpcChannelOptions grpcTransportOptions() {
46+
return new GrpcChannelOptions(scheme, grpcHost, grpcPort, headers, tokenProvider, trustManagerFactory);
4347
}
4448

4549
private abstract static class Builder<SELF extends Builder<SELF>> implements ObjectBuilder<Config> {
@@ -50,20 +54,33 @@ private abstract static class Builder<SELF extends Builder<SELF>> implements Obj
5054
protected String grpcHost;
5155
protected int grpcPort;
5256
protected TokenProvider tokenProvider;
57+
protected TrustManagerFactory trustManagerFactory;
5358
protected Map<String, String> headers = new HashMap<>();
5459

60+
/**
61+
* Set URL scheme. Subclasses may increase the visibility of this method to
62+
* {@code public} if using a different scheme is allowed.
63+
*/
5564
@SuppressWarnings("unchecked")
5665
protected SELF scheme(String scheme) {
5766
this.scheme = scheme;
5867
return (SELF) this;
5968
}
6069

70+
/**
71+
* Set port for REST requests. Subclasses may increase the visibility of this
72+
* method to {@code public} if using a different port is allowed.
73+
*/
6174
@SuppressWarnings("unchecked")
6275
protected SELF httpHost(String httpHost) {
6376
this.httpHost = trimScheme(httpHost);
6477
return (SELF) this;
6578
}
6679

80+
/**
81+
* Set port for gRPC requests. Subclasses may increase the visibility of this
82+
* method to {@code public} if using a different port is allowed.
83+
*/
6784
@SuppressWarnings("unchecked")
6885
protected SELF grpcHost(String grpcHost) {
6986
this.grpcHost = trimScheme(grpcHost);
@@ -75,18 +92,41 @@ private String trimScheme(String url) {
7592
return url.replaceFirst("^https?\\/\\/", "");
7693
}
7794

95+
/**
96+
* Provide a {@link TrustManagerFactory}. Subclasses which support
97+
* secure connection should expose this method.
98+
*/
99+
@SuppressWarnings("unchecked")
100+
protected SELF trustManagerFactory(TrustManagerFactory tmf) {
101+
this.trustManagerFactory = tmf;
102+
return (SELF) this;
103+
}
104+
105+
/**
106+
* Set a single request header. The client does not support header lists,
107+
* so there is no equivalent {@code addHeader} to append to existing header.
108+
* This will be applied both to REST and gRPC requests.
109+
*/
78110
@SuppressWarnings("unchecked")
79111
public SELF setHeader(String key, String value) {
80112
this.headers.put(key, value);
81113
return (SELF) this;
82114
}
83115

116+
/**
117+
* Set multiple request headers.
118+
* This will be applied both to REST and gRPC requests.
119+
*/
84120
@SuppressWarnings("unchecked")
85121
public SELF setHeaders(Map<String, String> headers) {
86-
this.headers = Map.copyOf(headers);
122+
this.headers.putAll(Map.copyOf(headers));
87123
return (SELF) this;
88124
}
89125

126+
/**
127+
* Weaviate will use the URL in this header to call Weaviate Embeddings
128+
* Service if an appropriate vectorizer is configured for collection.
129+
*/
90130
private static final String HEADER_X_WEAVIATE_CLUSTER_URL = "X-Weaviate-Cluster-URL";
91131

92132
/**
@@ -102,13 +142,27 @@ private static boolean isWeaviateDomain(String host) {
102142

103143
@Override
104144
public Config build() {
145+
// For clusters hosted on Weaviate Cloud, Weaviate Embedding Service
146+
// will be available under the same domain.
105147
if (isWeaviateDomain(httpHost) && tokenProvider != null) {
106148
setHeader(HEADER_X_WEAVIATE_CLUSTER_URL, "https://" + httpHost + ":" + httpPort);
107149
}
108150
return new Config(this);
109151
}
110152
}
111153

154+
/**
155+
* Configuration for Weaviate instances deployed locally.
156+
*
157+
* <p>
158+
* Has sane defaults that match standard Weaviate deployment configuration:
159+
* <ul>
160+
* <li>{@code scheme: http}</li>
161+
* <li>{@code host: localhost}</li>
162+
* <li>{@code httpPort: 8080}</li>
163+
* <li>{@code grpcPort: 50051}</li>
164+
* </ul>
165+
*/
112166
public static class Local extends Builder<Local> {
113167
public Local() {
114168
scheme("http");
@@ -117,23 +171,37 @@ public Local() {
117171
grpcPort(50051);
118172
}
119173

174+
/**
175+
* Set a different hostname.
176+
* This changes both {@code httpHost} and {@code grpcHost}.
177+
*/
120178
public Local host(String host) {
121179
httpHost(host);
122180
grpcHost(host);
123181
return this;
124182
}
125183

184+
/** Override default HTTP port. */
126185
public Local httpPort(int port) {
127186
this.httpPort = port;
128187
return this;
129188
}
130189

190+
/** Override default gRPC port. */
131191
public Local grpcPort(int port) {
132192
this.grpcPort = port;
133193
return this;
134194
}
135195
}
136196

197+
/**
198+
* Configuration for instances hosted on Weaviate Cloud.
199+
* {@link WeaviateCloud} will create a secure client
200+
* with {@code schema: https} and {@code http-/grpcPort: 443}.
201+
*
202+
* Custom SSL certificates are suppored via
203+
* {@link #trustManagerFactory}.
204+
*/
137205
public static class WeaviateCloud extends Builder<WeaviateCloud> {
138206
public WeaviateCloud(String httpHost, TokenProvider tokenProvider) {
139207
this(URI.create(httpHost), tokenProvider);
@@ -144,48 +212,108 @@ public WeaviateCloud(URI clusterUri, TokenProvider tokenProvider) {
144212
super.httpHost(clusterUri.getHost() != null
145213
? clusterUri.getHost() // https://[example.com]/about
146214
: clusterUri.getPath().split("/")[0]); // [example.com]/about
147-
this.httpPort = 443;
148215
super.grpcHost("grpc-" + this.httpHost);
216+
this.httpPort = 443;
149217
this.grpcPort = 443;
150218
this.tokenProvider = tokenProvider;
151219
}
220+
221+
/**
222+
* Configure a custom TrustStore to validate third-party SSL certificates.
223+
*
224+
* <p>
225+
* Usage:
226+
*
227+
* <pre>{@code
228+
* // Create a TrustManagerFactory to validate custom certificates.
229+
* TrustManagerFactory tmf;
230+
* try (var keys = new FileInputStream("/path/to/custom/truststore.p12")) {
231+
* KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
232+
* trustStore.load(myKeys, "secret-password".toCharArra());
233+
*
234+
* tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
235+
* tmf.init(trustStore);
236+
* }
237+
*
238+
* // Pass it to wcd -> wcd.trustManagerFactory(tmf)
239+
* }</pre>
240+
*/
241+
public WeaviateCloud trustManagerFactory(TrustManagerFactory tmf) {
242+
return super.trustManagerFactory(tmf);
243+
}
152244
}
153245

246+
/** Configuration for custom Weaviate deployements. */
154247
public static class Custom extends Builder<Custom> {
155248
/**
156249
* Scheme controls which protocol will be used for the database connection.
157250
* REST and gRPC ports will be automatically inferred from it:
158251
* <strong>443</strong> for HTTPS connection and <strong>80</strong> for HTTP.
252+
*
253+
* These can be overriden with {@link #httpPort(int)} and
254+
* {@link #grpcPort(int)}.
159255
*/
160256
public Custom scheme(String scheme) {
161257
httpPort("https".equals(scheme) ? 443 : 80);
162258
grpcPort("https".equals(scheme) ? 443 : 80);
163259
return super.scheme(scheme);
164260
}
165261

262+
/** Set HTTP hostname. */
166263
public Custom httpHost(String httpHost) {
167264
super.httpHost(httpHost);
168265
return this;
169266
}
170267

268+
/** Set HTTP port. */
171269
public Custom httpPort(int port) {
172270
this.httpPort = port;
173271
return this;
174272
}
175273

274+
/** Set gRPC hostname. */
176275
public Custom grpcHost(String grpcHost) {
177276
super.grpcHost(grpcHost);
178277
return this;
179278
}
180279

280+
/** Set gRPC port. */
181281
public Custom grpcPort(int port) {
182282
this.grpcPort = port;
183283
return this;
184284
}
185285

286+
/**
287+
* Set authorization method. Setting this to {@code null} or omitting
288+
* will not use any authorization mechanism.
289+
*/
186290
public Custom authorization(TokenProvider tokenProvider) {
187291
this.tokenProvider = tokenProvider;
188292
return this;
189293
}
294+
295+
/**
296+
* Configure a custom TrustStore to validate third-party SSL certificates.
297+
*
298+
* <p>
299+
* Usage:
300+
*
301+
* <pre>{@code
302+
* // Create a TrustManagerFactory to validate custom certificates.
303+
* TrustManagerFactory tmf;
304+
* try (var keys = new FileInputStream("/path/to/custom/truststore.p12")) {
305+
* KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
306+
* trustStore.load(myKeys, "secret-password".toCharArra());
307+
*
308+
* tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
309+
* tmf.init(trustStore);
310+
* }
311+
*
312+
* // Pass it to custom -> custom.trustManagerFactory(tmf)
313+
* }</pre>
314+
*/
315+
public Custom trustManagerFactory(TrustManagerFactory tmf) {
316+
return super.trustManagerFactory(tmf);
317+
}
190318
}
191319
}

src/main/java/io/weaviate/client6/v1/api/WeaviateClient.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,32 +28,81 @@ public WeaviateClient(Config config) {
2828
this.collections = new WeaviateCollectionsClient(restTransport, grpcTransport);
2929
}
3030

31+
/**
32+
* Create {@link WeaviateClientAsync} with identical configurations.
33+
* It is a shorthand for:
34+
*
35+
* <pre>{@code
36+
* var config = new Config(...);
37+
* var client = new WeaviateClient(config);
38+
* var async = new WeaviateClientAsync(config);
39+
* }</pre>
40+
*
41+
* and as such, this does not manage or reuse resources (transport, gRPC
42+
* channel, etc) used by the original client. Keep that in mind and make
43+
* sure to close the original and async clients individually.
44+
*
45+
* <p>
46+
* Example:
47+
*
48+
* <pre>{@code
49+
* var client = WeaviateClient.local();
50+
*
51+
* // Need to make the next request non-blocking
52+
* try (final var async = client.async()) {
53+
* async.collections.create("Things");
54+
* }
55+
* // At this point only `async` resource has been auto-closed.
56+
*
57+
* client.close();
58+
* }</pre>
59+
*
60+
*
61+
* If you only intend to use {@link WeaviateClientAsync}, prefer creating it
62+
* directly via one of its static factories:
63+
* <ul>
64+
* <li>{@link WeaviateClientAsync#local}
65+
* <li>{@link WeaviateClientAsync#wcd}
66+
* <li>{@link WeaviateClientAsync#custom}
67+
* </ul>
68+
*
69+
* Otherwise the client wastes time initializing resources it will never use.
70+
*/
3171
public WeaviateClientAsync async() {
3272
return new WeaviateClientAsync(config);
3373
}
3474

75+
/** Connect to a local Weaviate instance. */
3576
public static WeaviateClient local() {
3677
return local(ObjectBuilder.identity());
3778
}
3879

80+
/** Connect to a local Weaviate instance. */
3981
public static WeaviateClient local(Function<Config.Local, ObjectBuilder<Config>> fn) {
4082
return new WeaviateClient(fn.apply(new Config.Local()).build());
4183
}
4284

85+
/** Connect to a Weaviate Cloud instance. */
4386
public static WeaviateClient wcd(String httpHost, String apiKey) {
4487
return wcd(httpHost, apiKey, ObjectBuilder.identity());
4588
}
4689

90+
/** Connect to a Weaviate Cloud instance. */
4791
public static WeaviateClient wcd(String httpHost, String apiKey,
4892
Function<Config.WeaviateCloud, ObjectBuilder<Config>> fn) {
4993
var config = new Config.WeaviateCloud(httpHost, Authorization.apiKey(apiKey));
5094
return new WeaviateClient(fn.apply(config).build());
5195
}
5296

97+
/** Connect to a Weaviate instance with custom configuration. */
5398
public static WeaviateClient custom(Function<Config.Custom, ObjectBuilder<Config>> fn) {
5499
return new WeaviateClient(fn.apply(new Config.Custom()).build());
55100
}
56101

102+
/**
103+
* Close {@link #restTransport} and {@link #grpcTransport}
104+
* and release associated resources.
105+
*/
57106
@Override
58107
public void close() throws IOException {
59108
this.restTransport.close();

0 commit comments

Comments
 (0)