Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
12df7fa
HSEARCH-5464 Remove the apache host from ElasticsearchResponse
marko-bekhta Aug 27, 2025
8a2f651
HSEARCH-5464 Move out client common classes
marko-bekhta Aug 27, 2025
b55cdae
HSEARCH-5464 Remove dependency on ES version class in the client code
marko-bekhta Aug 27, 2025
2b8a46b
HSEARCH-5464 Continue factoring out the Client
marko-bekhta Sep 1, 2025
98be8f2
HSEARCH-5464 OpenSearch client
marko-bekhta Sep 3, 2025
b233d76
HSEARCH-5464 Elasticsearch-java client
marko-bekhta Sep 4, 2025
99829f7
HSEARCH-5464 Test different clients
marko-bekhta Sep 4, 2025
46dcd5e
HSEARCH-5464 Rename the clients
marko-bekhta Sep 4, 2025
0265ffe
HSEARCH-5464 Adjust elasticsearch-java tests
marko-bekhta Sep 5, 2025
7bbd391
HSEARCH-5464 Fix aggregated javadocs and dependency alignment
marko-bekhta Sep 5, 2025
9fa1417
HSEARCH-5464 More renames to address JQAssistant rules
marko-bekhta Sep 5, 2025
ae517ef
HSEARCH-5464 Explicitly list the apache http client libs we use to im…
marko-bekhta Sep 5, 2025
4f34aea
HSEARCH-5464 Push client specific settings to client modules
marko-bekhta Sep 9, 2025
76dfab4
HSEARCH-5464 Make AWS signing compatible with other client impls
marko-bekhta Sep 9, 2025
5bb0448
HSEARCH-5464 Adjust search-util-common exports
marko-bekhta Sep 9, 2025
40e92a8
HSEARCH-5464 Add missing platform dependencies
marko-bekhta Oct 13, 2025
0576d04
HSEARCH-5464 Add a few notes on the new pluggable clients
marko-bekhta Oct 16, 2025
64fbea2
HSEARCH-5464 Move (most of) the elasticsearch-client-rest back into …
marko-bekhta Oct 23, 2025
8e5cec1
HSEARCH-5464 Address some of the review comments
marko-bekhta Oct 23, 2025
7cc3d5a
HSEARCH-5464 Switch to the new elasticsearch-rest5-client
marko-bekhta Oct 24, 2025
d00fa96
HSEARCH-5464 Rename Elasticsearch clients and their packages
marko-bekhta Oct 24, 2025
65d49d3
HSEARCH-5464 Clarify a few points in the docs
marko-bekhta Oct 24, 2025
29e679a
HSEARCH-5464 Mark rest5 and opensearch clients as incubating
marko-bekhta Oct 24, 2025
c399484
HSEARCH-5464 Finish with renaming and JQAssistant
marko-bekhta Oct 24, 2025
d5f37ba
HSEARCH-5464 Move around gson entity to keep the common code in the b…
marko-bekhta Oct 25, 2025
d32db4d
HSEARCH-5464 Change how non-default JDK tests are executed
marko-bekhta Nov 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,9 @@ stage('Non-default environments') {
withMavenWorkspace {
// Re-run integration tests against the JARs produced by the default build,
// but using a different JDK to build and run the tests.
mavenNonDefaultBuild buildEnv, "-f integrationtest"
// we pass -Dgib.buildAll=true so that we won't skip the test modules, otherwise it may lead to
// part of test sources compiled with a more recent JDK then the one we are about to test with:
mavenNonDefaultBuild buildEnv, "-f integrationtest -Dgib.buildAll=true"
}
}
})
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import org.hibernate.search.backend.elasticsearch.aws.cfg.ElasticsearchAwsCredentialsTypeNames;
import org.hibernate.search.backend.elasticsearch.aws.spi.ElasticsearchAwsCredentialsProvider;
import org.hibernate.search.backend.elasticsearch.client.ElasticsearchHttpClientConfigurer;
import org.hibernate.search.backend.elasticsearch.client.common.spi.ElasticsearchRequestInterceptorProvider;
import org.hibernate.search.engine.environment.bean.BeanHolder;
import org.hibernate.search.engine.environment.bean.spi.BeanConfigurationContext;
import org.hibernate.search.engine.environment.bean.spi.BeanConfigurer;
Expand All @@ -17,8 +17,8 @@ public class ElasticsearchAwsBeanConfigurer implements BeanConfigurer {
@Override
public void configure(BeanConfigurationContext context) {
context.define(
ElasticsearchHttpClientConfigurer.class,
beanResolver -> BeanHolder.of( new ElasticsearchAwsHttpClientConfigurer() )
ElasticsearchRequestInterceptorProvider.class,
beanResolver -> BeanHolder.of( new ElasticsearchAwsSigningInterceptorProvider() )
);
context.define(
ElasticsearchAwsCredentialsProvider.class, ElasticsearchAwsCredentialsTypeNames.DEFAULT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@
*/
package org.hibernate.search.backend.elasticsearch.aws.impl;

import org.hibernate.search.backend.elasticsearch.ElasticsearchDistributionName;
import org.hibernate.search.backend.elasticsearch.ElasticsearchVersion;
import java.util.Locale;
import java.util.Optional;
import java.util.regex.Pattern;

import org.hibernate.search.backend.elasticsearch.aws.cfg.ElasticsearchAwsBackendSettings;
import org.hibernate.search.backend.elasticsearch.aws.cfg.ElasticsearchAwsCredentialsTypeNames;
import org.hibernate.search.backend.elasticsearch.aws.logging.impl.AwsLog;
import org.hibernate.search.backend.elasticsearch.aws.spi.ElasticsearchAwsCredentialsProvider;
import org.hibernate.search.backend.elasticsearch.client.ElasticsearchHttpClientConfigurationContext;
import org.hibernate.search.backend.elasticsearch.client.ElasticsearchHttpClientConfigurer;
import org.hibernate.search.backend.elasticsearch.client.common.spi.ElasticsearchRequestInterceptor;
import org.hibernate.search.backend.elasticsearch.client.common.spi.ElasticsearchRequestInterceptorProvider;
import org.hibernate.search.backend.elasticsearch.client.common.spi.ElasticsearchRequestInterceptorProviderContext;
import org.hibernate.search.engine.cfg.ConfigurationPropertySource;
import org.hibernate.search.engine.cfg.spi.ConfigurationProperty;
import org.hibernate.search.engine.cfg.spi.OptionalConfigurationProperty;
Expand All @@ -22,8 +25,8 @@
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.regions.Region;

public class ElasticsearchAwsHttpClientConfigurer implements ElasticsearchHttpClientConfigurer {

public class ElasticsearchAwsSigningInterceptorProvider implements ElasticsearchRequestInterceptorProvider {
private static final Pattern DISTRIBUTION_NAME_PATTERN = Pattern.compile( "([^\\d]+)?(?:(?<=^)|(?=$)|(?<=.):(?=.))(.+)?" );
private static final ConfigurationProperty<Boolean> SIGNING_ENABLED =
ConfigurationProperty.forKey( ElasticsearchAwsBackendSettings.SIGNING_ENABLED )
.asBoolean()
Expand Down Expand Up @@ -52,36 +55,48 @@ public class ElasticsearchAwsHttpClientConfigurer implements ElasticsearchHttpCl
.asString()
.build();

static final OptionalConfigurationProperty<String> DISTRIBUTION_NAME =
ConfigurationProperty.forKey( "version" )
.asString()
.build();

@Override
public void configure(ElasticsearchHttpClientConfigurationContext context) {
public Optional<ElasticsearchRequestInterceptor> provide(ElasticsearchRequestInterceptorProviderContext context) {
ConfigurationPropertySource propertySource = context.configurationPropertySource();

if ( !SIGNING_ENABLED.get( propertySource ) ) {
AwsLog.INSTANCE.signingDisabled();
return;
return Optional.empty();
}

Region region = REGION.getAndMapOrThrow( propertySource, Region::of, AwsLog.INSTANCE::missingPropertyForSigning );
String service;
switch ( context.configuredVersion().map( ElasticsearchVersion::distribution )
.orElse( ElasticsearchDistributionName.OPENSEARCH ) ) {
case AMAZON_OPENSEARCH_SERVERLESS:
service = "aoss";
break;
case ELASTIC:
case OPENSEARCH:
default:
service = "es";
break;

String distributionName = DISTRIBUTION_NAME.getAndTransform( propertySource,
v -> v.map( ver -> ver.toLowerCase( Locale.ROOT ) )
.map( DISTRIBUTION_NAME_PATTERN::matcher )
.map( matcher -> {
if ( matcher.matches() ) {
return matcher.group( 1 );
}
return null;
} ).orElse( "opensearch" ) );

if ( "amazon-opensearch-serverless".equals( distributionName ) ) {
service = "aoss";
}
else {
service = "es";
}

AwsCredentialsProvider credentialsProvider = createCredentialsProvider( context.beanResolver(), propertySource );

AwsLog.INSTANCE.signingEnabled( region, service, credentialsProvider );

AwsSigningRequestInterceptor signingInterceptor =
new AwsSigningRequestInterceptor( region, service, credentialsProvider );
ElasticsearchAwsSigningRequestInterceptor signingInterceptor =
new ElasticsearchAwsSigningRequestInterceptor( region, service, credentialsProvider );

context.clientBuilder().addInterceptorLast( signingInterceptor );
return Optional.of( signingInterceptor );
}

private AwsCredentialsProvider createCredentialsProvider(BeanResolver beanResolver,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.search.backend.elasticsearch.aws.impl;

import java.io.IOException;

import org.hibernate.search.backend.elasticsearch.aws.logging.impl.AwsLog;
import org.hibernate.search.backend.elasticsearch.client.common.spi.ElasticsearchRequestInterceptor;
import org.hibernate.search.backend.elasticsearch.client.common.spi.ElasticsearchRequestInterceptorContext;

import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.http.ContentStreamProvider;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner;
import software.amazon.awssdk.http.auth.spi.signer.SignedRequest;
import software.amazon.awssdk.regions.Region;

class ElasticsearchAwsSigningRequestInterceptor implements ElasticsearchRequestInterceptor {

private final AwsV4HttpSigner signer;
private final Region region;
private final String service;
private final AwsCredentialsProvider credentialsProvider;

ElasticsearchAwsSigningRequestInterceptor(Region region, String service, AwsCredentialsProvider credentialsProvider) {
this.signer = AwsV4HttpSigner.create();
this.region = region;
this.service = service;
this.credentialsProvider = credentialsProvider;
}

@Override
public void intercept(ElasticsearchRequestInterceptorContext requestContext) throws IOException {
try ( HttpEntityContentStreamProvider contentStreamProvider =
HttpEntityContentStreamProvider.create( requestContext ) ) {
sign( requestContext, contentStreamProvider );
}
}

private void sign(ElasticsearchRequestInterceptorContext requestContext,
HttpEntityContentStreamProvider contentStreamProvider) {
SdkHttpFullRequest awsRequest = toAwsRequest( requestContext, contentStreamProvider );

if ( AwsLog.INSTANCE.isTraceEnabled() ) {
AwsLog.INSTANCE.httpRequestBeforeSigning( requestContext );
AwsLog.INSTANCE.awsRequestBeforeSigning( awsRequest );
}

AwsCredentials credentials = credentialsProvider.resolveCredentials();
AwsLog.INSTANCE.awsCredentials( credentials );

SignedRequest signedRequest = signer.sign( r -> r.identity( credentials )
.request( awsRequest )
.payload( awsRequest.contentStreamProvider().orElse( null ) )
.putProperty( AwsV4HttpSigner.SERVICE_SIGNING_NAME, service )
.putProperty( AwsV4HttpSigner.REGION_NAME, region.id() ) );

// The AWS SDK added some headers.
// Let's just override the existing headers with whatever the AWS SDK came up with.
// We don't expect signing to affect anything else (path, query, content, ...).
requestContext.overrideHeaders( signedRequest.request().headers() );

if ( AwsLog.INSTANCE.isTraceEnabled() ) {
AwsLog.INSTANCE.httpRequestAfterSigning( signedRequest );
AwsLog.INSTANCE.awsRequestAfterSigning( requestContext );
}
}

private SdkHttpFullRequest toAwsRequest(
ElasticsearchRequestInterceptorContext requestContext,
ContentStreamProvider contentStreamProvider) {
SdkHttpFullRequest.Builder awsRequestBuilder = SdkHttpFullRequest.builder();

awsRequestBuilder.host( requestContext.host() );
awsRequestBuilder.port( requestContext.port() );
awsRequestBuilder.protocol( requestContext.scheme() );

awsRequestBuilder.method( SdkHttpMethod.fromValue( requestContext.method() ) );

String path = requestContext.path();

// For some reason this is needed on Amazon OpenSearch Serverless
if ( "aoss".equals( service ) ) {
awsRequestBuilder.appendHeader( "x-amz-content-sha256", "required" );
}

awsRequestBuilder.encodedPath( path );
for ( var param : requestContext.queryParameters().entrySet() ) {
awsRequestBuilder.appendRawQueryParameter( param.getKey(), param.getValue() );
}

// Do NOT copy the headers, as the AWS SDK will sometimes sign some headers
// that are not properly taken into account by the AWS servers (e.g. content-length).

awsRequestBuilder.contentStreamProvider( contentStreamProvider );

return awsRequestBuilder.build();
}

}
Loading