All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.dirigible.components.api.s3.S3Facade Maven / Gradle / Ivy

/*
 * Copyright (c) 2024 Eclipse Dirigible contributors
 *
 * All rights reserved. This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 *
 * SPDX-FileCopyrightText: Eclipse Dirigible contributors SPDX-License-Identifier: EPL-2.0
 */
package org.eclipse.dirigible.components.api.s3;

import org.eclipse.dirigible.commons.config.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.ResponseInputStream;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
import software.amazon.awssdk.services.s3.paginators.ListObjectsV2Iterable;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.DirectoryUpload;
import software.amazon.awssdk.transfer.s3.model.UploadDirectoryRequest;

import java.io.IOException;
import java.net.URI;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;

/**
 * The Class S3Facade.
 */
@Component
public class S3Facade {
    /**
     * The Constant logger.
     */
    private static final Logger logger = LoggerFactory.getLogger(S3Facade.class);
    /** The Constant AWS_ACCESS_KEY_ID. */
    private static final String AWS_ACCESS_KEY_ID = Configuration.get("AWS_ACCESS_KEY_ID");
    /** The Constant AWS_SECRET_ACCESS_KEY. */
    private static final String AWS_SECRET_ACCESS_KEY = Configuration.get("AWS_SECRET_ACCESS_KEY");
    /** The Constant DIRIGIBLE_S3_PROVIDER. */
    private static final String DIRIGIBLE_S3_PROVIDER = Configuration.get("DIRIGIBLE_S3_PROVIDER", "aws");
    /** The Constant LOCALSTACK_URI. */
    private static final String DEFAULT_LOCALSTACK_URI = "https://s3.localhost.localstack.cloud:4566";

    /** The Constant FOLDER_SUFFIX. */
    private static final String FOLDER_SUFFIX = "/";
    /** The instance. */
    private static S3Facade INSTANCE;

    /** The tenant path resolved. */
    private final TenantPathResolved tenantPathResolved;
    /** The s 3. */
    private S3Client s3;

    /**
     * Instantiates a new s 3 facade.
     *
     * @param tenantPathResolved the tenant path resolved
     */
    S3Facade(TenantPathResolved tenantPathResolved) {
        this.tenantPathResolved = tenantPathResolved;
        INSTANCE = this;
    }

    /**
     * Upload directory.
     *
     * @param sourceDirectory the source directory
     */
    public static void uploadDirectory(String sourceDirectory) {
        String bucket = getBucketName();
        S3TransferManager transferManager = S3TransferManager.create();

        DirectoryUpload directoryUpload = transferManager.uploadDirectory(UploadDirectoryRequest.builder()
                                                                                                .source(Paths.get(sourceDirectory))
                                                                                                .bucket(bucket)
                                                                                                .build());

        directoryUpload.completionFuture()
                       .join();
    }

    /**
     * Gets the bucket name.
     *
     * @return the bucket name
     */
    private static String getBucketName() {
        return Configuration.get("DIRIGIBLE_S3_BUCKET", "cmis-bucket");
    }

    /**
     * Delete.
     *
     * @param name the name
     */
    public static void delete(String name) {
        String tenantName = INSTANCE.tenantPathResolved.resolve(name);
        String bucket = getBucketName();
        if (isFolderPath(tenantName)) {
            deleteFolder(tenantName);
        } else {
            DeleteObjectRequest objectRequest = DeleteObjectRequest.builder()
                                                                   .bucket(bucket)
                                                                   .key(tenantName)
                                                                   .build();

            S3Facade.get()
                    .getS3Client()
                    .deleteObject(objectRequest);
        }
    }

    /**
     * Checks if is folder path.
     *
     * @param path the path
     * @return true, if is folder path
     */
    private static boolean isFolderPath(String path) {
        return path.endsWith(FOLDER_SUFFIX);
    }

    /**
     * Delete folder.
     *
     * @param prefix the prefix
     */
    public static void deleteFolder(String prefix) {
        String tenantPrefix = INSTANCE.tenantPathResolved.resolve(prefix);
        String bucket = getBucketName();
        try (S3Client s3Client = S3Client.builder()
                                         .build()) {
            ListObjectsV2Request request = ListObjectsV2Request.builder()
                                                               .bucket(bucket)
                                                               .prefix(tenantPrefix)
                                                               .build();
            ListObjectsV2Iterable list = s3Client.listObjectsV2Paginator(request);

            List objectIdentifiers = list.stream()
                                                           .flatMap(r -> r.contents()
                                                                          .stream())
                                                           .map(o -> ObjectIdentifier.builder()
                                                                                     .key(o.key())
                                                                                     .build())
                                                           .collect(Collectors.toList());

            if (objectIdentifiers.isEmpty())
                return;
            DeleteObjectsRequest deleteObjectsRequest = DeleteObjectsRequest.builder()
                                                                            .bucket(bucket)
                                                                            .delete(Delete.builder()
                                                                                          .objects(objectIdentifiers)
                                                                                          .build())
                                                                            .build();
            s3Client.deleteObjects(deleteObjectsRequest);
        }
    }

    /**
     * Gets the s3 client.
     *
     * @return the s3 client
     */
    private S3Client getS3Client() {
        return INSTANCE.s3;
    }

    /**
     * Gets the S3Facade.
     *
     * @return the s3 facade
     */
    public static S3Facade get() {
        if (INSTANCE.s3 == null) {
            INSTANCE.initClient();
        }
        return INSTANCE;
    }

    /**
     * Initializes the client.
     */
    private void initClient() {
        String bucket = getBucketName();

        if (DIRIGIBLE_S3_PROVIDER.equals("aws")) {
            if (AWS_ACCESS_KEY_ID == null || AWS_SECRET_ACCESS_KEY == null) {
                logger.warn("AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY not set, so assuming runing on AWS");
                s3 = S3Client.builder()
                             .build();
            } else {
                AwsBasicCredentials credentials = AwsBasicCredentials.create(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY);
                s3 = S3Client.builder()
                             .credentialsProvider(StaticCredentialsProvider.create(credentials))
                             .build();
            }
        } else if (DIRIGIBLE_S3_PROVIDER.equals("localstack")) {
            s3 = S3Client.builder()
                         .endpointOverride(URI.create(DEFAULT_LOCALSTACK_URI))
                         .build();
            createBucket(bucket);
        } else {
            logger.warn("Unknown S3 provider via DIRIGIBLE_S3_PROVIDER: " + DIRIGIBLE_S3_PROVIDER);
            return;
        }
    }

    /**
     * Creates the bucket.
     *
     * @param bucket the bucket
     */
    private void createBucket(String bucket) {
        CreateBucketRequest createBucketRequest = CreateBucketRequest.builder()
                                                                     .bucket(bucket)
                                                                     .build();

        try {
            s3.createBucket(createBucketRequest);
        } catch (BucketAlreadyOwnedByYouException ex) {
            logger.debug("Bucket: [" + bucket + "] already created", ex);
        }
    }

    /**
     * Gets the.
     *
     * @param name the name
     * @return the byte[]
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public static byte[] get(String name) throws IOException {
        String bucket = getBucketName();

        String tenantName = INSTANCE.tenantPathResolved.resolve(name);
        ResponseInputStream response = S3Facade.get()
                                                                  .getS3Client()
                                                                  .getObject(GetObjectRequest.builder()
                                                                                             .bucket(bucket)
                                                                                             .key(tenantName)
                                                                                             .build());

        return response.readAllBytes();
    }

    /**
     * Update.
     *
     * @param name the name
     * @param inputStream the input stream
     */
    public static void update(String name, byte[] inputStream) {
        // will upload the updated object to S3, overwriting the existing object
        // TODO fix update
        put(name, inputStream, "");
    }

    /**
     * Put.
     *
     * @param name the name
     * @param input the input
     * @param contentType the content type
     */
    public static void put(String name, byte[] input, String contentType) {
        String tenantName = INSTANCE.tenantPathResolved.resolve(name);
        String bucket = getBucketName();
        PutObjectRequest objectRequest;
        if (isFolderPath(tenantName)) {
            objectRequest = PutObjectRequest.builder()
                                            .bucket(bucket)
                                            .key(tenantName)
                                            .build();
        } else {
            objectRequest = PutObjectRequest.builder()
                                            .bucket(bucket)
                                            .key(tenantName)
                                            .contentType(contentType)
                                            .build();
        }
        S3Facade.get()
                .getS3Client()
                .putObject(objectRequest, RequestBody.fromBytes(input));
    }

    /**
     * List objects.
     *
     * @param path the path
     * @return the list
     */
    public static List listObjects(String path) {
        String tenantPath = INSTANCE.tenantPathResolved.resolve(path);

        String bucket = getBucketName();
        ListObjectsV2Request listObjectsV2Request = ListObjectsV2Request.builder()
                                                                        .bucket(bucket)
                                                                        .prefix(tenantPath)
                                                                        .build();

        ListObjectsV2Response listObjectsV2Response = S3Facade.get()
                                                              .getS3Client()
                                                              .listObjectsV2(listObjectsV2Request);

        List contents = listObjectsV2Response.contents();

        logger.info("Number of objects in the bucket: [{}]", contents.size());
        return contents;
    }

    /**
     * Exists.
     *
     * @param keyName the key name
     * @return true, if successful
     */
    public static boolean exists(String keyName) {
        String tenantKeyName = INSTANCE.tenantPathResolved.resolve(keyName);
        String bucket = getBucketName();
        try {
            HeadObjectRequest objectRequest = HeadObjectRequest.builder()
                                                               .key(tenantKeyName)
                                                               .bucket(bucket)
                                                               .build();

            HeadObjectResponse objectHead = S3Facade.get()
                                                    .getS3Client()
                                                    .headObject(objectRequest);
            objectHead.contentType();
            return true;
        } catch (BucketAlreadyExistsException | BucketAlreadyOwnedByYouException ex) {
            logger.debug("[{}] already exists", tenantKeyName, ex);
            return true;
        } catch (NoSuchKeyException ex) {
            logger.debug("[{}] is missing", tenantKeyName, ex);
            return false;
        } catch (S3Exception ex) {
            logger.warn("Returning false for [{}]", tenantKeyName, ex);
            return false;
        }
    }

    /**
     * Gets the object content type.
     *
     * @param keyName the key name
     * @return the object content type
     */
    public static String getObjectContentType(String keyName) {
        String bucket = getBucketName();
        String tenantKeyName = INSTANCE.tenantPathResolved.resolve(keyName);
        try {
            HeadObjectRequest objectRequest = HeadObjectRequest.builder()
                                                               .key(tenantKeyName)
                                                               .bucket(bucket)
                                                               .build();

            HeadObjectResponse objectHead = S3Facade.get()
                                                    .getS3Client()
                                                    .headObject(objectRequest);
            return objectHead.contentType();
        } catch (S3Exception e) {
            logger.error(e.awsErrorDetails()
                          .errorMessage());
            return "";
        }
    }

    /**
     * Sets the client for test container.
     *
     * @param localstackUri the new client for test container
     * @param bucket the bucket
     */
    public static void setClientForTestContainer(URI localstackUri, String bucket) {
        S3Facade.get()
                .setS3Client(S3Client.builder()
                                     .endpointOverride(localstackUri)
                                     .build());
        S3Facade.get()
                .createBucket(bucket);
    }

    /**
     * Sets the s3 client.
     *
     * @param s3 the new s3 client
     */
    public void setS3Client(S3Client s3) {
        INSTANCE.s3 = s3;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy