Source:
<code>protected MantaMultipartUploadPart uploadPart(final EncryptedMultipartUpload<WRAPPED_UPLOAD> upload, final int partNumber, final HttpEntity sourceEntity) throws IOException { Validate.notNull(upload, "Multipart upload object must not be null"); if (!upload.canUpload()) { String msg = "Multipart object is not in a state that it can be " + "used for uploading parts. For encrypted multipart " + "uploads, only multipart upload objects returned from " + "the initiateUpload() method can be used to upload parts."; throw new IllegalArgumentException(msg); } final EncryptionState encryptionState = upload.getEncryptionState(); final EncryptionContext encryptionContext = encryptionState.getEncryptionContext(); EncryptionStateSnapshot snapshot = null; encryptionState.getLock().lock(); try { validatePartNumber(partNumber); if (encryptionState.getLastPartNumber() != -1) { Validate.isTrue(encryptionState.getLastPartNumber() + 1 == partNumber, "Encrypted MPU parts must be serial and sequential"); } else { encryptionState.setMultipartStream(new MultipartOutputStream( encryptionContext.getCipherDetails().getBlockSizeInBytes())); encryptionState.setCipherStream(EncryptingEntityHelper.makeCipherOutputForStream( encryptionState.getMultipartStream(), encryptionContext)); } snapshot = RECORDER.record(encryptionState); final EncryptingPartEntity entity = new EncryptingPartEntity( encryptionState.getCipherStream(), encryptionState.getMultipartStream(), sourceEntity, new EncryptingPartEntity.LastPartCallback() { @Override public ByteArrayOutputStream call(final long uploadedBytes) throws IOException { if (uploadedBytes < wrapped.getMinimumPartSize()) { LOGGER.debug("Detected part as last part based on size", partNumber); return encryptionState.remainderAndLastPartAuth(); } else { return new ByteArrayOutputStream(); } } }); final MantaMultipartUploadPart part = wrapped.uploadPart(upload.getWrapped(), partNumber, entity); encryptionState.setLastPartNumber(partNumber); return part; } finally { <START> if (encryptionState.getLastPartNumber() != partNumber && snapshot != null) { <END> RECORDER.rewind(encryptionState, snapshot); } encryptionState.getLock().unlock(); } }</code><technical_language>Is rewind done for *any* error wrapped.uploadPart? Previously part ordering validation effectively consumers shooting in foot retrying parts. figure out of this opes new error paths</technical_language>

Target:
protected MantaMultipartUploadPart uploadPart(final EncryptedMultipartUpload<WRAPPED_UPLOAD> upload, final int partNumber, final HttpEntity sourceEntity) throws IOException { Validate.notNull(upload, "Multipart upload object must not be null"); if (!upload.canUpload()) { String msg = "Multipart object is not in a state that it can be " + "used for uploading parts. For encrypted multipart " + "uploads, only multipart upload objects returned from " + "the initiateUpload() method can be used to upload parts."; throw new IllegalArgumentException(msg); } final EncryptionState encryptionState = upload.getEncryptionState(); final EncryptionContext encryptionContext = encryptionState.getEncryptionContext(); encryptionState.getLock().lock(); try { validatePartNumber(partNumber); if (encryptionState.getLastPartNumber() != -1) { Validate.isTrue(encryptionState.getLastPartNumber() + 1 == partNumber, "Encrypted MPU parts must be serial and sequential"); } else { encryptionState.setMultipartStream(new MultipartOutputStream( encryptionContext.getCipherDetails().getBlockSizeInBytes())); encryptionState.setCipherStream(EncryptingEntityHelper.makeCipherOutputForStream( encryptionState.getMultipartStream(), encryptionContext)); } final EncryptionStateSnapshot snapshot = EncryptionStateRecorder.record(encryptionState, upload.getId()); final EncryptingPartEntity entity = new EncryptingPartEntity( encryptionState.getCipherStream(), encryptionState.getMultipartStream(), sourceEntity, new EncryptingPartEntity.LastPartCallback() { @Override public ByteArrayOutputStream call(final long uploadedBytes) throws IOException { if (uploadedBytes < wrapped.getMinimumPartSize()) { LOGGER.debug("Detected part as last part based on size", partNumber); return encryptionState.remainderAndLastPartAuth(); } else { return new ByteArrayOutputStream(); } } }); try { final MantaMultipartUploadPart part = wrapped.uploadPart(upload.getWrapped(), partNumber, entity); encryptionState.setLastPartNumber(partNumber); return part; } catch (Exception e) { if (encryptionState.getLastPartNumber() != partNumber) { EncryptionStateRecorder.rewind(encryptionState, snapshot); } throw e; } } finally { encryptionState.getLock().unlock(); } }