com.ibm.stocator.fs.swift.SwiftOutputStream.java Source code

Java tutorial

Introduction

Here is the source code for com.ibm.stocator.fs.swift.SwiftOutputStream.java

Source

/**
 * (C) Copyright IBM Corp. 2015, 2016
 *
 * 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 com.ibm.stocator.fs.swift;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.InputStreamEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ibm.stocator.fs.swift.auth.JossAccount;
import com.ibm.stocator.fs.swift.http.SwiftConnectionManager;

/**
 *
 * Wraps OutputStream
 * This class is not thread-safe
 *
 */
public class SwiftOutputStream extends OutputStream {
    /*
     * Logger
     */
    private static final Logger LOG = LoggerFactory.getLogger(SwiftOutputStream.class);
    /*
     * Output stream
     */
    private OutputStream mOutputStream;
    /*
     * Access url
     */
    private URL mUrl;

    /*
     * Client used
     */
    private HttpClient client;

    /*
     * Request
     */
    private HttpPut request;

    /*
     *  Executor service to handle threads
     */
    private ExecutorService execService;
    private Future<Void> futureTask;
    private long totalWritten;
    private JossAccount mAccount;

    /**
     * Default constructor
     *
     * @param account JOSS account object
     * @param url URL connection
     * @param contentType content type
     * @param metadata input metadata
     * @param connectionManager SwiftConnectionManager
     * @throws IOException if error
     */
    public SwiftOutputStream(JossAccount account, URL url, final String contentType, Map<String, String> metadata,
            SwiftConnectionManager connectionManager) throws IOException {
        mUrl = url;
        totalWritten = 0;
        mAccount = account;
        client = connectionManager.createHttpConnection();
        request = new HttpPut(mUrl.toString());
        request.addHeader("X-Auth-Token", account.getAuthToken());
        if (metadata != null && !metadata.isEmpty()) {
            for (Map.Entry<String, String> entry : metadata.entrySet()) {
                request.addHeader("X-Object-Meta-" + entry.getKey(), entry.getValue());
            }
        }

        PipedOutputStream out = new PipedOutputStream();
        final PipedInputStream in = new PipedInputStream();
        out.connect(in);
        execService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
        mOutputStream = out;
        Callable<Void> task = new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                InputStreamEntity entity = new InputStreamEntity(in, -1);
                entity.setChunked(true);
                entity.setContentType(contentType);
                request.setEntity(entity);

                LOG.debug("HTTP PUT request {}", mUrl.toString());
                HttpResponse response = client.execute(request);
                int responseCode = response.getStatusLine().getStatusCode();
                LOG.debug("HTTP PUT response {}. Response code {}", mUrl.toString(), responseCode);
                if (responseCode == 401) { // Unauthorized error
                    mAccount.authenticate();
                    request.removeHeaders("X-Auth-Token");
                    request.addHeader("X-Auth-Token", mAccount.getAuthToken());
                    LOG.warn("Token recreated for {}.  Retry request", mUrl.toString());
                    response = client.execute(request);
                    responseCode = response.getStatusLine().getStatusCode();
                }
                if (responseCode >= 400) { // Code may have changed from retrying
                    throw new IOException("HTTP Error: " + responseCode + " Reason: "
                            + response.getStatusLine().getReasonPhrase());
                }

                return null;
            }
        };
        futureTask = execService.submit(task);
    }

    @Override
    public void write(int b) throws IOException {
        if (LOG.isTraceEnabled()) {
            totalWritten = totalWritten + 1;
            LOG.trace("Write {} one byte. Total written {}", mUrl.toString(), totalWritten);
        }
        mOutputStream.write(b);
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        if (LOG.isTraceEnabled()) {
            totalWritten = totalWritten + len;
            LOG.trace("Write {} off {} len {}. Total {}", mUrl.toString(), off, len, totalWritten);
        }
        mOutputStream.write(b, off, len);
    }

    @Override
    public void write(byte[] b) throws IOException {
        if (LOG.isTraceEnabled()) {
            totalWritten = totalWritten + b.length;
            LOG.trace("Write {} len {}. Total {}", mUrl.toString(), b.length, totalWritten);
        }
        mOutputStream.write(b);
    }

    @Override
    public void close() throws IOException {
        LOG.debug("HTTP PUT close {}", mUrl.toString());
        flush();
        mOutputStream.close();
        try {
            futureTask.get();
            futureTask.cancel(true);
            execService.shutdown();
        } catch (Exception e) {
            execService.shutdown();
            throw new IOException("Unable to complete write.", e);
        }
    }

    @Override
    public void flush() throws IOException {
        LOG.trace("{} flush method", mUrl.toString());
        mOutputStream.flush();
    }
}