From b7a64a31c6ed1112c8ff3baddb4a33e2af0efced Mon Sep 17 00:00:00 2001 From: Luo Date: Fri, 25 Nov 2022 22:21:57 +0800 Subject: [PATCH] Support multiple LocalS3 instance in the same JVM (#18) --- local-s3-interationtest/build.gradle | 1 + .../MultiLocalS3InstanceIntegrationTest.java | 62 ++ .../java/com/robothy/s3/jupiter/LocalS3.java | 19 +- .../java/com/robothy/s3/rest/LocalS3.java | 26 +- .../bootstrap/FileSystemLocalS3Bootstrap.java | 2 +- .../rest/handler/BucketPolicyController.java | 6 +- .../CompleteMultipartUploadController.java | 10 +- .../s3/rest/handler/CopyObjectController.java | 9 +- .../rest/handler/CreateBucketController.java | 9 +- .../CreateMultipartUploadController.java | 9 +- .../rest/handler/DeleteBucketController.java | 6 +- .../DeleteBucketTaggingController.java | 6 +- .../rest/handler/DeleteObjectController.java | 6 +- .../s3/rest/handler/ExceptionHandler.java | 1 - .../rest/handler/GetBucketAclController.java | 9 +- .../s3/rest/handler/GetBucketController.java | 9 +- .../handler/GetBucketTaggingController.java | 10 +- .../GetBucketVersioningController.java | 9 +- .../s3/rest/handler/GetObjectController.java | 6 +- .../s3/rest/handler/HeadBucketController.java | 6 +- .../s3/rest/handler/HeadObjectController.java | 7 +- .../handler/ListObjectVersionsController.java | 9 +- .../rest/handler/ListObjectsController.java | 9 +- .../rest/handler/LocalS3ExceptionHandler.java | 6 +- .../s3/rest/handler/LocalS3RouterFactory.java | 581 +++++++++--------- .../rest/handler/PutBucketAclController.java | 9 +- .../handler/PutBucketTaggingController.java | 9 +- .../PutBucketVersioningController.java | 9 +- .../s3/rest/handler/PutObjectController.java | 10 +- .../s3/rest/handler/UploadPartController.java | 13 +- .../rest/service/DefaultServiceFactory.java | 29 + .../s3/rest/service/ServiceFactory.java | 22 +- .../handler/CopyObjectControllerTest.java | 5 +- 33 files changed, 555 insertions(+), 384 deletions(-) create mode 100644 local-s3-interationtest/src/test/java/com/robothy/s3/test/MultiLocalS3InstanceIntegrationTest.java create mode 100644 local-s3-rest/src/main/java/com/robothy/s3/rest/service/DefaultServiceFactory.java diff --git a/local-s3-interationtest/build.gradle b/local-s3-interationtest/build.gradle index 19665f4..e9a9821 100644 --- a/local-s3-interationtest/build.gradle +++ b/local-s3-interationtest/build.gradle @@ -13,6 +13,7 @@ dependencies { testImplementation (project(":local-s3-datatypes")) testImplementation(project(":local-s3-core")) + testImplementation(project(":local-s3-rest")) testRuntimeOnly "ch.qos.logback:logback-core:${libVersion['ch.qos.logback.logback-core']}" } diff --git a/local-s3-interationtest/src/test/java/com/robothy/s3/test/MultiLocalS3InstanceIntegrationTest.java b/local-s3-interationtest/src/test/java/com/robothy/s3/test/MultiLocalS3InstanceIntegrationTest.java new file mode 100644 index 0000000..90fea6c --- /dev/null +++ b/local-s3-interationtest/src/test/java/com/robothy/s3/test/MultiLocalS3InstanceIntegrationTest.java @@ -0,0 +1,62 @@ +package com.robothy.s3.test; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import com.amazonaws.client.builder.AwsClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.s3.model.HeadBucketRequest; +import com.amazonaws.services.s3.transfer.TransferManagerBuilder; +import com.robothy.s3.rest.LocalS3; +import com.robothy.s3.rest.bootstrap.LocalS3Mode; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class MultiLocalS3InstanceIntegrationTest { + + @Test + @DisplayName("Multiple LocalS3 instance in the same JVM") + void test() throws IOException { + Path dataPath = Files.createTempDirectory("local-s3"); + + LocalS3 persistenceInstance = LocalS3.builder() + .mode(LocalS3Mode.PERSISTENCE) + .dataPath(dataPath.toAbsolutePath().toString()) + .port(-1) + .build(); + + persistenceInstance.start(); + LocalS3 inMemoryInstance = LocalS3.builder() + .mode(LocalS3Mode.IN_MEMORY) + .port(-1) + .build(); + inMemoryInstance.start(); + + AmazonS3 persistenceS3 = createClient(persistenceInstance.getPort()); + AmazonS3 inMemoS3 = createClient(inMemoryInstance.getPort()); + assertDoesNotThrow(() -> persistenceS3.createBucket("my-bucket")); + assertDoesNotThrow(() -> inMemoS3.createBucket("my-bucket")); + + persistenceInstance.shutdown(); + inMemoryInstance.shutdown(); + + LocalS3 inMemoWithInitial = LocalS3.builder().mode(LocalS3Mode.IN_MEMORY) + .dataPath(dataPath.toAbsolutePath().toString()) + .port(-1) + .build(); + inMemoWithInitial.start(); + AmazonS3 client = createClient(inMemoWithInitial.getPort()); + assertDoesNotThrow(() -> client.headBucket(new HeadBucketRequest("my-bucket"))); + + } + + AmazonS3 createClient(int port) { + return AmazonS3ClientBuilder.standard().withEndpointConfiguration( + new AwsClientBuilder.EndpointConfiguration("http://localhost:" + port, "local")) + .enablePathStyleAccess() + .build(); + } + +} diff --git a/local-s3-jupiter/src/main/java/com/robothy/s3/jupiter/LocalS3.java b/local-s3-jupiter/src/main/java/com/robothy/s3/jupiter/LocalS3.java index 36776d4..e45a9b9 100644 --- a/local-s3-jupiter/src/main/java/com/robothy/s3/jupiter/LocalS3.java +++ b/local-s3-jupiter/src/main/java/com/robothy/s3/jupiter/LocalS3.java @@ -19,22 +19,22 @@ * *

Below example injects an {@code AmazonS3} instance to the parameter: * - *

- *  @LocalS3
+ * 
{@code
+ *  @LocalS3
  *  class AppTest {
- *    @Test
+ *    @Test
  *    void test(AmazonS3 s3) {
  *      s3.createBucket("my-bucket");
  *    }
  *  }
- * 
+ * }
* * Or resolve a {@linkplain LocalS3Endpoint}. * - *
+ * 
{@code
  *  class AppTest {
- *    @Test
- *    @LocalS3
+ *    @Test
+ *    @LocalS3
  *    void test1(LocalS3Endpoint endpoint) {
  *      AmazonS3 client = AmazonS3ClientBuilder.standard()
  *        .enablePathStyleAccess()
@@ -42,9 +42,8 @@
  *        .build();
  *      assertDoesNotThrow(() -> client.createBucket("my-bucket"));
  *    }
- *
  *  }
- * 
+ * }
* *

If {@code @LocalS3} is on a test class, the Junit5 extension will create a shared * service for all test methods in the class and shut it down in the "after all" callback. @@ -52,7 +51,7 @@ * for the method and shut down the service in the "after each" callback. * */ -@Target({ElementType.TYPE, ElementType.METHOD}) +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @ExtendWith(LocalS3Extension.class) @ExtendWith(AmazonS3Resolver.class) diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/LocalS3.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/LocalS3.java index 6c88751..4c0cb81 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/LocalS3.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/LocalS3.java @@ -11,6 +11,7 @@ import com.robothy.s3.core.service.manager.LocalS3Manager; import com.robothy.s3.rest.bootstrap.LocalS3Mode; import com.robothy.s3.rest.handler.LocalS3RouterFactory; +import com.robothy.s3.rest.service.DefaultServiceFactory; import com.robothy.s3.rest.service.ServiceFactory; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; @@ -80,17 +81,16 @@ public static Builder builder() { */ @SneakyThrows public void start() { - registerServices(); + ServiceFactory serviceFactory = createServiceFactory(); this.parentGroup = new NioEventLoopGroup(nettyParentEventGroupThreadNum); this.childGroup = new NioEventLoopGroup(nettyChildEventGroupThreadNum); this.executorGroup = new DefaultEventLoopGroup(s3ExecutorThreadNum); - ServerBootstrap serverBootstrap = new ServerBootstrap(); ChannelFuture channelFuture = serverBootstrap.group(parentGroup, childGroup) .handler(new LoggingHandler(LogLevel.DEBUG)) .channel(NioServerSocketChannel.class) - .childHandler(new HttpServerInitializer(executorGroup, LocalS3RouterFactory.create())) + .childHandler(new HttpServerInitializer(executorGroup, LocalS3RouterFactory.create(serviceFactory))) .bind(port) .sync(); log.info("LocalS3 started."); @@ -98,29 +98,29 @@ public void start() { Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown)); } - private void registerServices() { + private ServiceFactory createServiceFactory() { + + LocalS3Manager manager; if (mode == LocalS3Mode.IN_MEMORY) { log.info("Created in-memory LocalS3 manager."); - LocalS3Manager.createInMemoryS3Manager(dataPath, initialDataCacheEnabled); + manager = LocalS3Manager.createInMemoryS3Manager(dataPath, initialDataCacheEnabled); } else { log.info("Created file system LocalS3 manager."); - LocalS3Manager.createFileSystemS3Manager(dataPath); + manager = LocalS3Manager.createFileSystemS3Manager(dataPath); } - LocalS3Manager manager = mode == LocalS3Mode.IN_MEMORY ? - LocalS3Manager.createInMemoryS3Manager(dataPath, initialDataCacheEnabled) : - LocalS3Manager.createFileSystemS3Manager(dataPath); - + ServiceFactory serviceFactory = new DefaultServiceFactory(); BucketService bucketService = manager.bucketService(); ObjectService objectService = manager.objectService(); - ServiceFactory.register(BucketService.class, () -> bucketService); - ServiceFactory.register(ObjectService.class, () -> objectService); + serviceFactory.register(BucketService.class, () -> bucketService); + serviceFactory.register(ObjectService.class, () -> objectService); XMLInputFactory input = new WstxInputFactory(); input.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE); XmlMapper xmlMapper = new XmlMapper(new XmlFactory(input, new WstxOutputFactory())); xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - ServiceFactory.register(XmlMapper.class, () -> xmlMapper); + serviceFactory.register(XmlMapper.class, () -> xmlMapper); + return serviceFactory; } /** diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/bootstrap/FileSystemLocalS3Bootstrap.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/bootstrap/FileSystemLocalS3Bootstrap.java index c415474..315f711 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/bootstrap/FileSystemLocalS3Bootstrap.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/bootstrap/FileSystemLocalS3Bootstrap.java @@ -47,7 +47,7 @@ public void start() { ChannelFuture channelFuture = serverBootstrap.group(parentGroup, childGroup) .handler(new LoggingHandler(LogLevel.DEBUG)) .channel(NioServerSocketChannel.class) - .childHandler(new HttpServerInitializer(executorGroup, LocalS3RouterFactory.create())) + .childHandler(new HttpServerInitializer(executorGroup, LocalS3RouterFactory.create(null))) .bind(options.getPort()) .sync(); this.serverSocketChannel = channelFuture.channel(); diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/BucketPolicyController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/BucketPolicyController.java index 0095d6c..f247f0a 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/BucketPolicyController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/BucketPolicyController.java @@ -15,7 +15,11 @@ class BucketPolicyController { - private final BucketPolicyService bucketPolicyService = ServiceFactory.getInstance(BucketService.class); + private final BucketPolicyService bucketPolicyService; + + BucketPolicyController(ServiceFactory serviceFactory) { + this.bucketPolicyService = serviceFactory.getInstance(BucketService.class); + } /** * Handle GetBucketPolicy diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/CompleteMultipartUploadController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/CompleteMultipartUploadController.java index 74d75a1..225d47c 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/CompleteMultipartUploadController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/CompleteMultipartUploadController.java @@ -8,7 +8,6 @@ import com.robothy.s3.core.model.request.CompleteMultipartUploadPartOption; import com.robothy.s3.core.service.CompleteMultipartUploadService; import com.robothy.s3.core.service.ObjectService; -import com.robothy.s3.core.util.JsonUtils; import com.robothy.s3.rest.assertions.RequestAssertions; import com.robothy.s3.rest.constants.AmzHeaderNames; import com.robothy.s3.rest.model.request.CompleteMultipartUpload; @@ -26,9 +25,14 @@ */ class CompleteMultipartUploadController implements HttpRequestHandler { - private final CompleteMultipartUploadService uploadService = ServiceFactory.getInstance(ObjectService.class); + private final CompleteMultipartUploadService uploadService; - private final XmlMapper xmlMapper = ServiceFactory.getInstance(XmlMapper.class); + private final XmlMapper xmlMapper; + + CompleteMultipartUploadController(ServiceFactory serviceFactory) { + this.uploadService = serviceFactory.getInstance(ObjectService.class); + this.xmlMapper = serviceFactory.getInstance(XmlMapper.class); + } @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/CopyObjectController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/CopyObjectController.java index c74b0fa..86e7a81 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/CopyObjectController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/CopyObjectController.java @@ -25,9 +25,14 @@ */ class CopyObjectController implements HttpRequestHandler { - private final CopyObjectService objectService = ServiceFactory.getInstance(ObjectService.class); + private final CopyObjectService objectService; - private final XmlMapper xmlMapper = ServiceFactory.getInstance(XmlMapper.class); + private final XmlMapper xmlMapper; + + CopyObjectController(ServiceFactory serviceFactory) { + this.objectService = serviceFactory.getInstance(ObjectService.class); + this.xmlMapper = serviceFactory.getInstance(XmlMapper.class); + } @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/CreateBucketController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/CreateBucketController.java index 92571c7..57af099 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/CreateBucketController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/CreateBucketController.java @@ -23,9 +23,14 @@ @Slf4j class CreateBucketController implements HttpRequestHandler { - BucketService bucketService = ServiceFactory.getInstance(BucketService.class); + BucketService bucketService; - XmlMapper xmlMapper = ServiceFactory.getInstance(XmlMapper.class); + XmlMapper xmlMapper; + + CreateBucketController(ServiceFactory serviceFactory) { + this.bucketService = serviceFactory.getInstance(BucketService.class); + this.xmlMapper = serviceFactory.getInstance(XmlMapper.class); + } @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/CreateMultipartUploadController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/CreateMultipartUploadController.java index 33dfb5f..1d0bfd2 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/CreateMultipartUploadController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/CreateMultipartUploadController.java @@ -18,9 +18,14 @@ */ class CreateMultipartUploadController implements HttpRequestHandler { - private final CreateMultipartUploadService uploadService = ServiceFactory.getInstance(ObjectService.class); + private final CreateMultipartUploadService uploadService; - private final XmlMapper xmlMapper = ServiceFactory.getInstance(XmlMapper.class); + private final XmlMapper xmlMapper; + + CreateMultipartUploadController(ServiceFactory serviceFactory) { + this.uploadService = serviceFactory.getInstance(ObjectService.class); + this.xmlMapper = serviceFactory.getInstance(XmlMapper.class); + } @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/DeleteBucketController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/DeleteBucketController.java index e5cf03b..957bd36 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/DeleteBucketController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/DeleteBucketController.java @@ -14,7 +14,11 @@ */ class DeleteBucketController implements HttpRequestHandler { - private final BucketService bucketService = ServiceFactory.getInstance(BucketService.class); + private final BucketService bucketService; + + DeleteBucketController(ServiceFactory serviceFactory) { + this.bucketService = serviceFactory.getInstance(BucketService.class); + } @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/DeleteBucketTaggingController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/DeleteBucketTaggingController.java index 4f6263f..f53fb65 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/DeleteBucketTaggingController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/DeleteBucketTaggingController.java @@ -13,7 +13,11 @@ */ class DeleteBucketTaggingController implements HttpRequestHandler { - private final BucketService bucketService = ServiceFactory.getInstance(BucketService.class); + private final BucketService bucketService; + + DeleteBucketTaggingController(ServiceFactory serviceFactory) { + this.bucketService = serviceFactory.getInstance(BucketService.class); + } @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/DeleteObjectController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/DeleteObjectController.java index 4e82827..04eb719 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/DeleteObjectController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/DeleteObjectController.java @@ -17,7 +17,11 @@ */ class DeleteObjectController implements HttpRequestHandler { - private final DeleteObjectService deleteObjectService = ServiceFactory.getInstance(ObjectService.class); + private final DeleteObjectService deleteObjectService; + + DeleteObjectController(ServiceFactory serviceFactory) { + this.deleteObjectService = serviceFactory.getInstance(ObjectService.class); + } @Override public void handle(HttpRequest httpRequest, HttpResponse httpResponse) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/ExceptionHandler.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/ExceptionHandler.java index 6262a85..5c28358 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/ExceptionHandler.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/ExceptionHandler.java @@ -8,7 +8,6 @@ import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.handler.codec.http.HttpResponseStatus; -import java.util.Random; /** * Handle any unhandled exceptions and construct an error response body. diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetBucketAclController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetBucketAclController.java index b425f63..e16d628 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetBucketAclController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetBucketAclController.java @@ -18,9 +18,14 @@ */ class GetBucketAclController implements HttpRequestHandler { - private final BucketAclService aclService = ServiceFactory.getInstance(BucketService.class); + private final BucketAclService aclService; - private final XmlMapper xmlMapper = ServiceFactory.getInstance(XmlMapper.class); + private final XmlMapper xmlMapper; + + GetBucketAclController(ServiceFactory serviceFactory) { + this.aclService = serviceFactory.getInstance(BucketService.class); + this.xmlMapper = serviceFactory.getInstance(XmlMapper.class); + } @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetBucketController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetBucketController.java index 06858d1..8eea769 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetBucketController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetBucketController.java @@ -17,9 +17,14 @@ */ class GetBucketController implements HttpRequestHandler { - private final BucketService bucketService = ServiceFactory.getInstance(BucketService.class); + private final BucketService bucketService; - private final XmlMapper xmlMapper = ServiceFactory.getInstance(XmlMapper.class); + private final XmlMapper xmlMapper; + + GetBucketController(ServiceFactory serviceFactory) { + this.bucketService = serviceFactory.getInstance(BucketService.class); + this.xmlMapper = serviceFactory.getInstance(XmlMapper.class); + } @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetBucketTaggingController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetBucketTaggingController.java index 019eb7e..74e4b6c 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetBucketTaggingController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetBucketTaggingController.java @@ -11,7 +11,6 @@ import com.robothy.s3.rest.utils.ResponseUtils; import io.netty.handler.codec.http.HttpResponseStatus; import java.util.Collection; -import java.util.Collections; import java.util.Map; /** @@ -19,9 +18,14 @@ */ class GetBucketTaggingController implements HttpRequestHandler { - private final BucketService bucketService = ServiceFactory.getInstance(BucketService.class); + private final BucketService bucketService; - private final XmlMapper xmlMapper = ServiceFactory.getInstance(XmlMapper.class); + private final XmlMapper xmlMapper; + + GetBucketTaggingController(ServiceFactory serviceFactory) { + this.bucketService = serviceFactory.getInstance(BucketService.class); + this.xmlMapper = serviceFactory.getInstance(XmlMapper.class); + } @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetBucketVersioningController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetBucketVersioningController.java index 10a0fa7..a75fe2f 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetBucketVersioningController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetBucketVersioningController.java @@ -17,9 +17,14 @@ */ class GetBucketVersioningController implements HttpRequestHandler { - private final BucketService bucketService = ServiceFactory.getInstance(BucketService.class); + private final BucketService bucketService; - private final XmlMapper xmlMapper = ServiceFactory.getInstance(XmlMapper.class); + private final XmlMapper xmlMapper; + + GetBucketVersioningController(ServiceFactory serviceFactory) { + this.bucketService = serviceFactory.getInstance(BucketService.class); + this.xmlMapper = serviceFactory.getInstance(XmlMapper.class); + } @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetObjectController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetObjectController.java index cc20831..574dc0b 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetObjectController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/GetObjectController.java @@ -21,7 +21,11 @@ */ class GetObjectController implements HttpRequestHandler { - private final ObjectService objectService = ServiceFactory.getInstance(ObjectService.class); + private final ObjectService objectService; + + GetObjectController(ServiceFactory serviceFactory) { + this.objectService = serviceFactory.getInstance(ObjectService.class); + } @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/HeadBucketController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/HeadBucketController.java index e0f4805..c3b5db4 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/HeadBucketController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/HeadBucketController.java @@ -15,7 +15,11 @@ */ class HeadBucketController implements HttpRequestHandler { - private final BucketService bucketService = ServiceFactory.getInstance(BucketService.class); + private final BucketService bucketService; + + HeadBucketController(ServiceFactory serviceFactory) { + this.bucketService = serviceFactory.getInstance(BucketService.class); + } @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/HeadObjectController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/HeadObjectController.java index 304e04b..786e539 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/HeadObjectController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/HeadObjectController.java @@ -22,7 +22,12 @@ */ class HeadObjectController implements HttpRequestHandler { - private final GetObjectService objectService = ServiceFactory.getInstance(ObjectService.class); + private final GetObjectService objectService; + + HeadObjectController(ServiceFactory serviceFactory) { + this.objectService = serviceFactory.getInstance(ObjectService.class); + } + @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/ListObjectVersionsController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/ListObjectVersionsController.java index 0f2cc95..757b517 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/ListObjectVersionsController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/ListObjectVersionsController.java @@ -26,9 +26,14 @@ */ class ListObjectVersionsController implements HttpRequestHandler { - private final ListObjectVersionsService listObjectVersionsService = ServiceFactory.getInstance(ObjectService.class); + private final ListObjectVersionsService listObjectVersionsService; - private final XmlMapper xmlMapper = ServiceFactory.getInstance(XmlMapper.class); + private final XmlMapper xmlMapper; + + ListObjectVersionsController(ServiceFactory serviceFactory) { + this.listObjectVersionsService = serviceFactory.getInstance(ObjectService.class); + this.xmlMapper = serviceFactory.getInstance(XmlMapper.class); + } @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/ListObjectsController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/ListObjectsController.java index 8fb9a27..7849137 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/ListObjectsController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/ListObjectsController.java @@ -26,9 +26,14 @@ */ class ListObjectsController implements HttpRequestHandler { - private final ListObjectsService listObjectsService = ServiceFactory.getInstance(ObjectService.class); + private final ListObjectsService listObjectsService; - private final XmlMapper xmlMapper = ServiceFactory.getInstance(XmlMapper.class); + private final XmlMapper xmlMapper; + + public ListObjectsController(ServiceFactory serviceFactory) { + this.listObjectsService = serviceFactory.getInstance(ObjectService.class); + this.xmlMapper = serviceFactory.getInstance(XmlMapper.class); + } @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/LocalS3ExceptionHandler.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/LocalS3ExceptionHandler.java index cb548da..0875649 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/LocalS3ExceptionHandler.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/LocalS3ExceptionHandler.java @@ -21,7 +21,11 @@ */ class LocalS3ExceptionHandler implements ExceptionHandler { - private final XmlMapper xmlMapper = ServiceFactory.getInstance(XmlMapper.class); + private final XmlMapper xmlMapper; + + LocalS3ExceptionHandler(ServiceFactory serviceFactory) { + this.xmlMapper = serviceFactory.getInstance(XmlMapper.class); + } @Override public void handle(LocalS3Exception e, HttpRequest request, HttpResponse response) { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/LocalS3RouterFactory.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/LocalS3RouterFactory.java index aa22874..47b8617 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/LocalS3RouterFactory.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/LocalS3RouterFactory.java @@ -4,302 +4,303 @@ import com.robothy.netty.router.Router; import com.robothy.s3.core.exception.LocalS3Exception; import com.robothy.s3.rest.constants.AmzHeaderNames; +import com.robothy.s3.rest.service.ServiceFactory; import io.netty.handler.codec.http.HttpMethod; +import java.util.Objects; public class LocalS3RouterFactory { - private final BucketPolicyController bucketPolicy = new BucketPolicyController(); - - private final Route CopyObject = Route.builder() - .method(HttpMethod.PUT) - .path("/{bucket}/{*key}") - .headerMatcher(headers -> headers.containsKey(AmzHeaderNames.X_AMZ_COPY_SOURCE)) - .handler(new CopyObjectController()) - .build(); - - private final Route CreateBucket = Route.builder() - .method(HttpMethod.PUT) - .path("/{bucket}") - .handler(new CreateBucketController()) - .build(); - - private final Route CreateMultipartUpload = Route.builder() - .method(HttpMethod.POST) - .path("/{bucket}/{*key}") - .paramMatcher(params -> params.containsKey("uploads")) - .handler(new CreateMultipartUploadController()) - .build(); - - private final Route CompleteMultipartUpload = Route.builder() - .method(HttpMethod.POST) - .path("/{bucket}/{*key}") - .handler(new CompleteMultipartUploadController()) - .build(); - - private final Route DeleteBucket = Route.builder() - .method(HttpMethod.DELETE) - .path("/{bucket}") - .handler(new DeleteBucketController()) - .build(); - - private final Route DeleteBucketPolicy = Route.builder() - .method(HttpMethod.DELETE) - .path("/{bucket}") - .paramMatcher(params -> params.containsKey("policy")) - .handler(bucketPolicy::delete) - .build(); - - private final Route DeleteBucketPolicy_ = Route.builder() - .method(HttpMethod.DELETE) - .path("/{bucket}/") - .paramMatcher(params -> params.containsKey("policy")) - .handler(bucketPolicy::delete) - .build(); - - private final Route DeleteBucketTagging = Route.builder() - .method(HttpMethod.DELETE) - .path("/{bucket}") - .paramMatcher(params -> params.containsKey("tagging")) - .handler(new DeleteBucketTaggingController()) - .build(); - - private final Route DeleteBucketTagging_ = Route.builder() - .method(HttpMethod.DELETE) - .path("/{bucket}/") - .paramMatcher(params -> params.containsKey("tagging")) - .handler(new DeleteBucketTaggingController()) - .build(); - - private final Route DeleteObject = Route.builder() - .method(HttpMethod.DELETE) - .path("/{bucket}/{*key}") - .handler(new DeleteObjectController()) - .build(); - - private final Route GetBucket = Route.builder() - .method(HttpMethod.GET) - .path("/v20180820/bucket/{bucket}") - .handler(new GetBucketController()) - .build(); - - private final Route GetBucketAcl = Route.builder() - .method(HttpMethod.GET) - .path("/{bucket}") - .paramMatcher(params -> params.containsKey("acl")) - .handler(new GetBucketAclController()) - .build(); - - private final Route GetBucketAcl_ = Route.builder() - .method(HttpMethod.GET) - .path("/{bucket}/") - .paramMatcher(params -> params.containsKey("acl")) - .handler(new GetBucketAclController()) - .build(); - - private final Route GetBucketPolicy = Route.builder() - .method(HttpMethod.GET) - .path("/{bucket}") - .paramMatcher(params -> params.containsKey("policy")) - .handler(bucketPolicy::get) - .build(); - - private final Route GetBucketPolicy_ = Route.builder() - .method(HttpMethod.GET) - .path("/{bucket}/") - .paramMatcher(params -> params.containsKey("policy")) - .handler(bucketPolicy::get) - .build(); - - private final Route GetBucketVersioning = Route.builder() - .method(HttpMethod.GET) - .path("/{bucket}") - .paramMatcher(params -> params.containsKey("versioning")) - .handler(new GetBucketVersioningController()) - .build(); - - private final Route GetBucketVersioning_ = Route.builder() - .method(HttpMethod.GET) - .path("/{bucket}/") - .paramMatcher(params -> params.containsKey("versioning")) - .handler(new GetBucketVersioningController()) - .build(); - - private final Route GetBucketTagging = Route.builder() - .method(HttpMethod.GET) - .path("/{bucket}") - .paramMatcher(params -> params.containsKey("tagging")) - .handler(new GetBucketTaggingController()) - .build(); - - private final Route GetBucketTagging_ = Route.builder() - .method(HttpMethod.GET) - .path("/{bucket}/") - .paramMatcher(params -> params.containsKey("tagging")) - .handler(new GetBucketTaggingController()) - .build(); - - private final Route GetObject = Route.builder() - .method(HttpMethod.GET) - .path("/{bucket}/{*key}") - .handler(new GetObjectController()) - .build(); - - private final Route HeadBucket = Route.builder() - .method(HttpMethod.HEAD) - .path("/{bucket}") - .handler(new HeadBucketController()) - .build(); - - private final Route HeadObject = Route.builder() - .method(HttpMethod.HEAD) - .path("/{bucket}/{*key}") - .handler(new HeadObjectController()) - .build(); - - private final Route ListObjects = Route.builder() - .method(HttpMethod.GET) - .path("/{bucket}") - .handler(new ListObjectsController()) - .build(); - - private final Route ListObjects_ = Route.builder() - .method(HttpMethod.GET) - .path("/{bucket}/") - .handler(new ListObjectsController()) - .build(); - - private final Route ListObjectVersions = Route.builder() - .method(HttpMethod.GET) - .path("/{bucket}") - .paramMatcher(params -> params.containsKey("versions")) - .handler(new ListObjectVersionsController()) - .build(); - - private final Route ListObjectVersions_ = Route.builder() - .method(HttpMethod.GET) - .path("/{bucket}/") - .paramMatcher(params -> params.containsKey("versions")) - .handler(new ListObjectVersionsController()) - .build(); - - private final Route PutBucketAcl = Route.builder() - .method(HttpMethod.PUT) - .path("/{bucket}") - .paramMatcher(params -> params.containsKey("acl")) - .handler(new PutBucketAclController()) - .build(); - - private final Route PutBucketAcl_ = Route.builder() - .method(HttpMethod.PUT) - .path("/{bucket}/") - .paramMatcher(params -> params.containsKey("acl")) - .handler(new PutBucketAclController()) - .build(); - - private final Route PutBucketPolicy = Route.builder() - .method(HttpMethod.PUT) - .path("/{bucket}") - .paramMatcher(params -> params.containsKey("policy")) - .handler(bucketPolicy::put) - .build(); - - private final Route PutBucketPolicy_ = Route.builder() - .method(HttpMethod.PUT) - .path("/{bucket}/") - .paramMatcher(params -> params.containsKey("policy")) - .handler(bucketPolicy::put) - .build(); - - private final Route PutBucketVersioning = Route.builder() - .method(HttpMethod.PUT) - .path("/{bucket}") - .paramMatcher(params -> params.containsKey("versioning")) - .handler(new PutBucketVersioningController()) - .build(); - - private final Route PutBucketVersioning_ = Route.builder() - .method(HttpMethod.PUT) - .path("/{bucket}/") - .paramMatcher(params -> params.containsKey("versioning")) - .handler(new PutBucketVersioningController()) - .build(); - - private final Route PutBucketTagging = Route.builder() - .method(HttpMethod.PUT) - .path("/{bucket}") - .paramMatcher(params -> params.containsKey("tagging")) - .handler(new PutBucketTaggingController()) - .build(); - - private final Route PutBucketTagging_ = Route.builder() - .method(HttpMethod.PUT) - .path("/{bucket}/") - .paramMatcher(params -> params.containsKey("tagging")) - .handler(new PutBucketTaggingController()) - .build(); - - private final Route PutObject = Route.builder() - .method(HttpMethod.PUT) - .path("/{bucket}/{*key}") - .handler(new PutObjectController()) - .build(); - - private final Route UploadPart = Route.builder() - .method(HttpMethod.PUT) - .path("/{bucket}/{*key}") - .paramMatcher(params -> params.containsKey("uploadId") && params.containsKey("partNumber")) - .handler(new UploadPartController()) - .build(); - - private final Router router = Router.router() - .route(CopyObject) - .route(CreateBucket) - .route(CreateMultipartUpload) - .route(CompleteMultipartUpload) - .route(DeleteBucket) - .route(DeleteBucketPolicy) - .route(DeleteBucketPolicy_) - .route(DeleteBucketTagging) - .route(DeleteBucketTagging_) - .route(DeleteObject) - .route(GetBucket) - .route(GetObject) - .route(GetBucketAcl) - .route(GetBucketAcl_) - .route(GetBucketPolicy) - .route(GetBucketPolicy_) - .route(GetBucketVersioning) - .route(GetBucketVersioning_) - .route(GetBucketTagging) - .route(GetBucketTagging_) - .route(HeadBucket) - .route(HeadObject) - .route(ListObjects) - .route(ListObjects_) - .route(ListObjectVersions) - .route(ListObjectVersions_) - .route(PutObject) - .route(PutBucketAcl) - .route(PutBucketAcl_) - .route(PutBucketPolicy) - .route(PutBucketPolicy_) - .route(PutBucketVersioning) - .route(PutBucketVersioning_) - .route(PutBucketTagging) - .route(PutBucketTagging_) - .route(UploadPart) - - .notFound(new NotFoundHandler()) - .exceptionHandler(LocalS3Exception.class, new LocalS3ExceptionHandler()) - .exceptionHandler(IllegalArgumentException.class, new IllegalArgumentExceptionHandler()) - .exceptionHandler(Exception.class, new ExceptionHandler()) - ; - /** * Create a new LocalS3Router instance. */ - public static Router create() { - return new LocalS3RouterFactory().router; + public static Router create(ServiceFactory serviceFactory) { + Objects.requireNonNull(serviceFactory); + BucketPolicyController bucketPolicy = new BucketPolicyController(serviceFactory); + + Route CopyObject = Route.builder() + .method(HttpMethod.PUT) + .path("/{bucket}/{*key}") + .headerMatcher(headers -> headers.containsKey(AmzHeaderNames.X_AMZ_COPY_SOURCE)) + .handler(new CopyObjectController(serviceFactory)) + .build(); + + Route CreateBucket = Route.builder() + .method(HttpMethod.PUT) + .path("/{bucket}") + .handler(new CreateBucketController(serviceFactory)) + .build(); + + Route CreateMultipartUpload = Route.builder() + .method(HttpMethod.POST) + .path("/{bucket}/{*key}") + .paramMatcher(params -> params.containsKey("uploads")) + .handler(new CreateMultipartUploadController(serviceFactory)) + .build(); + + Route CompleteMultipartUpload = Route.builder() + .method(HttpMethod.POST) + .path("/{bucket}/{*key}") + .handler(new CompleteMultipartUploadController(serviceFactory)) + .build(); + + Route DeleteBucket = Route.builder() + .method(HttpMethod.DELETE) + .path("/{bucket}") + .handler(new DeleteBucketController(serviceFactory)) + .build(); + + Route DeleteBucketPolicy = Route.builder() + .method(HttpMethod.DELETE) + .path("/{bucket}") + .paramMatcher(params -> params.containsKey("policy")) + .handler(bucketPolicy::delete) + .build(); + + Route DeleteBucketPolicy_ = Route.builder() + .method(HttpMethod.DELETE) + .path("/{bucket}/") + .paramMatcher(params -> params.containsKey("policy")) + .handler(bucketPolicy::delete) + .build(); + + Route DeleteBucketTagging = Route.builder() + .method(HttpMethod.DELETE) + .path("/{bucket}") + .paramMatcher(params -> params.containsKey("tagging")) + .handler(new DeleteBucketTaggingController(serviceFactory)) + .build(); + + Route DeleteBucketTagging_ = Route.builder() + .method(HttpMethod.DELETE) + .path("/{bucket}/") + .paramMatcher(params -> params.containsKey("tagging")) + .handler(new DeleteBucketTaggingController(serviceFactory)) + .build(); + + Route DeleteObject = Route.builder() + .method(HttpMethod.DELETE) + .path("/{bucket}/{*key}") + .handler(new DeleteObjectController(serviceFactory)) + .build(); + + Route GetBucket = Route.builder() + .method(HttpMethod.GET) + .path("/v20180820/bucket/{bucket}") + .handler(new GetBucketController(serviceFactory)) + .build(); + + Route GetBucketAcl = Route.builder() + .method(HttpMethod.GET) + .path("/{bucket}") + .paramMatcher(params -> params.containsKey("acl")) + .handler(new GetBucketAclController(serviceFactory)) + .build(); + + Route GetBucketAcl_ = Route.builder() + .method(HttpMethod.GET) + .path("/{bucket}/") + .paramMatcher(params -> params.containsKey("acl")) + .handler(new GetBucketAclController(serviceFactory)) + .build(); + + Route GetBucketPolicy = Route.builder() + .method(HttpMethod.GET) + .path("/{bucket}") + .paramMatcher(params -> params.containsKey("policy")) + .handler(bucketPolicy::get) + .build(); + + Route GetBucketPolicy_ = Route.builder() + .method(HttpMethod.GET) + .path("/{bucket}/") + .paramMatcher(params -> params.containsKey("policy")) + .handler(bucketPolicy::get) + .build(); + + Route GetBucketVersioning = Route.builder() + .method(HttpMethod.GET) + .path("/{bucket}") + .paramMatcher(params -> params.containsKey("versioning")) + .handler(new GetBucketVersioningController(serviceFactory)) + .build(); + + Route GetBucketVersioning_ = Route.builder() + .method(HttpMethod.GET) + .path("/{bucket}/") + .paramMatcher(params -> params.containsKey("versioning")) + .handler(new GetBucketVersioningController(serviceFactory)) + .build(); + + Route GetBucketTagging = Route.builder() + .method(HttpMethod.GET) + .path("/{bucket}") + .paramMatcher(params -> params.containsKey("tagging")) + .handler(new GetBucketTaggingController(serviceFactory)) + .build(); + + Route GetBucketTagging_ = Route.builder() + .method(HttpMethod.GET) + .path("/{bucket}/") + .paramMatcher(params -> params.containsKey("tagging")) + .handler(new GetBucketTaggingController(serviceFactory)) + .build(); + + Route GetObject = Route.builder() + .method(HttpMethod.GET) + .path("/{bucket}/{*key}") + .handler(new GetObjectController(serviceFactory)) + .build(); + + Route HeadBucket = Route.builder() + .method(HttpMethod.HEAD) + .path("/{bucket}") + .handler(new HeadBucketController(serviceFactory)) + .build(); + + Route HeadObject = Route.builder() + .method(HttpMethod.HEAD) + .path("/{bucket}/{*key}") + .handler(new HeadObjectController(serviceFactory)) + .build(); + + Route ListObjects = Route.builder() + .method(HttpMethod.GET) + .path("/{bucket}") + .handler(new ListObjectsController(serviceFactory)) + .build(); + + Route ListObjects_ = Route.builder() + .method(HttpMethod.GET) + .path("/{bucket}/") + .handler(new ListObjectsController(serviceFactory)) + .build(); + + Route ListObjectVersions = Route.builder() + .method(HttpMethod.GET) + .path("/{bucket}") + .paramMatcher(params -> params.containsKey("versions")) + .handler(new ListObjectVersionsController(serviceFactory)) + .build(); + + Route ListObjectVersions_ = Route.builder() + .method(HttpMethod.GET) + .path("/{bucket}/") + .paramMatcher(params -> params.containsKey("versions")) + .handler(new ListObjectVersionsController(serviceFactory)) + .build(); + + Route PutBucketAcl = Route.builder() + .method(HttpMethod.PUT) + .path("/{bucket}") + .paramMatcher(params -> params.containsKey("acl")) + .handler(new PutBucketAclController(serviceFactory)) + .build(); + + Route PutBucketAcl_ = Route.builder() + .method(HttpMethod.PUT) + .path("/{bucket}/") + .paramMatcher(params -> params.containsKey("acl")) + .handler(new PutBucketAclController(serviceFactory)) + .build(); + + Route PutBucketPolicy = Route.builder() + .method(HttpMethod.PUT) + .path("/{bucket}") + .paramMatcher(params -> params.containsKey("policy")) + .handler(bucketPolicy::put) + .build(); + + Route PutBucketPolicy_ = Route.builder() + .method(HttpMethod.PUT) + .path("/{bucket}/") + .paramMatcher(params -> params.containsKey("policy")) + .handler(bucketPolicy::put) + .build(); + + Route PutBucketVersioning = Route.builder() + .method(HttpMethod.PUT) + .path("/{bucket}") + .paramMatcher(params -> params.containsKey("versioning")) + .handler(new PutBucketVersioningController(serviceFactory)) + .build(); + + Route PutBucketVersioning_ = Route.builder() + .method(HttpMethod.PUT) + .path("/{bucket}/") + .paramMatcher(params -> params.containsKey("versioning")) + .handler(new PutBucketVersioningController(serviceFactory)) + .build(); + + Route PutBucketTagging = Route.builder() + .method(HttpMethod.PUT) + .path("/{bucket}") + .paramMatcher(params -> params.containsKey("tagging")) + .handler(new PutBucketTaggingController(serviceFactory)) + .build(); + + Route PutBucketTagging_ = Route.builder() + .method(HttpMethod.PUT) + .path("/{bucket}/") + .paramMatcher(params -> params.containsKey("tagging")) + .handler(new PutBucketTaggingController(serviceFactory)) + .build(); + + Route PutObject = Route.builder() + .method(HttpMethod.PUT) + .path("/{bucket}/{*key}") + .handler(new PutObjectController(serviceFactory)) + .build(); + + Route UploadPart = Route.builder() + .method(HttpMethod.PUT) + .path("/{bucket}/{*key}") + .paramMatcher(params -> params.containsKey("uploadId") && params.containsKey("partNumber")) + .handler(new UploadPartController(serviceFactory)) + .build(); + + return Router.router() + .route(CopyObject) + .route(CreateBucket) + .route(CreateMultipartUpload) + .route(CompleteMultipartUpload) + .route(DeleteBucket) + .route(DeleteBucketPolicy) + .route(DeleteBucketPolicy_) + .route(DeleteBucketTagging) + .route(DeleteBucketTagging_) + .route(DeleteObject) + .route(GetBucket) + .route(GetObject) + .route(GetBucketAcl) + .route(GetBucketAcl_) + .route(GetBucketPolicy) + .route(GetBucketPolicy_) + .route(GetBucketVersioning) + .route(GetBucketVersioning_) + .route(GetBucketTagging) + .route(GetBucketTagging_) + .route(HeadBucket) + .route(HeadObject) + .route(ListObjects) + .route(ListObjects_) + .route(ListObjectVersions) + .route(ListObjectVersions_) + .route(PutObject) + .route(PutBucketAcl) + .route(PutBucketAcl_) + .route(PutBucketPolicy) + .route(PutBucketPolicy_) + .route(PutBucketVersioning) + .route(PutBucketVersioning_) + .route(PutBucketTagging) + .route(PutBucketTagging_) + .route(UploadPart) + + .notFound(new NotFoundHandler()) + .exceptionHandler(LocalS3Exception.class, new LocalS3ExceptionHandler(serviceFactory)) + .exceptionHandler(IllegalArgumentException.class, new IllegalArgumentExceptionHandler()) + .exceptionHandler(Exception.class, new ExceptionHandler()) + ; } - + } diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/PutBucketAclController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/PutBucketAclController.java index b4b9292..5981061 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/PutBucketAclController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/PutBucketAclController.java @@ -22,9 +22,14 @@ */ class PutBucketAclController implements HttpRequestHandler { - private final BucketAclService aclService = ServiceFactory.getInstance(BucketService.class); + private final BucketAclService aclService; - private final XmlMapper xmlMapper = new XmlMapper(); + private final XmlMapper xmlMapper; + + PutBucketAclController(ServiceFactory serviceFactory) { + this.aclService = serviceFactory.getInstance(BucketService.class); + this.xmlMapper = serviceFactory.getInstance(XmlMapper.class); + } private static final Map HEADER_PERMISSION_MAP = Map.of( "x-amz-grant-full-control", "FULL_CONTROL", diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/PutBucketTaggingController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/PutBucketTaggingController.java index c6b689f..bd717c5 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/PutBucketTaggingController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/PutBucketTaggingController.java @@ -17,9 +17,14 @@ */ class PutBucketTaggingController implements HttpRequestHandler { - private final BucketService bucketService = ServiceFactory.getInstance(BucketService.class); + private final BucketService bucketService; - private final XmlMapper xmlMapper = ServiceFactory.getInstance(XmlMapper.class); + private final XmlMapper xmlMapper; + + PutBucketTaggingController(ServiceFactory serviceFactory) { + this.bucketService = serviceFactory.getInstance(BucketService.class); + this.xmlMapper = serviceFactory.getInstance(XmlMapper.class); + } @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/PutBucketVersioningController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/PutBucketVersioningController.java index ef361b6..1d7183d 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/PutBucketVersioningController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/PutBucketVersioningController.java @@ -18,9 +18,14 @@ */ class PutBucketVersioningController implements HttpRequestHandler { - private final BucketService bucketService = ServiceFactory.getInstance(BucketService.class); + private final BucketService bucketService; - private final XmlMapper xmlMapper = ServiceFactory.getInstance(XmlMapper.class); + private final XmlMapper xmlMapper; + + PutBucketVersioningController(ServiceFactory serviceFactory) { + this.bucketService = serviceFactory.getInstance(BucketService.class); + this.xmlMapper = serviceFactory.getInstance(XmlMapper.class); + } @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/PutObjectController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/PutObjectController.java index b30aef0..8ae0f42 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/PutObjectController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/PutObjectController.java @@ -8,16 +8,12 @@ import com.robothy.s3.core.service.ObjectService; import com.robothy.s3.rest.assertions.RequestAssertions; import com.robothy.s3.rest.constants.AmzHeaderNames; -import com.robothy.s3.rest.constants.AmzHeaderValues; import com.robothy.s3.rest.model.request.DecodedAmzRequestBody; import com.robothy.s3.rest.service.ServiceFactory; -import com.robothy.s3.rest.utils.InputStreamUtils; import com.robothy.s3.rest.utils.RequestUtils; import com.robothy.s3.rest.utils.ResponseUtils; -import io.netty.buffer.ByteBufInputStream; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpResponseStatus; -import java.io.InputStream; import java.util.Objects; /** @@ -25,7 +21,11 @@ */ class PutObjectController implements HttpRequestHandler { - private final ObjectService objectService = ServiceFactory.getInstance(ObjectService.class); + private final ObjectService objectService; + + PutObjectController(ServiceFactory serviceFactory) { + this.objectService = serviceFactory.getInstance(ObjectService.class); + } @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/UploadPartController.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/UploadPartController.java index ea05f30..c01a81b 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/UploadPartController.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/handler/UploadPartController.java @@ -7,17 +7,10 @@ import com.robothy.s3.core.service.ObjectService; import com.robothy.s3.core.service.UploadPartService; import com.robothy.s3.rest.assertions.RequestAssertions; -import com.robothy.s3.rest.constants.AmzHeaderNames; -import com.robothy.s3.rest.constants.AmzHeaderValues; import com.robothy.s3.rest.model.request.DecodedAmzRequestBody; import com.robothy.s3.rest.service.ServiceFactory; -import com.robothy.s3.rest.utils.InputStreamUtils; import com.robothy.s3.rest.utils.RequestUtils; import com.robothy.s3.rest.utils.ResponseUtils; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufInputStream; -import io.netty.handler.codec.http.HttpHeaderNames; -import java.io.InputStream; import lombok.extern.slf4j.Slf4j; /** @@ -26,7 +19,11 @@ @Slf4j class UploadPartController implements HttpRequestHandler { - private final UploadPartService uploadPartService = ServiceFactory.getInstance(ObjectService.class); + private final UploadPartService uploadPartService; + + UploadPartController(ServiceFactory serviceFactory) { + this.uploadPartService = serviceFactory.getInstance(ObjectService.class); + } @Override public void handle(HttpRequest request, HttpResponse response) throws Exception { diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/service/DefaultServiceFactory.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/service/DefaultServiceFactory.java new file mode 100644 index 0000000..adab0e6 --- /dev/null +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/service/DefaultServiceFactory.java @@ -0,0 +1,29 @@ +package com.robothy.s3.rest.service; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class DefaultServiceFactory implements ServiceFactory { + + private static final Map, Supplier> factoryMap = new HashMap<>(); + + @Override + public void register(Class clazz, Supplier factory) { + factoryMap.put(clazz, factory); + log.debug("Registered service " + factory.get().getClass().getName() + "."); + } + + @Override + public T getInstance(Class clazz) { + if (!factoryMap.containsKey(clazz)) { + throw new IllegalArgumentException("Not cannot find service factory for " + clazz.getName() + "."); + } + + //noinspection unchecked + return (T) factoryMap.get(clazz).get(); + } + +} diff --git a/local-s3-rest/src/main/java/com/robothy/s3/rest/service/ServiceFactory.java b/local-s3-rest/src/main/java/com/robothy/s3/rest/service/ServiceFactory.java index 4e73c97..4d9f646 100644 --- a/local-s3-rest/src/main/java/com/robothy/s3/rest/service/ServiceFactory.java +++ b/local-s3-rest/src/main/java/com/robothy/s3/rest/service/ServiceFactory.java @@ -1,27 +1,11 @@ package com.robothy.s3.rest.service; -import java.util.HashMap; -import java.util.Map; import java.util.function.Supplier; -import lombok.extern.slf4j.Slf4j; -@Slf4j -public class ServiceFactory { +public interface ServiceFactory { - private static final Map, Supplier> factoryMap = new HashMap<>(); + void register(Class clazz, Supplier factory); - public static void register(Class clazz, Supplier factory) { - factoryMap.put(clazz, factory); - log.debug("Registered service " + factory.get().getClass().getName() + "."); - } - - public static T getInstance(Class clazz) { - if (!factoryMap.containsKey(clazz)) { - throw new IllegalArgumentException("Not cannot find service factory for " + clazz.getName() + "."); - } - - //noinspection unchecked - return (T) factoryMap.get(clazz).get(); - } + T getInstance(Class clazz); } diff --git a/local-s3-rest/src/test/java/com/robothy/s3/rest/handler/CopyObjectControllerTest.java b/local-s3-rest/src/test/java/com/robothy/s3/rest/handler/CopyObjectControllerTest.java index 34a3381..7c46843 100644 --- a/local-s3-rest/src/test/java/com/robothy/s3/rest/handler/CopyObjectControllerTest.java +++ b/local-s3-rest/src/test/java/com/robothy/s3/rest/handler/CopyObjectControllerTest.java @@ -6,18 +6,21 @@ import com.robothy.s3.core.exception.InvalidArgumentException; import com.robothy.s3.core.model.request.CopyObjectOptions; import com.robothy.s3.rest.constants.AmzHeaderNames; +import com.robothy.s3.rest.service.ServiceFactory; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; class CopyObjectControllerTest { @MethodSource("copySources") @ParameterizedTest void parseCopyOptions(String copySource, CopyObjectOptions result) { - CopyObjectController copyObjectController = new CopyObjectController(); + ServiceFactory serviceFactory = Mockito.mock(ServiceFactory.class); + CopyObjectController copyObjectController = new CopyObjectController(serviceFactory); HttpRequest request = HttpRequest.builder().build(); request.getHeaders().put(AmzHeaderNames.X_AMZ_COPY_SOURCE, copySource); if (result == null) {