/* * Copyright 2014 Amazon Technologies, Inc. * * 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://aws.amazon.com/apache2.0 * * This file 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.amediamanager.dao; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.HashMap; import java.util.Map; import java.util.UUID; import javax.annotation.PostConstruct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.web.multipart.commons.CommonsMultipartFile; import com.amazonaws.AmazonServiceException; import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.GetItemRequest; import com.amazonaws.services.dynamodbv2.model.GetItemResult; import com.amazonaws.services.dynamodbv2.model.PutItemRequest; import com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.CannedAccessControlList; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.PutObjectRequest; import com.amediamanager.config.ConfigurationSettings; import com.amediamanager.config.ConfigurationSettings.ConfigProps; import com.amediamanager.domain.User; import com.amediamanager.exceptions.DataSourceTableDoesNotExistException; import com.amediamanager.exceptions.UserExistsException; public class DynamoDbUserDaoImpl implements UserDao { private static final Logger LOG = LoggerFactory.getLogger(DynamoDbUserDaoImpl.class); @Autowired protected ConfigurationSettings config; @Autowired protected AmazonDynamoDB dynamoClient; @Autowired protected AmazonS3 s3Client; /** Attribute names **/ public static final String HASH_KEY_NAME = "EMail"; public static final String EMAIL_ATTR = HASH_KEY_NAME; public static final String PASSWORD_ATTR = "Password"; public static final String NICKNAME_ATTR = "Nickname"; public static final String TAGLINE_ATTR = "Tagline"; public static final String PROFILE_PIC_KEY_ATTR = "ProfilePicKey"; public static final String ALERT_ON_NEW_CONTENT_ATTR = "AlertOnNewContent"; @PostConstruct public void init() { } @Override public void save(User user) throws UserExistsException, DataSourceTableDoesNotExistException { try { // See if User item exists User existing = this.find(user.getEmail()); // If the user exists, throw an exception if(existing != null) { throw new UserExistsException(); } // Convert the User object to a Map. The DynamoDB PutItemRequest object // requires the Item to be in the Map<String, AttributeValue> structure Map<String, AttributeValue> userItem = getMapFromUser(user); // Create a request to save and return the user PutItemRequest putItemRequest = new PutItemRequest() .withTableName(config.getProperty(ConfigurationSettings.ConfigProps.DDB_USERS_TABLE)) .withItem(userItem); // Save user dynamoClient.putItem(putItemRequest); } catch (ResourceNotFoundException rnfe) { throw new DataSourceTableDoesNotExistException(config.getProperty(ConfigurationSettings.ConfigProps.DDB_USERS_TABLE)); } catch (AmazonServiceException ase) { throw ase; } } @Override public void update(User user) throws DataSourceTableDoesNotExistException { try { // If the object includes a profile pic file, upload it to S3 if(user.getprofilePicData() != null && user.getprofilePicData().getSize() > 0) { try { String profilePicUrl = this.uploadFileToS3(user.getprofilePicData()); user.setProfilePicKey(profilePicUrl); } catch (IOException e) { LOG.error("Error uploading profile pic to S3", e); } } // Convert the User object to a Map Map<String, AttributeValue> userItem = getMapFromUser(user); // Create a request to save and return the user PutItemRequest putItemRequest = new PutItemRequest() .withItem(userItem) .withTableName(config.getProperty(ConfigurationSettings.ConfigProps.DDB_USERS_TABLE)); // Save user dynamoClient.putItem(putItemRequest); } catch (ResourceNotFoundException rnfe) { throw new DataSourceTableDoesNotExistException(config.getProperty(ConfigurationSettings.ConfigProps.DDB_USERS_TABLE)); } catch (AmazonServiceException ase) { throw ase; } } @Override public User find(String email) throws DataSourceTableDoesNotExistException { try { User user = null; // Create a request to find a User by email address GetItemRequest getItemRequest = new GetItemRequest() .withTableName( config.getProperty(ConfigurationSettings.ConfigProps.DDB_USERS_TABLE)) .addKeyEntry(HASH_KEY_NAME, new AttributeValue(email)); // Issue the request to find the User in DynamoDB GetItemResult getItemResult = dynamoClient.getItem(getItemRequest); // If an item was found if (getItemResult.getItem() != null) { // Marshal the Map<String, AttributeValue> structure returned in // the // GetItemResult to a User object user = getUserFromMap(getItemResult.getItem()); } return user; } catch (ResourceNotFoundException rnfe) { // The ResourceNotFoundException method is thrown by the getItem() // method // if the DynamoDB table doesn't exist. This exception is re-thrown // as a // custom, more specific DataSourceTableDoesNotExistException that // users // of this DAO understand. throw new DataSourceTableDoesNotExistException(config.getProperty(ConfigurationSettings.ConfigProps.DDB_USERS_TABLE)); } } /** * Marshal a Map<String, AttributeValue> object (representing an Item * retrieved from DynamoDB) to a User. * * @return */ public User getUserFromMap(Map<String, AttributeValue> userItem) { // Create a new user from the minimum required values. As the password // is stored // hashed, the last parameter is false to indicate. User user = new User(); user.setEmail(userItem.get(EMAIL_ATTR).getS()); user.setPassword(userItem.get(PASSWORD_ATTR).getS()); user.setId(userItem.get(EMAIL_ATTR).getS()); // Look for other optional attributes and set them for the object if // they exist. if (null != userItem.get(NICKNAME_ATTR)) user.setNickname(userItem.get(NICKNAME_ATTR).getS()); if (null != userItem.get(TAGLINE_ATTR)) user.setTagline(userItem.get(TAGLINE_ATTR).getS()); if (null != userItem.get(PROFILE_PIC_KEY_ATTR)) user.setProfilePicKey((userItem.get(PROFILE_PIC_KEY_ATTR).getS())); return user; } /** * Marshal a User object to a Map<String, AttributeValue> suitable for inserting * as an item into a DynamoDB table. * * @param user * @return */ public Map<String, AttributeValue> getMapFromUser(User user) { // Create a Map object from the User Map<String, AttributeValue> userItem = new HashMap<String, AttributeValue>(); // Add items to the Map userItem.put(EMAIL_ATTR, new AttributeValue(user.getEmail())); userItem.put(PASSWORD_ATTR, new AttributeValue(user.getPassword())); // Ensure User properties are neither null nor empty strings if(null != user.getNickname() && true != user.getNickname().isEmpty()) userItem.put(NICKNAME_ATTR, new AttributeValue(user.getNickname())); if(null != user.getTagline() && true != user.getTagline().isEmpty()) userItem.put(TAGLINE_ATTR, new AttributeValue(user.getTagline())); if(null != user.getProfilePicKey() && true != user.getProfilePicKey().isEmpty()) userItem.put(PROFILE_PIC_KEY_ATTR, new AttributeValue(user.getProfilePicKey())); return userItem; } /** * Upload the profile pic to S3 and return it's URL * @param profilePic * @return The fully-qualified URL of the photo in S3 * @throws IOException */ public String uploadFileToS3(CommonsMultipartFile profilePic) throws IOException { // Profile pic prefix String prefix = config.getProperty(ConfigProps.S3_PROFILE_PIC_PREFIX); // Date string String dateString = new SimpleDateFormat("ddMMyyyy").format(new java.util.Date()); String s3Key = prefix + "/" + dateString + "/" + UUID.randomUUID().toString() + "_" + profilePic.getOriginalFilename(); // Get bucket String s3bucket = config.getProperty(ConfigProps.S3_UPLOAD_BUCKET); // Create a ObjectMetadata instance to set the ACL, content type and length ObjectMetadata metadata = new ObjectMetadata(); metadata.setContentType(profilePic.getContentType()); metadata.setContentLength(profilePic.getSize()); // Create a PutRequest to upload the image PutObjectRequest putObject = new PutObjectRequest(s3bucket, s3Key, profilePic.getInputStream(), metadata); // Put the image into S3 s3Client.putObject(putObject); s3Client.setObjectAcl(s3bucket, s3Key, CannedAccessControlList.PublicRead); return "http://" + s3bucket + ".s3.amazonaws.com/" + s3Key; } }