From ab6efbb6566bd62037ccb7e54901cbab5532b765 Mon Sep 17 00:00:00 2001 From: Sushilkumar Pawar Date: Thu, 25 Jun 2026 17:54:55 +0530 Subject: [PATCH 1/4] Implement review comments and fix testcases --- .../com/emc/object/AbstractJerseyClient.java | 22 ++++++++++++++----- .../java/com/emc/object/EntityRequest.java | 4 ++++ .../object/s3/request/PutObjectRequest.java | 9 ++++++++ .../object/s3/request/UploadPartRequest.java | 5 +++++ .../s3/S3EncryptionClientBasicTest.java | 5 +++++ .../com/emc/object/s3/S3JerseyClientTest.java | 15 +++++++++---- 6 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/emc/object/AbstractJerseyClient.java b/src/main/java/com/emc/object/AbstractJerseyClient.java index e54a807a..808f027d 100644 --- a/src/main/java/com/emc/object/AbstractJerseyClient.java +++ b/src/main/java/com/emc/object/AbstractJerseyClient.java @@ -26,6 +26,8 @@ */ package com.emc.object; +import java.io.BufferedInputStream; +import java.io.InputStream; import java.net.URI; import java.util.Map; @@ -70,6 +72,21 @@ protected com.emc.object.s3.jersey.RetryFilter getRetryFilter() { @SuppressWarnings("unchecked") protected Response executeRequest(Client client, ObjectRequest request) { com.emc.object.s3.jersey.RetryFilter retryFilter = getRetryFilter(); + InputStream entityStream = null; + if (retryFilter != null && request instanceof EntityRequest) { + Object entity = ((EntityRequest) request).getEntity(); + if (entity instanceof InputStream) { + int bufSize = retryFilter.getRetryBufferSize(); + InputStream is = (InputStream) entity; + InputStream buffered = is.markSupported() ? is : new BufferedInputStream(is, bufSize); + buffered.mark(bufSize); + try { + ((EntityRequest) request).setEntity(buffered); + } catch (UnsupportedOperationException ignored) { + } + entityStream = buffered; + } + } int retryCount = 0; while (true) { try { @@ -79,11 +96,6 @@ protected Response executeRequest(Client client, ObjectRequest request) { retryCount++; // stash retry count so GeoPinningFilter can fail over on reads request.property(com.emc.object.s3.jersey.RetryFilter.PROP_RETRY_COUNT, retryCount); - java.io.InputStream entityStream = null; - if (request instanceof EntityRequest) { - Object entity = ((EntityRequest) request).getEntity(); - if (entity instanceof java.io.InputStream) entityStream = (java.io.InputStream) entity; - } // shouldRetry throws the original exception if retries are exhausted or not retryable retryFilter.shouldRetry(orig, retryCount, entityStream); } diff --git a/src/main/java/com/emc/object/EntityRequest.java b/src/main/java/com/emc/object/EntityRequest.java index a5e85468..314ae859 100644 --- a/src/main/java/com/emc/object/EntityRequest.java +++ b/src/main/java/com/emc/object/EntityRequest.java @@ -34,4 +34,8 @@ public interface EntityRequest { Long getContentLength(); boolean isChunkable(); + + default void setEntity(Object entity) { + throw new UnsupportedOperationException("setEntity not supported on this request type"); + } } diff --git a/src/main/java/com/emc/object/s3/request/PutObjectRequest.java b/src/main/java/com/emc/object/s3/request/PutObjectRequest.java index 1577bdd2..ba03af8b 100644 --- a/src/main/java/com/emc/object/s3/request/PutObjectRequest.java +++ b/src/main/java/com/emc/object/s3/request/PutObjectRequest.java @@ -120,6 +120,15 @@ public Object getObject() { return object; } + public void setObject(Object object) { + this.object = object; + } + + @Override + public void setEntity(Object entity) { + setObject(entity); + } + public Range getRange() { return range; } diff --git a/src/main/java/com/emc/object/s3/request/UploadPartRequest.java b/src/main/java/com/emc/object/s3/request/UploadPartRequest.java index 2d352a6a..b9e75b77 100644 --- a/src/main/java/com/emc/object/s3/request/UploadPartRequest.java +++ b/src/main/java/com/emc/object/s3/request/UploadPartRequest.java @@ -99,6 +99,11 @@ public void setObject(Object object) { this.object = object; } + @Override + public void setEntity(Object entity) { + setObject(entity); + } + public void setContentLength(Long contentLength) { this.contentLength = contentLength; } diff --git a/src/test/java/com/emc/object/s3/S3EncryptionClientBasicTest.java b/src/test/java/com/emc/object/s3/S3EncryptionClientBasicTest.java index 5d7ef732..ee92a517 100644 --- a/src/test/java/com/emc/object/s3/S3EncryptionClientBasicTest.java +++ b/src/test/java/com/emc/object/s3/S3EncryptionClientBasicTest.java @@ -432,6 +432,11 @@ public void testDeleteBucketWithObjects() { public void testDeleteBucketWithBackgroundTasks() { } + @Ignore + @Override + public void testDeleteBucketInRetentionWithBackgroundTasks() { + } + @Ignore @Override public void testSetGetBucketAcl() { diff --git a/src/test/java/com/emc/object/s3/S3JerseyClientTest.java b/src/test/java/com/emc/object/s3/S3JerseyClientTest.java index 03322ff3..e7e2f295 100644 --- a/src/test/java/com/emc/object/s3/S3JerseyClientTest.java +++ b/src/test/java/com/emc/object/s3/S3JerseyClientTest.java @@ -59,6 +59,7 @@ import java.net.SocketException; import java.net.URI; import java.net.URL; +import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.*; @@ -642,7 +643,7 @@ public void testDeleteBucketInRetentionWithBackgroundTasks() throws Exception { String keyInRetention = "testObjectRetention"; client.enableObjectLock(bucketName); // ensure the retention period is longer than the bucket deletion background tasks. - Date retentionDate = new Date(System.currentTimeMillis() + 3 * 60 * 1000); + Date retentionDate = new Date(System.currentTimeMillis() + 6 * 60 * 1000); ObjectLockRetention objectLockRetention = new ObjectLockRetention() .withMode(ObjectLockRetentionMode.COMPLIANCE) .withRetainUntilDate(retentionDate); @@ -2842,6 +2843,9 @@ public void run() { || e.getMessage().startsWith("Connection reset by peer") || e.getMessage().startsWith("Software caused connection abort"))) continue; + // Java 25+ HttpURLConnection throws IOException instead of SocketException on abort + if (e instanceof java.io.IOException && "Error writing to server".equals(e.getMessage())) + continue; if (!(e instanceof S3Exception)) throw new RuntimeException(e); S3Exception se = (S3Exception) e; if (!"NoSuchUpload".equals(se.getErrorCode()) && !"NoSuchKey".equals(se.getErrorCode())) @@ -2855,10 +2859,13 @@ public void run() { @Test public void testListMarkerWithSpecialChars() { - String marker = "foo/bar/blah%blah&blah"; - ListObjectsResult result = client.listObjects(new ListObjectsRequest(getTestBucket()).withMarker(marker) + String rawMarker = "foo/bar/blah%blah&blah"; + String encodedMarker = URLEncoder.encode(rawMarker, StandardCharsets.UTF_8); + + ListObjectsResult result = client.listObjects(new ListObjectsRequest(getTestBucket()).withMarker(encodedMarker) .withEncodingType(EncodingType.url)); - Assert.assertEquals(marker, result.getMarker()); + + Assert.assertEquals(rawMarker, result.getMarker()); Assert.assertEquals(EncodingType.url, result.getEncodingType()); } From 09e63d0c5874671ba46d92c7d74788e625137698 Mon Sep 17 00:00:00 2001 From: Sushilkumar Pawar Date: Fri, 26 Jun 2026 09:23:01 +0530 Subject: [PATCH 2/4] Changed smart client GA release dependency --- build.gradle | 2 +- settings.gradle | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index a8b02986..36e30d0b 100644 --- a/build.gradle +++ b/build.gradle @@ -62,7 +62,7 @@ repositories { } dependencies { - api 'com.emc.ecs:smart-client-ecs:4.0.0-rc.3' + api 'com.emc.ecs:smart-client-ecs:4.0.0' implementation 'org.glassfish.jersey.core:jersey-client:2.47' implementation 'org.glassfish.jersey.connectors:jersey-apache-connector:2.47' implementation 'org.glassfish.jersey.inject:jersey-hk2:2.47' diff --git a/settings.gradle b/settings.gradle index 9974dcbe..5c40f1f6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,7 +1,2 @@ rootProject.name = 'object-client' -include 'geo-pin-cli' -includeBuild('../smart-client-java') { - dependencySubstitution { - substitute module('com.emc.ecs:smart-client-ecs') using project(':smart-client-ecs') - } -} \ No newline at end of file +include 'geo-pin-cli' \ No newline at end of file From f4652bc4137afd8ffb1a769fa01f68a0a865e035 Mon Sep 17 00:00:00 2001 From: Sushilkumar Pawar Date: Fri, 26 Jun 2026 14:15:55 +0530 Subject: [PATCH 3/4] Fixed key distribution case --- src/test/java/com/emc/object/s3/GeoPinningTest.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/test/java/com/emc/object/s3/GeoPinningTest.java b/src/test/java/com/emc/object/s3/GeoPinningTest.java index 2d037544..68a5d294 100644 --- a/src/test/java/com/emc/object/s3/GeoPinningTest.java +++ b/src/test/java/com/emc/object/s3/GeoPinningTest.java @@ -185,10 +185,7 @@ protected void testKeyDistribution(String key, int vdcIndex) { Assert.assertEquals(10, loadBalancer.getTotalConnections()); for (HostStats stats : loadBalancer.getHostStats()) { - if (vdcs.get(vdcIndex).equals(((VdcHost) stats).getVdc())) { - // all hosts in the appropriate VDC should have been used at least once - Assert.assertTrue(stats.getTotalConnections() > 0); - } else { + if (!vdcs.get(vdcIndex).equals(((VdcHost) stats).getVdc())) { // hosts in other VDCs should *not* be used Assert.assertEquals(0, stats.getTotalConnections()); } @@ -215,10 +212,7 @@ protected void testBucketDistribution(String bucket, int vdcIndex) { Assert.assertEquals(requestCount, loadBalancer.getTotalConnections()); for (HostStats stats : loadBalancer.getHostStats()) { - if (vdcs.get(vdcIndex).equals(((VdcHost) stats).getVdc())) { - // all hosts in the appropriate VDC should have been used at least once - Assert.assertTrue(stats.getTotalConnections() > 0); - } else { + if (!vdcs.get(vdcIndex).equals(((VdcHost) stats).getVdc())) { // hosts in other VDCs should *not* be used Assert.assertEquals(0, stats.getTotalConnections()); } From 94b86ef5704a7701826ca2a33bacd3ea823d56f0 Mon Sep 17 00:00:00 2001 From: Sushilkumar Pawar Date: Fri, 26 Jun 2026 16:32:59 +0530 Subject: [PATCH 4/4] Fixed key distribution case --- src/test/java/com/emc/object/s3/GeoPinningTest.java | 11 +++++++++-- src/test/java/com/emc/object/s3/GeoPinningV4Test.java | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/emc/object/s3/GeoPinningTest.java b/src/test/java/com/emc/object/s3/GeoPinningTest.java index 68a5d294..34d8d539 100644 --- a/src/test/java/com/emc/object/s3/GeoPinningTest.java +++ b/src/test/java/com/emc/object/s3/GeoPinningTest.java @@ -72,6 +72,7 @@ protected S3Config createS3Config() throws Exception { if (proxyUri != null) s3Config.setProperty(ObjectConfig.PROPERTY_PROXY_URI, proxyUri); s3Config.setGeoPinningEnabled(true); + s3Config.setProperty(ObjectConfig.PROPERTY_DISABLE_HOST_UPDATE, "true"); return s3Config; } @@ -185,7 +186,10 @@ protected void testKeyDistribution(String key, int vdcIndex) { Assert.assertEquals(10, loadBalancer.getTotalConnections()); for (HostStats stats : loadBalancer.getHostStats()) { - if (!vdcs.get(vdcIndex).equals(((VdcHost) stats).getVdc())) { + if (vdcs.get(vdcIndex).equals(((VdcHost) stats).getVdc())) { + // all hosts in the appropriate VDC should have been used at least once + Assert.assertTrue(stats.getTotalConnections() > 0); + } else { // hosts in other VDCs should *not* be used Assert.assertEquals(0, stats.getTotalConnections()); } @@ -212,7 +216,10 @@ protected void testBucketDistribution(String bucket, int vdcIndex) { Assert.assertEquals(requestCount, loadBalancer.getTotalConnections()); for (HostStats stats : loadBalancer.getHostStats()) { - if (!vdcs.get(vdcIndex).equals(((VdcHost) stats).getVdc())) { + if (vdcs.get(vdcIndex).equals(((VdcHost) stats).getVdc())) { + // all hosts in the appropriate VDC should have been used at least once + Assert.assertTrue(stats.getTotalConnections() > 0); + } else { // hosts in other VDCs should *not* be used Assert.assertEquals(0, stats.getTotalConnections()); } diff --git a/src/test/java/com/emc/object/s3/GeoPinningV4Test.java b/src/test/java/com/emc/object/s3/GeoPinningV4Test.java index 2d1fa3d1..d8870d7e 100644 --- a/src/test/java/com/emc/object/s3/GeoPinningV4Test.java +++ b/src/test/java/com/emc/object/s3/GeoPinningV4Test.java @@ -30,6 +30,7 @@ protected S3Config createS3Config() throws Exception { if (proxyUri != null) s3Config.setProperty(ObjectConfig.PROPERTY_PROXY_URI, proxyUri); s3Config.setGeoPinningEnabled(true); + s3Config.setProperty(ObjectConfig.PROPERTY_DISABLE_HOST_UPDATE, "true"); return s3Config; }