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

com.github.platform.team.plugin.AmazonS3Wagon Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2018-Present Platform Team.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.github.platform.team.plugin;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.maven.wagon.ResourceDoesNotExistException;
import org.apache.maven.wagon.TransferFailedException;
import org.apache.maven.wagon.authentication.AuthenticationException;
import org.apache.maven.wagon.authentication.AuthenticationInfo;
import org.apache.maven.wagon.proxy.ProxyInfoProvider;
import org.apache.maven.wagon.repository.Repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.amazonaws.AmazonServiceException;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.internal.Mimetypes;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.ListObjectsRequest;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import com.amazonaws.services.s3.model.SSECustomerKey;
import com.github.platform.team.plugin.aws.AWSMavenCredentialsProviderChain;
import com.github.platform.team.plugin.data.TransferProgress;
import com.github.platform.team.plugin.data.transfer.TransferProgressFileInputStream;
import com.github.platform.team.plugin.data.transfer.TransferProgressFileOutputStream;
import com.github.platform.team.plugin.maven.AbstractWagon;
import com.github.platform.team.plugin.util.IOUtils;
import com.github.platform.team.plugin.util.S3Utils;

/**
 * An implementation of the Maven Wagon interface that allows you to access the Amazon S3 service. URLs that reference
 * the S3 service should be in the form of s3://bucket.name. As an example
 * s3://static.springframework.org would put files into the static.springframework.org bucket
 * on the S3 service.
 * 

* This implementation uses the username and passphrase portions of the server authentication * metadata for credentials. */ public final class AmazonS3Wagon extends AbstractWagon { private static final String US_EAST_1 = "us-east-1"; private static final String US = "US"; private static final String KEY_FORMAT = "%s%s"; private static final String RESOURCE_FORMAT = "%s(.*)"; private volatile AmazonS3 amazonS3; private volatile String bucketName; private volatile String baseDirectory; private volatile SSECustomerKey sseCustomerKey; private volatile String sseBase64Key; private volatile String sseAlgorithm; private volatile boolean sse; private Logger logger = LoggerFactory.getLogger( this.getClass() ); /** * Creates a new instance of the wagon */ public AmazonS3Wagon() { super(true); } AmazonS3Wagon(AmazonS3 amazonS3, String bucketName, String baseDirectory) { super(true); this.amazonS3 = amazonS3; this.bucketName = bucketName; this.baseDirectory = baseDirectory; } private static ObjectMetadata getObjectMetadata(AmazonS3 amazonS3, String bucketName, String baseDirectory, String resourceName) { return amazonS3.getObjectMetadata(bucketName, getKey(baseDirectory, resourceName)); } private static String getKey(String baseDirectory, String resourceName) { return String.format(KEY_FORMAT, baseDirectory, resourceName); } private static List getResourceNames(ObjectListing objectListing, Pattern pattern) { List resourceNames = new ArrayList<>(); for (String commonPrefix : objectListing.getCommonPrefixes()) { resourceNames.add(getResourceName(commonPrefix, pattern)); } for (S3ObjectSummary s3ObjectSummary : objectListing.getObjectSummaries()) { resourceNames.add(getResourceName(s3ObjectSummary.getKey(), pattern)); } return resourceNames; } private static String getResourceName(String key, Pattern pattern) { Matcher matcher = pattern.matcher(key); if (matcher.find()) { return matcher.group(1); } return key; } private static void mkdirs(AmazonS3 amazonS3, String bucketName, String path, int index) throws TransferFailedException { int directoryIndex = path.indexOf('/', index) + 1; if (directoryIndex != 0) { String directory = path.substring(0, directoryIndex); PutObjectRequest putObjectRequest = createDirectoryPutObjectRequest(bucketName, directory); try { amazonS3.putObject(putObjectRequest); } catch (AmazonServiceException e) { throw new TransferFailedException(String.format("Cannot write directory '%s'", directory), e); } mkdirs(amazonS3, bucketName, path, directoryIndex); } } private static PutObjectRequest createDirectoryPutObjectRequest(String bucketName, String key) { ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[0]); ObjectMetadata objectMetadata = new ObjectMetadata(); objectMetadata.setContentLength(0); return new PutObjectRequest(bucketName, key, inputStream, objectMetadata).withCannedAcl(CannedAccessControlList.PublicRead); } private static String getBucketRegion(AWSCredentialsProvider credentialsProvider, ClientConfiguration clientConfiguration, String bucketName) { String bucketLocation = AmazonS3Client.builder() .withCredentials(credentialsProvider) .withClientConfiguration(clientConfiguration) .enableForceGlobalBucketAccess() .build() .getBucketLocation(bucketName); if (bucketLocation == null || US.equalsIgnoreCase(bucketLocation)) bucketLocation = US_EAST_1; return bucketLocation; } @Override protected void connectToRepository(Repository repository, AuthenticationInfo authenticationInfo, ProxyInfoProvider proxyInfoProvider) throws AuthenticationException { if (this.amazonS3 == null) { try { if ( sseBase64Key != null && sseBase64Key.trim().length() > 0) { if ( logger.isDebugEnabled() ) logger.debug( "Using customer key for server-side encryption." ); sseCustomerKey = new SSECustomerKey( sseBase64Key ); } } catch (Exception e) { logger.error( "Unable to load SSE Customer Key. Check settings.xml and make sure key is base64 encoded.", e ); } AWSMavenCredentialsProviderChain credentialsProvider = new AWSMavenCredentialsProviderChain(authenticationInfo); ClientConfiguration clientConfiguration = S3Utils.getClientConfiguration(proxyInfoProvider); this.bucketName = S3Utils.getBucketName(repository); this.baseDirectory = S3Utils.getBaseDirectory(repository); this.amazonS3 = AmazonS3Client.builder() .withCredentials(credentialsProvider) .withClientConfiguration(clientConfiguration) .withRegion(getBucketRegion(credentialsProvider, clientConfiguration, this.bucketName)) .build(); } } @Override protected void disconnectFromRepository() { this.amazonS3 = null; this.bucketName = null; this.baseDirectory = null; } @Override protected boolean doesRemoteResourceExist(String resourceName) { try { getObjectMetadata(this.amazonS3, this.bucketName, this.baseDirectory, resourceName); return true; } catch (AmazonServiceException e) { return false; } } @Override protected boolean isRemoteResourceNewer(String resourceName, long timestamp) throws ResourceDoesNotExistException { try { Date lastModified = getObjectMetadata(this.amazonS3, this.bucketName, this.baseDirectory, resourceName).getLastModified(); return lastModified == null || lastModified.getTime() > timestamp; } catch (AmazonServiceException e) { throw new ResourceDoesNotExistException(String.format("'%s' does not exist", resourceName), e); } } @Override protected List listDirectory(String directory) throws ResourceDoesNotExistException { List directoryContents = new ArrayList(); try { String prefix = getKey(this.baseDirectory, directory); Pattern pattern = Pattern.compile(String.format(RESOURCE_FORMAT, prefix)); ListObjectsRequest listObjectsRequest = new ListObjectsRequest() // .withBucketName(this.bucketName) // .withPrefix(prefix) // .withDelimiter("/"); ObjectListing objectListing; objectListing = this.amazonS3.listObjects(listObjectsRequest); directoryContents.addAll(getResourceNames(objectListing, pattern)); while (objectListing.isTruncated()) { objectListing = this.amazonS3.listObjects(listObjectsRequest); directoryContents.addAll(getResourceNames(objectListing, pattern)); } return directoryContents; } catch (AmazonServiceException e) { throw new ResourceDoesNotExistException(String.format("'%s' does not exist", directory), e); } } @Override protected void getResource(String resourceName, File destination, TransferProgress transferProgress) throws TransferFailedException, ResourceDoesNotExistException { InputStream in = null; OutputStream out = null; try { S3Object s3Object = this.amazonS3.getObject(this.bucketName, getKey(this.baseDirectory, resourceName)); in = s3Object.getObjectContent(); out = new TransferProgressFileOutputStream(destination, transferProgress); IOUtils.copy(in, out); } catch (AmazonServiceException e) { throw new ResourceDoesNotExistException(String.format("'%s' does not exist", resourceName), e); } catch (FileNotFoundException e) { throw new TransferFailedException(String.format("Cannot write file to '%s'", destination), e); } catch (IOException e) { throw new TransferFailedException(String.format("Cannot read from '%s' and write to '%s'", resourceName, destination), e); } finally { IOUtils.closeQuietly(in, out); } } @Override protected void putResource(File source, String destination, TransferProgress transferProgress) throws TransferFailedException, ResourceDoesNotExistException { String key = getKey(this.baseDirectory, destination); //mkdirs(amazonS3, this.bucketName, key, 0); InputStream in = null; try { ObjectMetadata objectMetadata = new ObjectMetadata(); objectMetadata.setContentLength(source.length()); objectMetadata.setContentType(Mimetypes.getInstance().getMimetype(source)); in = new TransferProgressFileInputStream(source, transferProgress); PutObjectRequest request = new PutObjectRequest(this.bucketName, key, in, objectMetadata); applyServerSideEncryption( request ); this.amazonS3.putObject(request); } catch (AmazonServiceException e) { throw new TransferFailedException(String.format("Cannot write file to '%s'", destination), e); } catch (FileNotFoundException e) { throw new ResourceDoesNotExistException(String.format("Cannot read file from '%s'", source), e); } finally { IOUtils.closeQuietly(in); } } private void applyServerSideEncryption( PutObjectRequest pRequest ) { if ( logger.isDebugEnabled() ) { logger.debug( "Using server-side encryption? {} (Algorithm={}), CustomerKey provided? {}", sse, sseAlgorithm, (sseCustomerKey != null) ); } if ( ! sse ) return; if ( sseCustomerKey == null ) { pRequest.getMetadata().setSSEAlgorithm( sseAlgorithm ); } else { pRequest.setSSECustomerKey( sseCustomerKey ); } } public void setSseAlgorithm( String pSseAlgorithm ) { sseAlgorithm = pSseAlgorithm; } public String getSseAlgorithm() { return sseAlgorithm; } public void setSse( boolean pSse ) { sse = pSse; } public boolean isSse() { return sse; } public void setSseBase64Key( String pSseBase64Key ) { sseBase64Key = pSseBase64Key; } public String getSseBase64Key() { return sseBase64Key; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy