All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fasterxml.clustermate.client.ahc.UncompressingAsyncHandler Maven / Gradle / Ivy

Go to download

Almost complete ClusterMate NetworkClient implementation built on Async HTTP Client

The newest version!
package com.fasterxml.clustermate.client.ahc;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;

import com.ning.compress.DataHandler;
import com.ning.compress.Uncompressor;
import com.ning.compress.UncompressorOutputStream;
import com.ning.compress.gzip.GZIPUncompressor;
import com.ning.compress.lzf.LZFUncompressor;

import com.ning.http.client.AsyncHandler;
import com.ning.http.client.FluentCaseInsensitiveStringsMap;
import com.ning.http.client.HttpResponseBodyPart;
import com.ning.http.client.HttpResponseHeaders;
import com.ning.http.client.HttpResponseStatus;

import com.fasterxml.clustermate.api.ClusterMateConstants;
import com.fasterxml.clustermate.client.call.GetContentProcessor;
import com.fasterxml.storemate.shared.compress.Compression;
import com.fasterxml.storemate.shared.util.ByteAggregator;
import com.fasterxml.storemate.shared.util.IOUtil;

/**
 * Translator class that implements {@link AsyncHandler} but
 * calls {@link GetContentProcessor} with contents.
 *
 * @param  Result type of actual GET content handler we are constructed with
 */
public class UncompressingAsyncHandler
    implements AsyncHandler, DataHandler
{
    /**
     * Maximum number of bytes we will read to get an excerpt for failed requests.
     */
    protected final static int MAX_EXCERPT_LENGTH = 1000;
    
    /**
     * Handler to delegate content calls to.
     */
    protected final GetContentProcessor.Handler _handler;

    /**
     * Stream wrapper used for passing content; wraps either uncompressor
     * or handler.
     */
    protected OutputStream _streamAdapter;

    /**
     * HTTP status code received, if any
     */
    protected int _status = 0;

    /**
     * Flag to indicate a failed call; response content of such calls is only
     * partially collector (for error excerpt), and no result Object will
     * be created.
     */
    protected boolean _failed;
    
    /**
     * Response headers received, if any
     */
    protected FluentCaseInsensitiveStringsMap _headers;

    /**
     * Partial aggregated content read from response sent by failed requests.
     */
    protected ByteAggregator _failExcerpt;
    
    protected String _failExcerptString;
    
    /*
    /**********************************************************************
    /* Construction
    /**********************************************************************
     */
    
    public UncompressingAsyncHandler(GetContentProcessor proc) {
        _handler = proc.createHandler();
    }

    public UncompressingAsyncHandler(GetContentProcessor.Handler h) {
        _handler = h;
    }

    /*
    /**********************************************************************
    /* Public accessors
    /**********************************************************************
     */

    public boolean isFailed() { return _failed; }
    
    public int getStatus() { return _status; }

    public FluentCaseInsensitiveStringsMap getHeaders() {
        return _headers;
    }

    public String getExcerpt()
    {
        String msg = _failExcerptString;
        if (msg == null) {
            if (_failExcerpt == null) {
                msg = "[no excerpt available]";
            } else {
                try {
                    msg = new String(_failExcerpt.toByteArray(), "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException("Internal error: could not do UTF-8 decoding: "+e, e);
                }
            }
            _failExcerptString = msg;
        }
        return msg;
    }
    
    /*
    /**********************************************************************
    /* AsyncHandler for AHC
    /**********************************************************************
     */
    
    @Override
    public com.ning.http.client.AsyncHandler.STATE onBodyPartReceived(HttpResponseBodyPart part)
            throws Exception
    {
        // One complication: for failed calls, we take (partial) excerpt
        if (_failExcerpt != null) {
            byte[] data = part.getBodyPartBytes();
            int needed = MAX_EXCERPT_LENGTH - _failExcerpt.size();
            if (needed > 0) {
                _failExcerpt.write(data, 0, Math.min(needed, data.length));
            }
            
            // and abort request as soon as we got as much as we needed
            if (_failExcerpt.size() < MAX_EXCERPT_LENGTH) {
                return STATE.CONTINUE;
            }
            return STATE.ABORT;
        }
        part.writeTo(_streamAdapter);
        return STATE.CONTINUE;
    }

    @Override
    public T onCompleted() throws Exception {
        return _handler.completeContentProcessing();
    }

    @Override
    public com.ning.http.client.AsyncHandler.STATE onHeadersReceived(HttpResponseHeaders h)
        throws Exception
    {
        FluentCaseInsensitiveStringsMap headers = (h == null) ? null : h.getHeaders();
        _headers = headers;
        String comps = (headers == null) ? null : headers.getFirstValue(ClusterMateConstants.HTTP_HEADER_COMPRESSION);
        if (comps != null && !comps.isEmpty()) {
            Uncompressor uncomp = null;
            Compression c = Compression.from(comps);
            if (c == Compression.LZF) {
                uncomp = new LZFUncompressor(this);
            } else if (c == Compression.GZIP) {
                uncomp = new GZIPUncompressor(this);
            } else if (c == Compression.NONE) {
                ; // not compressed, fine as is
            } else {
                throw new IOException("Unrecognized/unsupported compression type '"+comps+"': only 'gzip' and 'lzf' supported");
            }
            // adapter wraps uncompressor as OutputStream; uncompressor calls "handleData"
            // on this handler... a few layers of abstraction.
            _streamAdapter = new UncompressorOutputStream(uncomp);
        } else {
            // if no uncompression needed, still need to wrap async handler as an OutputStream
            // to efficiently get contents from BodyPart
            _streamAdapter = _handler.asStream();
        }
        // !!! TODO: if we are not uncompress, indicate remaining compression here
        if (!_handler.startContent(_status, null)) {
            return STATE.ABORT;
        }
        return STATE.CONTINUE;
    }

    @Override
    public com.ning.http.client.AsyncHandler.STATE onStatusReceived(HttpResponseStatus status)
        throws Exception
    {
        _status = status.getStatusCode();
        boolean fail = !IOUtil.isHTTPSuccess(_status);
        _failed = fail;
        if (fail) {
            _failExcerpt = new ByteAggregator(MAX_EXCERPT_LENGTH);
        }
        return STATE.CONTINUE;
    }

    @Override
    public void onThrowable(Throwable t) {
        _handler.contentProcessingFailed(t);
    }

    /*
    /**********************************************************************
    /* DataHandler (for Uncompressor)
    /**********************************************************************
     */
    
    /**
     * Method called by uncompressor; needs to dispatch content call appropriately.
     */
    @Override
    public boolean handleData(byte[] buffer, int offset, int len) throws IOException {
        return _handler.processContent(buffer, offset, len);
    }

    @Override
    public void allDataHandled() throws IOException {
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy