cloudstack/awsapi/src/com/cloud/bridge/persist/dao/MultipartLoadDao.java
Darren Shepherd f62e28c1ec New Transaction API
Introduction of a new Transaction API that is more consistent with the style
of Spring's transaction managment.  The existing Transaction class was renamed
to TransactionLegacy.  All of the non-DAO code in the management server has been
updated to use the new Transaction API.
2013-10-16 09:21:00 -07:00

358 lines
13 KiB
Java

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.cloud.bridge.persist.dao;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import javax.inject.Inject;
import org.apache.log4j.Logger;
import com.cloud.bridge.model.MultiPartPartsVO;
import com.cloud.bridge.model.MultiPartUploadsVO;
import com.cloud.bridge.model.MultipartMetaVO;
import com.cloud.bridge.service.core.s3.S3MetaDataEntry;
import com.cloud.bridge.service.core.s3.S3MultipartPart;
import com.cloud.bridge.service.core.s3.S3MultipartUpload;
import com.cloud.bridge.util.OrderedPair;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionLegacy;
public class MultipartLoadDao {
public static final Logger logger = Logger.getLogger(MultipartLoadDao.class);
@Inject MultipartMetaDao mpartMetaDao;
@Inject MultiPartPartsDao mpartPartsDao;
@Inject MultiPartUploadsDao mpartUploadDao;
public MultipartLoadDao() {}
/**
* If a multipart upload exists with the uploadId value then return the non-null creators
* accessKey.
*
* @param uploadId
* @return creator of the multipart upload, and NameKey of upload
*/
public OrderedPair<String,String> multipartExits( int uploadId )
throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException
{
return mpartUploadDao.multipartExits(uploadId);
}
/**
* The multipart upload was either successfully completed or was aborted. In either case, we need
* to remove all of its state from the tables. Note that we have cascade deletes so all tables with
* uploadId as a foreign key are automatically cleaned.
*
* @param uploadId
*
*/
public void deleteUpload( int uploadId ) {
mpartUploadDao.deleteUpload(uploadId);
}
/**
* The caller needs to know who initiated the multipart upload.
*
* @param uploadId
* @return the access key value defining the initiator
*/
public String getInitiator( int uploadId ) {
return mpartUploadDao.getAtrributeValue("AccessKey", uploadId);
}
/**
* Create a new "in-process" multipart upload entry to keep track of its state.
*
* @param accessKey
* @param bucketName
* @param key
* @param cannedAccess
*
* @return if positive its the uploadId to be returned to the client
*
*/
public int initiateUpload( String accessKey, String bucketName, String key, String cannedAccess, S3MetaDataEntry[] meta ) {
int uploadId = -1;
TransactionLegacy txn = null;
try {
txn = TransactionLegacy.open(TransactionLegacy.AWSAPI_DB);
Date tod = new Date();
MultiPartUploadsVO uploadVO = new MultiPartUploadsVO(accessKey,
bucketName, key, cannedAccess, tod);
uploadVO = mpartUploadDao.persist(uploadVO);
if (null != uploadVO) {
uploadId = uploadVO.getId().intValue();
if (null != meta) {
for (int i = 0; i < meta.length; i++) {
MultipartMetaVO mpartMeta = new MultipartMetaVO();
mpartMeta.setUploadID(uploadId);
S3MetaDataEntry entry = meta[i];
mpartMeta.setName(entry.getName());
mpartMeta.setValue(entry.getValue());
mpartMetaDao.persist(mpartMeta);
}
txn.commit();
}
}
return uploadId;
} finally {
txn.close();
}
}
/**
* Remember all the individual parts that make up the entire multipart upload so that once
* the upload is complete all the parts can be glued together into a single object. Note,
* the caller can over write an existing part.
*
* @param uploadId
* @param partNumber
* @param md5
* @param storedPath
* @param size
* @throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException
*/
public void savePart( int uploadId, int partNumber, String md5, String storedPath, int size ) {
try {
MultiPartPartsVO partVO = null;
partVO = mpartPartsDao.findByUploadID(uploadId, partNumber);
// -> are we doing an update or an insert? (are we over writting an
// existing entry?)
if (null == partVO) {
MultiPartPartsVO part = new MultiPartPartsVO(uploadId,
partNumber, md5, storedPath, size, new Date());
mpartPartsDao.persist(part);
} else {
partVO.setMd5(md5);
partVO.setStoredSize(new Long(size));
partVO.setCreateTime(new Date());
partVO.setUploadid(new Long(uploadId));
partVO.setPartNumber(partNumber);
mpartPartsDao.updateParts(partVO, uploadId, partNumber);
}
} finally {
}
}
/**
* It is possible for there to be a null canned access policy defined.
* @param uploadId
* @return the value defined in the x-amz-acl header or null
*/
public String getCannedAccess( int uploadId ) {
return mpartUploadDao.getAtrributeValue("x_amz_acl", uploadId);
}
/**
* When the multipart are being composed into one object we need any meta data to be saved with
* the new re-constituted object.
*
* @param uploadId
* @return an array of S3MetaDataEntry (will be null if no meta values exist)
* @throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException
*/
public S3MetaDataEntry[] getMeta( int uploadId )
throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException
{
List<S3MetaDataEntry> metaList = new ArrayList<S3MetaDataEntry>();
int count = 0;
List<MultipartMetaVO> metaVO;
try {
metaVO = mpartMetaDao.getByUploadID(uploadId);
for (MultipartMetaVO multipartMetaVO : metaVO) {
S3MetaDataEntry oneMeta = new S3MetaDataEntry();
oneMeta.setName( multipartMetaVO.getName());
oneMeta.setValue( multipartMetaVO.getValue());
metaList.add( oneMeta );
count++;
}
if ( 0 == count )
return null;
else return metaList.toArray(new S3MetaDataEntry[0]);
} finally {
}
}
/**
* The result has to be ordered by key and if there is more than one identical key then all the
* identical keys are ordered by create time.
*
* @param bucketName
* @param maxParts
* @param prefix - can be null
* @param keyMarker - can be null
* @param uploadIdMarker - can be null, should only be defined if keyMarker is not-null
* @return OrderedPair<S3MultipartUpload[], isTruncated>
* @throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException
*/
public OrderedPair<S3MultipartUpload[],Boolean> getInitiatedUploads( String bucketName, int maxParts, String prefix, String keyMarker, String uploadIdMarker )
throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException
{
S3MultipartUpload[] inProgress = new S3MultipartUpload[maxParts];
boolean isTruncated = false;
int i = 0;
int pos = 1;
List<MultiPartUploadsVO> uploadList;
// -> SQL like condition requires the '%' as a wildcard marker
if (null != prefix) prefix = prefix + "%";
try {
uploadList = mpartUploadDao.getInitiatedUploads(bucketName, maxParts, prefix, keyMarker, uploadIdMarker);
for (MultiPartUploadsVO uploadsVO : uploadList) {
Calendar tod = Calendar.getInstance();
tod.setTime(uploadsVO.getCreateTime());
inProgress[i] = new S3MultipartUpload();
inProgress[i].setId( uploadsVO.getId().intValue());
inProgress[i].setAccessKey(uploadsVO.getAccessKey());
inProgress[i].setLastModified( tod );
inProgress[i].setBucketName( bucketName );
inProgress[i].setKey(uploadsVO.getNameKey());
i++;
}
if (i < maxParts)
inProgress = (S3MultipartUpload[]) resizeArray(inProgress, i);
return new OrderedPair<S3MultipartUpload[], Boolean>(inProgress,
isTruncated);
}finally {
}
}
/**
* Return info on a range of upload parts that have already been stored in disk.
* Note that parts can be uploaded in any order yet we must returned an ordered list
* of parts thus we use the "ORDERED BY" clause to sort the list.
*
* @param uploadId
* @param maxParts
* @param startAt
* @return an array of S3MultipartPart objects
* @throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException
*/
public S3MultipartPart[] getParts( int uploadId, int maxParts, int startAt )
throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException
{
S3MultipartPart[] parts = new S3MultipartPart[maxParts];
int i = 0;
List<MultiPartPartsVO> partsVO;
try {
partsVO = mpartPartsDao.getParts(uploadId, startAt + maxParts + 1, startAt);
for (MultiPartPartsVO partVO : partsVO) {
Calendar tod = Calendar.getInstance();
tod.setTime(partVO.getCreateTime());
parts[i] = new S3MultipartPart();
parts[i].setPartNumber(partVO.getPartNumber());
parts[i].setEtag(partVO.getMd5());
parts[i].setLastModified(tod);
parts[i].setSize(partVO.getStoredSize().intValue());
parts[i].setPath(partVO.getStoredPath());
i++;
}
if (i < maxParts) parts = (S3MultipartPart[])resizeArray(parts,i);
return parts;
} finally {
}
}
/**
* How many parts exist after the endMarker part number?
*
* @param uploadId
* @param endMarker - can be used to see if getUploadedParts was truncated
* @return number of parts with partNumber greater than endMarker
* @throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException
*/
public int numParts( int uploadId, int endMarker ) {
return mpartPartsDao.getnumParts(uploadId, endMarker);
}
/**
* A multipart upload request can have zero to many meta data entries to be applied to the
* final object. We need to remember all of the objects meta data until the multipart is complete.
*
* @param uploadId - defines an in-process multipart upload
* @param meta - an array of meta data to be assocated with the uploadId value
*
*/
private void saveMultipartMeta( int uploadId, S3MetaDataEntry[] meta ) {
if (null == meta) return;
TransactionLegacy txn = null;
try {
txn = TransactionLegacy.open(TransactionLegacy.AWSAPI_DB);
for( int i=0; i < meta.length; i++ )
{
S3MetaDataEntry entry = meta[i];
MultipartMetaVO metaVO = new MultipartMetaVO();
metaVO.setUploadID(uploadId);
metaVO.setName(entry.getName());
metaVO.setValue(entry.getValue());
metaVO=mpartMetaDao.persist(metaVO);
}
txn.commit();
} finally {
txn.close();
}
}
/**
* Reallocates an array with a new size, and copies the contents
* of the old array to the new array.
*
* @param oldArray the old array, to be reallocated.
* @param newSize the new array size.
* @return A new array with the same contents.
*/
private static Object resizeArray(Object oldArray, int newSize)
{
int oldSize = java.lang.reflect.Array.getLength(oldArray);
Class elementType = oldArray.getClass().getComponentType();
Object newArray = java.lang.reflect.Array.newInstance(
elementType,newSize);
int preserveLength = Math.min(oldSize,newSize);
if (preserveLength > 0)
System.arraycopy (oldArray,0,newArray,0,preserveLength);
return newArray;
}
}