io.jsync.net.impl.ConnectionBase.java Source code

Java tutorial

Introduction

Here is the source code for io.jsync.net.impl.ConnectionBase.java

Source

/*
 * Copyright (c) 2011-2013 The original author or authors
 * ------------------------------------------------------
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution.
 *
 *     The Eclipse Public License is available at
 *     http://www.eclipse.org/legal/epl-v10.html
 *
 *     The Apache License v2.0 is available at
 *     http://www.opensource.org/licenses/apache2.0.php
 *
 * You may elect to redistribute this code under either of these licenses.
 */

package io.jsync.net.impl;

import io.jsync.AsyncResult;
import io.jsync.Handler;
import io.jsync.impl.AsyncInternal;
import io.jsync.impl.DefaultContext;
import io.jsync.impl.DefaultFutureResult;
import io.netty.channel.*;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.stream.ChunkedFile;

import javax.net.ssl.SSLPeerUnverifiedException;
import javax.security.cert.X509Certificate;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.InetSocketAddress;

/**
 * Abstract base class for TCP connections.
 *
 * @author <a href="http://tfox.org">Tim Fox</a>
 */
public abstract class ConnectionBase {

    protected final AsyncInternal async;
    protected final Channel channel;
    protected final DefaultContext context;
    protected Handler<Throwable> exceptionHandler;
    protected Handler<Void> closeHandler;
    private volatile boolean writable = true;
    private boolean read;
    private boolean needsFlush;

    protected ConnectionBase(AsyncInternal async, Channel channel, DefaultContext context) {
        this.async = async;
        this.channel = channel;
        this.context = context;
    }

    public final void startRead() {
        read = true;
    }

    public final void endReadAndFlush() {
        read = false;
        if (needsFlush) {
            needsFlush = false;
            // flush now
            channel.flush();
        }
    }

    public ChannelFuture queueForWrite(final Object obj) {
        needsFlush = true;
        return channel.write(obj);
    }

    public ChannelFuture write(Object obj) {
        if (read) {
            return queueForWrite(obj);
        }
        if (channel.isOpen()) {
            return channel.writeAndFlush(obj);
        } else {
            return null;
        }
    }

    /**
     * Close the connection
     */
    public void close() {
        // make sure everything is flushed out on close
        endReadAndFlush();
        channel.close();
    }

    public void doPause() {
        channel.config().setAutoRead(false);
    }

    public void doResume() {
        channel.config().setAutoRead(true);
    }

    public void doSetWriteQueueMaxSize(int size) {
        channel.config().setWriteBufferLowWaterMark(size / 2);
        channel.config().setWriteBufferHighWaterMark(size);
    }

    public boolean doWriteQueueFull() {
        return !writable;
    }

    protected void setWritable(boolean writable) {
        this.writable = writable;
    }

    protected DefaultContext getContext() {
        return context;
    }

    protected void handleException(Throwable t) {
        if (exceptionHandler != null) {
            setContext();
            try {
                exceptionHandler.handle(t);
            } catch (Throwable t2) {
                handleHandlerException(t2);
            }
        }
    }

    protected void handleClosed() {
        if (closeHandler != null) {
            setContext();
            try {
                closeHandler.handle(null);
            } catch (Throwable t) {
                handleHandlerException(t);
            }
        }
    }

    protected void addFuture(final Handler<AsyncResult<Void>> doneHandler, final ChannelFuture future) {
        if (future != null) {
            future.addListener(new ChannelFutureListener() {
                public void operationComplete(final ChannelFuture channelFuture) throws Exception {
                    if (doneHandler != null) {
                        context.execute(new Runnable() {
                            public void run() {
                                if (channelFuture.isSuccess()) {
                                    doneHandler.handle(new DefaultFutureResult<>((Void) null));
                                } else {
                                    doneHandler.handle(new DefaultFutureResult<Void>(channelFuture.cause()));
                                }
                            }
                        });
                    } else if (!channelFuture.isSuccess()) {
                        handleException(channelFuture.cause());
                    }
                }
            });
        }
    }

    protected void setContext() {
        async.setContext(context);
    }

    protected void handleHandlerException(Throwable t) {
        async.reportException(t);
    }

    protected boolean supportsFileRegion() {
        return !isSSL();
    }

    private boolean isSSL() {
        return channel.pipeline().get(SslHandler.class) != null;
    }

    protected ChannelFuture sendFile(File file) {
        final RandomAccessFile raf;
        try {
            raf = new RandomAccessFile(file, "r");
            long fileLength = file.length();

            // Write the content.
            ChannelFuture writeFuture;
            if (!supportsFileRegion()) {
                // Cannot use zero-copy
                writeFuture = write(new ChunkedFile(raf, 0, fileLength, 8192));
            } else {
                // No encryption - use zero-copy.
                final FileRegion region = new DefaultFileRegion(raf.getChannel(), 0, fileLength);
                writeFuture = write(region);
            }
            writeFuture.addListener(new ChannelFutureListener() {
                public void operationComplete(ChannelFuture future) throws Exception {
                    raf.close();
                }
            });
            return writeFuture;
        } catch (IOException e) {
            handleException(e);
            return null;
        }
    }

    public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
        if (isSSL()) {
            final ChannelHandlerContext sslHandlerContext = channel.pipeline().context("ssl");
            assert sslHandlerContext != null;
            final SslHandler sslHandler = (SslHandler) sslHandlerContext.handler();
            return sslHandler.engine().getSession().getPeerCertificateChain();
        } else {
            return null;
        }
    }

    public InetSocketAddress remoteAddress() {
        return (InetSocketAddress) channel.remoteAddress();
    }

    public InetSocketAddress localAddress() {
        return (InetSocketAddress) channel.localAddress();
    }

    protected abstract void handleInterestedOpsChanged();
}