Java tutorial
/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.blobstore.s3.internal; import java.io.InputStream; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; import org.slf4j.Logger; import org.xwiki.blobstore.BlobStore; import org.xwiki.component.annotation.Component; import org.xwiki.component.phase.Initializable; import org.xwiki.component.phase.InitializationException; import org.xwiki.configuration.ConfigurationSource; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.S3Object; /** * Amazon S3 blob store implementation. This is a singleton in order to reuse as much as possible the Amazon S3 client * object (https://forums.aws.amazon.com/thread.jspa?threadID=50723) * * @version $Id: e9efc1b73cfd40ce01ea9f92eb14d02239b05f7f $ */ @Component @Named("s3") @Singleton public class S3BlobStore implements BlobStore, Initializable { /** * The bucket to be used for storing data. */ private String bucket; /** * The S3 client. No particular mechanisms are used in the code to deal with multiple thread interactions because * the Amazon S3 client is thread safe: https://forums.aws.amazon.com/thread.jspa?threadID=50723 */ private AmazonS3Client client; /** * Configuration. */ @Inject @Named("cloud") private ConfigurationSource configurationSource; /** * Logger. */ @Inject private Logger logger; /** * The namespace where to store data. */ private Object namespace; @Override public void initialize() throws InitializationException { final String formatString = "%s property is not defined."; this.logger.error("Using {}", this.configurationSource.getClass().getName()); this.bucket = this.configurationSource.getProperty(BlobStore.BLOBSTORE_BUCKET_PROPERTY); if (this.bucket == null) { throw new InitializationException(String.format(formatString, BlobStore.BLOBSTORE_BUCKET_PROPERTY)); } String accessKey = this.configurationSource.getProperty(BlobStore.BLOBSTORE_IDENTITY_PROPERTY); if (accessKey == null) { throw new InitializationException(String.format(formatString, BlobStore.BLOBSTORE_IDENTITY_PROPERTY)); } String secretKey = this.configurationSource.getProperty(BlobStore.BLOBSTORE_CREDENTIAL_PROPERTY); if (secretKey == null) { throw new InitializationException(String.format(formatString, BlobStore.BLOBSTORE_CREDENTIAL_PROPERTY)); } BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey); this.client = new AmazonS3Client(credentials); boolean bucketExists = this.client.doesBucketExist(this.bucket); if (!bucketExists) { this.client.createBucket(this.bucket); } this.namespace = this.configurationSource.getProperty(BlobStore.BLOBSTORE_NAMESPACE_PROPERTY); this.logger.debug("S3 blob store initialized using namespace '{}' and bucket '{}'", this.namespace != null ? this.namespace : "no namespace specified", this.bucket); } @Override public void deleteBlob(String path) { String normalizedPath = normalizePath(path); this.logger.debug("Deleting blob '{}' from bucket '{}'", normalizedPath, this.bucket); this.client.deleteObject(this.bucket, normalizedPath); } @Override public InputStream getBlob(String path) { String normalizedPath = normalizePath(path); this.logger.debug("Getting blob '{}' from bucket '{}'", normalizedPath, this.bucket); S3Object object = this.client.getObject(this.bucket, normalizedPath); if (object != null) { return object.getObjectContent(); } return null; } @Override public void putBlob(String path, InputStream content) { putBlob(normalizePath(path), content, 0); } @Override public void putBlob(String path, InputStream content, long length) { String normalizedPath = normalizePath(path); this.logger.debug("Putting blob to '{}'", normalizedPath); ObjectMetadata objectMetadata = new ObjectMetadata(); if (length > 0) { objectMetadata.setContentLength(length); } this.client.putObject(this.bucket, normalizedPath, content, objectMetadata); } /** * Return the actual path for retrieving the blob by taking into account the namespace. * * @param path The path provided by the user. * @return The actual path that takes into account the namespace, if provided in the configuration. */ private String normalizePath(String path) { if (this.namespace != null) { return String.format("%s/%s", this.namespace, path); } return path; } }