This is an automated email from the ASF dual-hosted git repository.

chungen0126 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new c8fa8dafd5c HDDS-16101. Extract bucket lifecycle operations into 
dedicated handler (#10969)
c8fa8dafd5c is described below

commit c8fa8dafd5cd42e729ef7b9ae1f581a6765a336c
Author: YANG-SYUAN CHOU <[email protected]>
AuthorDate: Mon Aug 10 14:15:40 2026 +0800

    HDDS-16101. Extract bucket lifecycle operations into dedicated handler 
(#10969)
---
 .../ozone/s3/endpoint/BucketCrudHandler.java       | 126 +--------------------
 .../hadoop/ozone/s3/endpoint/BucketEndpoint.java   |   1 +
 ...rudHandler.java => BucketLifecycleHandler.java} |  97 +++-------------
 3 files changed, 21 insertions(+), 203 deletions(-)

diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketCrudHandler.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketCrudHandler.java
index 6b716993ef0..3d4cc4d6ad5 100644
--- 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketCrudHandler.java
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketCrudHandler.java
@@ -17,25 +17,15 @@
 
 package org.apache.hadoop.ozone.s3.endpoint;
 
-import static 
org.apache.hadoop.ozone.s3.util.S3Consts.EXPECTED_BUCKET_OWNER_HEADER;
-
 import java.io.IOException;
 import java.io.InputStream;
-import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.hadoop.ozone.audit.S3GAction;
 import org.apache.hadoop.ozone.client.OzoneBucket;
-import org.apache.hadoop.ozone.client.OzoneLifecycleConfiguration;
-import org.apache.hadoop.ozone.om.exceptions.OMException;
-import org.apache.hadoop.ozone.om.helpers.OmLifecycleConfiguration;
 import org.apache.hadoop.ozone.s3.exception.OS3Exception;
-import org.apache.hadoop.ozone.s3.exception.S3ErrorTable;
 import org.apache.hadoop.ozone.s3.util.S3Consts.QueryParams;
 import org.apache.http.HttpStatus;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Handler for default bucket CRUD operations.
@@ -50,7 +40,6 @@
  * (configuration, headers, request context, audit logging, metrics, etc.).
  */
 public class BucketCrudHandler extends BucketOperationHandler {
-  private static final Logger LOG = 
LoggerFactory.getLogger(BucketCrudHandler.class);
 
   /**
    * Handle only plain PUT bucket (create bucket), not subresources.
@@ -59,22 +48,8 @@ private boolean shouldHandle() {
     return queryParams().get(QueryParams.ACL) == null
         && queryParams().get(QueryParams.UPLOADS) == null
         && queryParams().get(QueryParams.DELETE) == null
-        && queryParams().get(QueryParams.TAGGING) == null;
-  }
-
-  /**
-   * Handle GET /{bucket} for bucket LIFECYCLE configuration.
-   */
-  @Override
-  Response handleGetRequest(S3RequestContext context, String bucketName)
-      throws IOException, OS3Exception {
-    final String lifecycleMarker = queryParams().get(QueryParams.LIFECYCLE);
-
-    if (lifecycleMarker != null) {
-      context.setAction(S3GAction.GET_BUCKET_LIFECYCLE);
-      return getBucketLifecycleConfiguration(context, bucketName);
-    }
-    return null;
+        && queryParams().get(QueryParams.TAGGING) == null
+        && queryParams().get(QueryParams.LIFECYCLE) == null;
   }
 
   /**
@@ -84,12 +59,6 @@ Response handleGetRequest(S3RequestContext context, String 
bucketName)
   Response handlePutRequest(S3RequestContext context, String bucketName, 
InputStream body)
       throws IOException, OS3Exception {
 
-    final String lifecycleMarker = queryParams().get(QueryParams.LIFECYCLE);
-    if (lifecycleMarker != null) {
-      context.setAction(S3GAction.PUT_BUCKET_LIFECYCLE);
-      return putBucketLifecycleConfiguration(context, bucketName, body);
-    }
-
     if (!shouldHandle()) {
       return null;
     }
@@ -115,12 +84,6 @@ Response handlePutRequest(S3RequestContext context, String 
bucketName, InputStre
   Response handleDeleteRequest(S3RequestContext context, String bucketName)
       throws IOException, OS3Exception {
 
-    final String lifecycleMarker = queryParams().get(QueryParams.LIFECYCLE);
-    if (lifecycleMarker != null) {
-      context.setAction(S3GAction.DELETE_BUCKET_LIFECYCLE);
-      return deleteBucketLifecycleConfiguration(context, bucketName);
-    }
-
     if (!shouldHandle()) {
       return null;
     }
@@ -143,89 +106,4 @@ Response handleDeleteRequest(S3RequestContext context, 
String bucketName)
         .status(HttpStatus.SC_NO_CONTENT)
         .build();
   }
-
-  public Response deleteBucketLifecycleConfiguration(S3RequestContext context, 
String bucketName)
-      throws IOException, OS3Exception {
-    verifyBucketOwner(context, bucketName);
-    deleteLifecycleConfiguration(context, bucketName);
-    return Response.noContent().build();
-  }
-
-  protected void deleteLifecycleConfiguration(S3RequestContext context, String 
bucketName)
-      throws IOException, OS3Exception {
-    try {
-      context.getVolume().getBucket(bucketName).deleteLifecycleConfiguration();
-    } catch (OMException ex) {
-      // DeleteBucketLifecycle is idempotent: deleting a missing config
-      // must still return 204, not 404 — same as normal key deletion.
-      if (ex.getResult() != 
OMException.ResultCodes.LIFECYCLE_CONFIGURATION_NOT_FOUND) {
-        throw S3ErrorTable.newError(bucketName, ex);
-      }
-    }
-  }
-
-  private void verifyBucketOwner(S3RequestContext context, String bucketName) 
throws OS3Exception {
-    HttpHeaders httpHeaders = getHeaders();
-    if (httpHeaders == null) {
-      return;
-    }
-    String expectedBucketOwner = 
httpHeaders.getHeaderString(EXPECTED_BUCKET_OWNER_HEADER);
-    if (expectedBucketOwner == null || expectedBucketOwner.isEmpty()) {
-      return;
-    }
-
-    try {
-      String actualOwner = 
context.getVolume().getBucket(bucketName).getOwner();
-      if (actualOwner != null && !actualOwner.equals(expectedBucketOwner)) {
-        LOG.debug("Bucket: {}, ExpectedBucketOwner: {}, ActualBucketOwner: {}",
-            bucketName, expectedBucketOwner, actualOwner);
-        throw S3ErrorTable.newError(S3ErrorTable.ACCESS_DENIED, bucketName);
-      }
-    } catch (Exception ex) {
-      LOG.error("Owner verification failed for bucket: {}", bucketName, ex);
-      throw S3ErrorTable.newError(S3ErrorTable.ACCESS_DENIED, bucketName);
-    }
-  }
-
-  public Response putBucketLifecycleConfiguration(S3RequestContext context, 
String bucketName, InputStream body)
-      throws IOException, OS3Exception {
-    verifyBucketOwner(context, bucketName);
-    S3LifecycleConfiguration s3LifecycleConfiguration;
-    OzoneBucket ozoneBucket = context.getVolume().getBucket(bucketName);
-    try {
-      s3LifecycleConfiguration = new 
PutBucketLifecycleConfigurationUnmarshaller().readFrom(null,
-          null, null, null, null, body);
-      OmLifecycleConfiguration lcc =
-          s3LifecycleConfiguration.toOmLifecycleConfiguration(ozoneBucket);
-      ozoneBucket.setLifecycleConfiguration(lcc);
-    } catch (WebApplicationException ex) {
-      throw S3ErrorTable.newError(S3ErrorTable.MALFORMED_XML, bucketName);
-    } catch (OMException ex) {
-      throw S3ErrorTable.newError(bucketName, ex);
-    }
-    return Response.ok().build();
-  }
-
-  public Response getBucketLifecycleConfiguration(S3RequestContext context, 
String bucketName)
-      throws IOException, OS3Exception {
-    verifyBucketOwner(context, bucketName);
-    OzoneLifecycleConfiguration ozoneLifecycleConfiguration =
-        getLifecycleConfiguration(context, bucketName);
-    return 
Response.ok(S3LifecycleConfiguration.fromOzoneLifecycleConfiguration(
-        ozoneLifecycleConfiguration), MediaType.APPLICATION_XML_TYPE).build();
-  }
-
-  protected OzoneLifecycleConfiguration getLifecycleConfiguration(
-      S3RequestContext context, String bucketName) throws IOException, 
OS3Exception {
-    try {
-      OzoneBucket ozoneBucket = context.getVolume().getBucket(bucketName);
-      return ozoneBucket.getLifecycleConfiguration();
-    } catch (OMException ex) {
-      if (ex.getResult() == 
OMException.ResultCodes.LIFECYCLE_CONFIGURATION_NOT_FOUND) {
-        throw S3ErrorTable.newError(
-            S3ErrorTable.NO_SUCH_LIFECYCLE_CONFIGURATION, bucketName);
-      }
-      throw ex;
-    }
-  }
 }
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java
index 7840f195d0f..aac2c392064 100644
--- 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java
@@ -435,6 +435,7 @@ protected void init() {
         .add(new BucketAclHandler())
         .add(new ListMultipartUploadsHandler())
         .add(new BucketTaggingHandler())
+        .add(new BucketLifecycleHandler())
         .add(new BucketCrudHandler())
         .add(this)
         .build();
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketCrudHandler.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketLifecycleHandler.java
similarity index 65%
copy from 
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketCrudHandler.java
copy to 
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketLifecycleHandler.java
index 6b716993ef0..61191196c0d 100644
--- 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketCrudHandler.java
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketLifecycleHandler.java
@@ -33,115 +33,54 @@
 import org.apache.hadoop.ozone.s3.exception.OS3Exception;
 import org.apache.hadoop.ozone.s3.exception.S3ErrorTable;
 import org.apache.hadoop.ozone.s3.util.S3Consts.QueryParams;
-import org.apache.http.HttpStatus;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Handler for default bucket CRUD operations.
- * Implements PUT (create bucket) and DELETE operations when no
- * subresource query parameters are present.
- *
- * This handler processes bucket-level requests that do not target
- * specific subresources (such as {@code ?acl}, {@code ?uploads},
- * {@code ?delete} or {@code ?tagging}), which are handled by dedicated 
handlers.
- *
- * This handler extends EndpointBase to inherit all required functionality
- * (configuration, headers, request context, audit logging, metrics, etc.).
+ * Handler for S3 bucket lifecycle configuration operations.
  */
-public class BucketCrudHandler extends BucketOperationHandler {
-  private static final Logger LOG = 
LoggerFactory.getLogger(BucketCrudHandler.class);
 
-  /**
-   * Handle only plain PUT bucket (create bucket), not subresources.
-   */
+public class BucketLifecycleHandler extends BucketOperationHandler {
+
+  private static final Logger LOG =
+      LoggerFactory.getLogger(BucketLifecycleHandler.class);
+
   private boolean shouldHandle() {
-    return queryParams().get(QueryParams.ACL) == null
-        && queryParams().get(QueryParams.UPLOADS) == null
-        && queryParams().get(QueryParams.DELETE) == null
-        && queryParams().get(QueryParams.TAGGING) == null;
+    return queryParams().get(QueryParams.LIFECYCLE) != null;
   }
 
-  /**
-   * Handle GET /{bucket} for bucket LIFECYCLE configuration.
-   */
   @Override
   Response handleGetRequest(S3RequestContext context, String bucketName)
       throws IOException, OS3Exception {
-    final String lifecycleMarker = queryParams().get(QueryParams.LIFECYCLE);
-
-    if (lifecycleMarker != null) {
-      context.setAction(S3GAction.GET_BUCKET_LIFECYCLE);
-      return getBucketLifecycleConfiguration(context, bucketName);
+    if (!shouldHandle()) {
+      return null;
     }
-    return null;
+
+    context.setAction(S3GAction.GET_BUCKET_LIFECYCLE);
+    return getBucketLifecycleConfiguration(context, bucketName);
   }
 
-  /**
-   * Handle PUT /{bucket} for bucket creation.
-   */
   @Override
-  Response handlePutRequest(S3RequestContext context, String bucketName, 
InputStream body)
+  Response handlePutRequest(
+      S3RequestContext context, String bucketName, InputStream body)
       throws IOException, OS3Exception {
-
-    final String lifecycleMarker = queryParams().get(QueryParams.LIFECYCLE);
-    if (lifecycleMarker != null) {
-      context.setAction(S3GAction.PUT_BUCKET_LIFECYCLE);
-      return putBucketLifecycleConfiguration(context, bucketName, body);
-    }
-
     if (!shouldHandle()) {
       return null;
     }
 
-    context.setAction(S3GAction.CREATE_BUCKET);
-
-    try {
-      getClient().getObjectStore().createS3Bucket(bucketName);
-      getMetrics().updateCreateBucketSuccessStats(context.getStartNanos());
-      return Response.status(HttpStatus.SC_OK)
-          .header(HttpHeaders.LOCATION, "/" + bucketName)
-          .build();
-    } catch (Exception e) {
-      getMetrics().updateCreateBucketFailureStats(context.getStartNanos());
-      throw e;
-    }
+    context.setAction(S3GAction.PUT_BUCKET_LIFECYCLE);
+    return putBucketLifecycleConfiguration(context, bucketName, body);
   }
 
-  /**
-   * Handle DELETE /{bucket} for bucket deletion.
-   */
   @Override
   Response handleDeleteRequest(S3RequestContext context, String bucketName)
       throws IOException, OS3Exception {
-
-    final String lifecycleMarker = queryParams().get(QueryParams.LIFECYCLE);
-    if (lifecycleMarker != null) {
-      context.setAction(S3GAction.DELETE_BUCKET_LIFECYCLE);
-      return deleteBucketLifecycleConfiguration(context, bucketName);
-    }
-
     if (!shouldHandle()) {
       return null;
     }
 
-    context.setAction(S3GAction.DELETE_BUCKET);
-
-    try {
-      if (S3Owner.hasBucketOwnershipVerificationConditions(getHeaders())) {
-        OzoneBucket bucket = context.getVolume().getBucket(bucketName);
-        S3Owner.verifyBucketOwnerCondition(getHeaders(), bucketName, 
bucket.getOwner());
-      }
-      context.getVolume().deleteBucket(bucketName);
-    } catch (Exception ex) {
-      getMetrics().updateDeleteBucketFailureStats(context.getStartNanos());
-      throw ex;
-    }
-
-    getMetrics().updateDeleteBucketSuccessStats(context.getStartNanos());
-    return Response
-        .status(HttpStatus.SC_NO_CONTENT)
-        .build();
+    context.setAction(S3GAction.DELETE_BUCKET_LIFECYCLE);
+    return deleteBucketLifecycleConfiguration(context, bucketName);
   }
 
   public Response deleteBucketLifecycleConfiguration(S3RequestContext context, 
String bucketName)


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to