@Override
    public Checksum upload(final Path file, final Local local, final BandwidthThrottle throttle,
                           final StreamListener listener, final TransferStatus status,
                           final ConnectionCallback callback) throws BackgroundException {
        try {
            final IRODSFileSystemAO fs = session.getClient();
            final IRODSFile f = fs.getIRODSFileFactory().instanceIRODSFile(file.getAbsolute());
            final TransferControlBlock block = DefaultTransferControlBlock.instance(StringUtils.EMPTY,
                new HostPreferences(session.getHost()).getInteger("connection.retry"));
            final TransferOptions options = new DefaultTransferOptionsConfigurer().configure(new TransferOptions());
            if(Host.TransferType.unknown.equals(session.getHost().getTransferType())) {
                options.setUseParallelTransfer(Host.TransferType.valueOf(PreferencesFactory.get().getProperty("queue.transfer.type")).equals(Host.TransferType.concurrent));
            }
            else {
                options.setUseParallelTransfer(session.getHost().getTransferType().equals(Host.TransferType.concurrent));
            }
            block.setTransferOptions(options);
            final DataTransferOperations transfer = fs.getIRODSAccessObjectFactory().getDataTransferOperations(fs.getIRODSAccount());
            transfer.putOperation(new File(local.getAbsolute()), f, new DefaultTransferStatusCallbackListener(
                status, listener, block
            ), block);
            if(status.isComplete()) {
                final DataObjectChecksumUtilitiesAO checksum = fs
                    .getIRODSAccessObjectFactory()
                    .getDataObjectChecksumUtilitiesAO(fs.getIRODSAccount());
                final ChecksumValue value = checksum.computeChecksumOnDataObject(f);
                final Checksum fingerprint = Checksum.parse(value.getChecksumStringValue());
                if(null == fingerprint) {
                    log.warn(String.format("Unsupported checksum algorithm %s", value.getChecksumEncoding()));
                }
                else {
                    if(file.getType().contains(Path.Type.encrypted)) {
                        log.warn(String.format("Skip checksum verification for %s with client side encryption enabled", file));
                    }
                    else {
                        final Checksum expected = ChecksumComputeFactory.get(fingerprint.algorithm).compute(local.getInputStream(), new TransferStatus(status));
                        if(!expected.equals(fingerprint)) {
                            throw new ChecksumException(MessageFormat.format(LocaleFactory.localizedString("Upload {0} failed", "Error"), file.getName()),
                                MessageFormat.format("Mismatch between {0} hash {1} of uploaded data and ETag {2} returned by the server",
                                    fingerprint.algorithm.toString(), expected, fingerprint.hash));
                        }
                    }
                }
                return fingerprint;
            }
            return null;
        }
        catch(JargonException e) {
            throw new IRODSExceptionMappingService().map(e);
        }
    }
@Test
    public void testWrite() throws Exception {
        final ProtocolFactory factory = new ProtocolFactory(new HashSet<>(Collections.singleton(new IRODSProtocol())));
        final Profile profile = new ProfilePlistReader(factory).read(
                this.getClass().getResourceAsStream("/iRODS (iPlant Collaborative).cyberduckprofile"));
        final Host host = new Host(profile, profile.getDefaultHostname(), new Credentials(
                PROPERTIES.get("irods.key"), PROPERTIES.get("irods.secret")
        ));

        final IRODSSession session = new IRODSSession(host);
        session.open(new DisabledProxyFinder(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback());
        session.login(new DisabledLoginCallback(), new DisabledCancelCallback());
        final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
        final int length = 32770;
        final byte[] content = RandomUtils.nextBytes(length);
        final OutputStream out = local.getOutputStream(false);
        IOUtils.write(content, out);
        out.close();
        final Checksum checksum;
        final Path test = new Path(new IRODSHomeFinderService(session).find(), UUID.randomUUID().toString(), EnumSet.of(Path.Type.file));
        final TransferStatus status = new TransferStatus().withLength(content.length);
        final TransferStatus copy = new TransferStatus(status);
        final BytecountStreamListener count = new BytecountStreamListener();
        checksum = new IRODSUploadFeature(session).upload(
                test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), count, status, new DisabledConnectionCallback());
        assertTrue(status.isComplete());
        assertEquals(content.length, count.getSent());
        assertEquals(checksum, new MD5ChecksumCompute().compute(new FileInputStream(local.getAbsolute()), copy));
        final byte[] buffer = new byte[content.length];
        final InputStream in = new IRODSReadFeature(session).read(test, new TransferStatus().withLength(content.length), new DisabledConnectionCallback());
        IOUtils.readFully(in, buffer);
        in.close();
        assertArrayEquals(content, buffer);
        new IRODSDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
        session.close();
    }