@Override
    public void revert(final Path file) throws BackgroundException {
        if(file.isFile()) {
            try {
                final S3Object destination = new S3Object(containerService.getKey(file));
                // Keep same storage class
                destination.setStorageClass(file.attributes().getStorageClass());
                final Encryption.Algorithm encryption = file.attributes().getEncryption();
                destination.setServerSideEncryptionAlgorithm(encryption.algorithm);
                // Set custom key id stored in KMS
                destination.setServerSideEncryptionKmsKeyId(encryption.key);
                try {
                    // Apply non standard ACL
                    final Acl list = acl.getPermission(file);
                    if(list.isEditable()) {
                        destination.setAcl(acl.toAcl(list));
                    }
                }
                catch(AccessDeniedException | InteroperabilityException e) {
                    log.warn(String.format("Ignore failure %s", e));
                }
                final Path bucket = containerService.getContainer(file);
                final String bucketname = bucket.isRoot() ? RequestEntityRestStorageService.findBucketInHostname(session.getHost()) : bucket.getName();
                session.getClient().copyVersionedObject(file.attributes().getVersionId(),
                        bucketname, containerService.getKey(file), bucketname, destination, false);
                if(file.getParent().attributes().getCustom().containsKey(S3VersionedObjectListService.KEY_DELETE_MARKER)) {
                    // revert placeholder
                    session.getClient().deleteVersionedObject(
                            file.getParent().attributes().getVersionId(),
                            bucketname, containerService.getKey(file.getParent()));
                }
            }
            catch(ServiceException e) {
                throw new S3ExceptionMappingService().map("Cannot revert file", e, file);
            }
        }
    }
@Test
    public void testRevert() throws Exception {
        final Path bucket = new Path("versioning-test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
        final S3AccessControlListFeature acl = new S3AccessControlListFeature(session);
        final Path directory = new S3DirectoryFeature(session, new S3WriteFeature(session, acl), acl).mkdir(new Path(
                bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
        final Path test = new S3TouchFeature(session, acl).touch(new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
        final Path ignored = new S3TouchFeature(session, acl).touch(new Path(directory, String.format("%s-2", test.getName()), EnumSet.of(Path.Type.file)), new TransferStatus());
        {
            // Make sure there is another versioned copy of a file not to be included when listing
            final byte[] content = RandomUtils.nextBytes(245);
            final TransferStatus status = new TransferStatus().withLength(content.length);
            final S3MultipartWriteFeature writer = new S3MultipartWriteFeature(session, acl);
            final HttpResponseOutputStream<StorageObject> out = writer.write(ignored, status, new DisabledConnectionCallback());
            new StreamCopier(status, status).transfer(new ByteArrayInputStream(content), out);
        }
        final PathAttributes initialAttributes = new PathAttributes(test.attributes());
        final String initialVersion = test.attributes().getVersionId();
        final byte[] content = RandomUtils.nextBytes(32769);
        final TransferStatus status = new TransferStatus();
        status.setLength(content.length);
        final S3MultipartWriteFeature writer = new S3MultipartWriteFeature(session, acl);
        final HttpResponseOutputStream<StorageObject> out = writer.write(test, status, new DisabledConnectionCallback());
        assertNotNull(out);
        new StreamCopier(status, status).transfer(new ByteArrayInputStream(content), out);
        final PathAttributes updated = new S3AttributesFinderFeature(session, acl).find(new Path(test).withAttributes(PathAttributes.EMPTY));
        assertNotEquals(initialVersion, updated.getVersionId());
        final S3VersioningFeature feature = new S3VersioningFeature(session, acl);
        {
            final AttributedList<Path> versions = feature.list(new Path(test).withAttributes(status.getResponse()), new DisabledListProgressListener());
            assertFalse(versions.isEmpty());
            assertEquals(1, versions.size());
            assertEquals(new Path(test).withAttributes(initialAttributes), versions.get(0));
            assertTrue(new S3FindFeature(session, acl).find(versions.get(0)));
            assertEquals(initialVersion, new S3AttributesFinderFeature(session, acl).find(versions.get(0)).getVersionId());
        }
        feature.revert(new Path(test).withAttributes(initialAttributes));
        final PathAttributes reverted = new S3AttributesFinderFeature(session, acl).find(new Path(test).withAttributes(PathAttributes.EMPTY));
        assertNotEquals(initialVersion, reverted.getVersionId());
        assertEquals(test.attributes().getSize(), reverted.getSize());
        {
            final AttributedList<Path> versions = feature.list(test, new DisabledListProgressListener());
            assertFalse(versions.isEmpty());
            assertEquals(2, versions.size());
            assertEquals(content.length, versions.get(0).attributes().getSize());
            assertEquals(updated.getVersionId(), versions.get(0).attributes().getVersionId());
            assertEquals(initialVersion, versions.get(1).attributes().getVersionId());
        }
        new S3DefaultDeleteFeature(session).delete(Arrays.asList(directory, test, ignored), new DisabledLoginCallback(), new Delete.DisabledCallback());
    }