class B2WriteFeature extends AbstractHttpWriteFeature<BaseB2Response> implements Write<BaseB2Response> {

    @Override
    public HttpResponseOutputStream<BaseB2Response> write(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException {
        // Submit store call to background thread
        final DelayedHttpEntityCallable<BaseB2Response> command = new DelayedHttpEntityCallable<BaseB2Response>() {
            /**
             * @return The SHA-1 returned by the server for the uploaded object
             */
            @Override
            public BaseB2Response call(final AbstractHttpEntity entity) throws BackgroundException {
                try {
                    final Checksum checksum = status.getChecksum();
                    if(status.isSegment()) {
                        final B2GetUploadPartUrlResponse uploadUrl
                            = session.getClient().getUploadPartUrl(status.getVersion().id);
                        return session.getClient().uploadLargeFilePart(uploadUrl, status.getPart(), entity, checksum.hash);
                    }
                    else {
                        if(null == urls.get()) {
                            final B2GetUploadUrlResponse uploadUrl = session.getClient().getUploadUrl(fileid.getFileid(containerService.getContainer(file), new DisabledListProgressListener()));
                            if(log.isDebugEnabled()) {
                                log.debug(String.format("Obtained upload URL %s for file %s", uploadUrl, file));
                            }
                            urls.set(uploadUrl);
                            return this.upload(uploadUrl, entity, checksum);
                        }
                        else {
                            final B2GetUploadUrlResponse uploadUrl = urls.get();
                            if(log.isDebugEnabled()) {
                                log.debug(String.format("Use cached upload URL %s for file %s", uploadUrl, file));
                            }
                            try {
                                return this.upload(uploadUrl, entity, checksum);
                            }
                            catch(IOException | B2ApiException e) {
                                // Upload many files to the same upload_url until that URL gives an error
                                log.warn(String.format("Remove cached upload URL after failure %s", e));
                                urls.remove();
                                // Retry
                                return this.upload(uploadUrl, entity, checksum);
                            }
                        }
                    }
                }
                catch(B2ApiException e) {
                    throw new B2ExceptionMappingService().map("Upload {0} failed", e, file);
                }
                catch(IOException e) {
                    throw new DefaultIOExceptionMappingService().map("Upload {0} failed", e, file);
                }
            }

            protected BaseB2Response upload(final B2GetUploadUrlResponse uploadUrl, final AbstractHttpEntity entity, final Checksum checksum) throws B2ApiException, IOException, BackgroundException {
                final Map<String, String> fileinfo = new HashMap<>(status.getMetadata());
                if(null != status.getTimestamp()) {
                    fileinfo.put(X_BZ_INFO_SRC_LAST_MODIFIED_MILLIS, String.valueOf(status.getTimestamp()));
                }
                return session.getClient().uploadFile(uploadUrl,
                    containerService.getKey(file),
                    entity, checksum.algorithm == HashAlgorithm.sha1 ? checksum.hash : "do_not_verify",
                    status.getMime(),
                    fileinfo);
            }

            @Override
            public long getContentLength() {
                return status.getLength();
            }
        };
        return this.write(file, status, command);
    }

    public  B2WriteFeature(final B2Session session, final B2FileidProvider fileid);
    public  B2WriteFeature(final B2Session session, final B2FileidProvider fileid, final Find finder, final AttributesFinder attributes);

}