@Override
    public Set<UUID> pre(final Transfer.Type type, final Map<TransferItem, TransferStatus> files, final ConnectionCallback callback) throws BackgroundException {
        final Ds3Client client = new SpectraClientBuilder().wrap(session.getClient(), session.getHost());
        final Map<Path, List<Ds3Object>> objects = new HashMap<Path, List<Ds3Object>>();
        for(Map.Entry<TransferItem, TransferStatus> item : files.entrySet()) {
            final Path file = item.getKey().remote;
            final Path container = containerService.getContainer(file);
            if(!objects.containsKey(container)) {
                objects.put(container, new ArrayList<Ds3Object>());
            }
            if(file.isFile()) {
                final TransferStatus status = item.getValue();
                switch(type) {
                    case upload:
                        if(status.isExists()) {
                            log.warn(String.format("Delete existing file %s", file));
                            delete.delete(Collections.singletonMap(file, status), callback, new Delete.DisabledCallback());
                        }
                        break;
                }
                final Ds3Object o = new Ds3Object(containerService.getKey(file), status.getLength());
                o.setVersionId(file.attributes().getVersionId());
                objects.get(container).add(o);
            }
            if(file.isDirectory()) {
                switch(type) {
                    case upload:
                        objects.get(container).add(new Ds3Object(containerService.getKey(file), 0L));
                        // Do not include folders when creating a GET job
                        break;
                }
            }
        }
        try {
            final Set<UUID> jobs = new HashSet<UUID>();
            for(Map.Entry<Path, List<Ds3Object>> container : objects.entrySet()) {
                if(container.getValue().isEmpty()) {
                    continue;
                }
                final MasterObjectList master;
                switch(type) {
                    case download:
                        final GetBulkJobSpectraS3Response getResponse = client.getBulkJobSpectraS3(new GetBulkJobSpectraS3Request(container.getKey().getName(), container.getValue()));
                        master = getResponse.getMasterObjectList();
                        break;
                    case upload:
                        final PutBulkJobSpectraS3Response putResponse = client.putBulkJobSpectraS3(new PutBulkJobSpectraS3Request(container.getKey().getName(), container.getValue()).withMaxUploadSize(Long.MAX_VALUE));
                        master = putResponse.getMasterObjectList();
                        break;
                    default:
                        throw new NotfoundException(String.format("Unsupported transfer type %s", type));
                }
                jobs.add(master.getJobId());
                final Map<String, Integer> counters = this.getNumberOfObjects(master);
                for(Map.Entry<TransferItem, TransferStatus> item : files.entrySet()) {
                    if(container.getKey().equals(containerService.getContainer(item.getKey().remote))) {
                        final TransferStatus status = item.getValue();
                        final Map<String, String> parameters = new HashMap<>(status.getParameters());
                        parameters.put(REQUEST_PARAMETER_JOBID_IDENTIFIER, master.getJobId().toString());
                        status.withParameters(parameters);
                        status.setPart(counters.get(containerService.getKey(item.getKey().remote)));
                    }
                }
            }
            return jobs;
        }
        catch(XmlProcessingException e) {
            throw new DefaultExceptionMappingService().map(e);
        }
        catch(FailedRequestException e) {
            throw new SpectraExceptionMappingService().map(e);
        }
        catch(IOException e) {
            throw new DefaultIOExceptionMappingService().map(e);
        }
    }
@Test(expected = AccessDeniedException.class)
    public void testPreDownloadNotFound() throws Exception {
        new SpectraBulkService(session).pre(Transfer.Type.download, Collections.singletonMap(
            new TransferItem(new Path(String.format("/cyberduck/%s", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file))), new TransferStatus().withLength(1L)
        ), new DisabledConnectionCallback());
    }