org.jenkinsci.plugins.relution_publisher.net.requests.ZeroCopyFileRequestProducer.java Source code

Java tutorial

Introduction

Here is the source code for org.jenkinsci.plugins.relution_publisher.net.requests.ZeroCopyFileRequestProducer.java

Source

/*
 * Copyright (c) 2013-2015 M-Way Solutions GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jenkinsci.plugins.relution_publisher.net.requests;

import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.nio.ContentEncoder;
import org.apache.http.nio.ContentEncoderChannel;
import org.apache.http.nio.FileContentEncoder;
import org.apache.http.nio.IOControl;
import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
import org.apache.http.protocol.HttpContext;
import org.apache.tika.Tika;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.UUID;

public class ZeroCopyFileRequestProducer implements HttpAsyncRequestProducer {

    private final static String CHARSET_NAME = "UTF-8";
    private final static Charset CHARSET = Charset.forName(CHARSET_NAME);

    private final static String CONTENT_TYPE_MULTIPART_FORM_DATA = "multipart/form-data; boundary=%s";
    private final static String CRLF = "\r\n";

    private final String mMultipartBoundary = UUID.randomUUID().toString();

    private byte[] mMultipartHeader;
    private int mMultipartHeaderIndex;

    private byte[] mMultipartFooter;
    private int mMultipartFooterIndex;

    private final ZeroCopyFileRequest mRequest;

    private final File mFile;
    private final RandomAccessFile mAccessfile;

    private FileChannel mFileChannel;
    private long mIndexFile = -1;

    public ZeroCopyFileRequestProducer(final ZeroCopyFileRequest request) throws FileNotFoundException {

        this.mRequest = request;

        this.mFile = request.getFile();
        this.mAccessfile = new RandomAccessFile(this.mFile, "r");
    }

    private void closeChannel() throws IOException {
        if (this.mFileChannel != null) {
            this.mFileChannel.close();
            this.mFileChannel = null;
        }
    }

    private String getContentType(final File file) {

        try {
            final Tika tika = new Tika();
            return tika.detect(file);

        } catch (final IOException e) {
            return ContentType.DEFAULT_BINARY.toString();
        }
    }

    private byte[] getHeader() {

        if (this.mMultipartHeader == null) {
            final StringBuilder sb = new StringBuilder();
            this.writeln(sb, "--%s", this.mMultipartBoundary);
            this.writeln(sb, "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"", "file",
                    this.mFile.getName());

            final String contentType = this.getContentType(this.mFile);
            this.writeln(sb, "Content-Type: %s", contentType);

            this.writeln(sb, "Content-Transfer-Encoding: binary");
            this.writeln(sb);

            final String value = sb.toString();
            this.mMultipartHeader = value.getBytes(CHARSET);
        }
        return this.mMultipartHeader;
    }

    private byte[] getFooter() {

        if (this.mMultipartFooter == null) {
            final StringBuilder sb = new StringBuilder();

            this.writeln(sb);
            this.writeln(sb, "--%s--", this.mMultipartBoundary);

            final String value = sb.toString();
            this.mMultipartFooter = value.getBytes(CHARSET);
        }
        return this.mMultipartFooter;
    }

    private boolean writeHeader(final ContentEncoder encoder, final IOControl ioctrl) throws IOException {

        final byte[] array = this.getHeader();

        if (this.mMultipartHeaderIndex >= array.length) {
            return true;
        }

        final int length = array.length - this.mMultipartHeaderIndex;
        final ByteBuffer buffer = ByteBuffer.wrap(array, this.mMultipartHeaderIndex, length);
        this.mMultipartHeaderIndex += encoder.write(buffer);

        return false;
    }

    private boolean writeFooter(final ContentEncoder encoder, final IOControl ioctrl) throws IOException {

        final byte[] array = this.getFooter();

        if (this.mMultipartFooterIndex >= array.length) {
            return true;
        }

        final int length = array.length - this.mMultipartFooterIndex;
        final ByteBuffer buffer = ByteBuffer.wrap(array, this.mMultipartFooterIndex, length);
        this.mMultipartFooterIndex += encoder.write(buffer);

        return false;
    }

    private void writeln(final StringBuilder sb, final String value, final Object... args) {

        final String data = String.format(value, args);

        sb.append(data);
        sb.append(CRLF);
    }

    private void writeln(final StringBuilder sb) {
        sb.append(CRLF);
    }

    protected HttpEntityEnclosingRequest createRequest(final String uri, final HttpEntity entity) {

        final HttpPost request = new HttpPost(uri);
        request.setEntity(entity);

        this.mRequest.addHeaders(request);
        return request;
    }

    public String getContentType() {
        return String.format(CONTENT_TYPE_MULTIPART_FORM_DATA, this.mMultipartBoundary);
    }

    public long getContentLength() {
        final byte[] header = this.getHeader();
        final byte[] footer = this.getFooter();

        return header.length + this.mFile.length() + footer.length;
    }

    @Override
    public HttpRequest generateRequest() throws IOException, HttpException {
        final BasicHttpEntity entity = new BasicHttpEntity();

        entity.setContentLength(this.getContentLength());
        entity.setContentType(this.getContentType());
        entity.setChunked(false);

        return this.createRequest(this.mRequest.getUri(), entity);
    }

    @Override
    public synchronized void produceContent(final ContentEncoder encoder, final IOControl ioctrl)
            throws IOException {

        if (!this.writeHeader(encoder, ioctrl)) {
            return;
        }

        if (this.mFileChannel == null) {
            this.mFileChannel = this.mAccessfile.getChannel();
            this.mIndexFile = 0;
        }

        final long transferred;

        if (encoder instanceof FileContentEncoder) {
            transferred = ((FileContentEncoder) encoder).transfer(this.mFileChannel, this.mIndexFile,
                    Integer.MAX_VALUE);

        } else {
            transferred = this.mFileChannel.transferTo(this.mIndexFile, Integer.MAX_VALUE,
                    new ContentEncoderChannel(encoder));

        }

        if (transferred > 0) {
            this.mIndexFile += transferred;
        }

        if (this.mIndexFile >= this.mFileChannel.size()) {
            if (this.writeFooter(encoder, ioctrl)) {
                this.closeChannel();
                encoder.complete();
            }
        }
    }

    @Override
    public void requestCompleted(final HttpContext context) {
    }

    @Override
    public void failed(final Exception ex) {
    }

    @Override
    public boolean isRepeatable() {
        return true;
    }

    @Override
    public synchronized void resetRequest() throws IOException {
        this.mMultipartHeaderIndex = 0;
        this.mMultipartFooterIndex = 0;
        this.closeChannel();
    }

    @Override
    public HttpHost getTarget() {
        final URI uri = URI.create(this.mRequest.getUri());
        return URIUtils.extractHost(uri);
    }

    @Override
    public synchronized void close() throws IOException {

        try {
            this.mAccessfile.close();
        } catch (final IOException e) {
            // do nothing
        }
    }
}