Java tutorial
/* * 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.VoidHandler; import io.jsync.buffer.Buffer; import io.jsync.eventbus.Message; import io.jsync.file.impl.PathAdjuster; import io.jsync.impl.AsyncInternal; import io.jsync.impl.DefaultContext; import io.jsync.impl.DefaultFutureResult; import io.jsync.net.NetSocket; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.handler.ssl.SslHandler; import io.netty.util.CharsetUtil; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import java.io.File; import java.net.InetSocketAddress; import java.nio.charset.Charset; import java.util.ArrayDeque; import java.util.Queue; import java.util.UUID; public class DefaultNetSocket extends ConnectionBase implements NetSocket { private final String writeHandlerID; private final Handler<Message<Buffer>> writeHandler; private final TCPSSLHelper helper; private Handler<Buffer> dataHandler; private Handler<Void> endHandler; private Handler<Void> drainHandler; private Queue<Buffer> pendingData; private boolean paused = false; private boolean client; private ChannelFuture writeFuture; public DefaultNetSocket(AsyncInternal async, Channel channel, DefaultContext context, TCPSSLHelper helper, boolean client) { super(async, channel, context); this.helper = helper; this.client = client; this.writeHandlerID = UUID.randomUUID().toString(); writeHandler = new Handler<Message<Buffer>>() { public void handle(Message<Buffer> msg) { write(msg.body()); } }; async.eventBus().registerLocalHandler(writeHandlerID, writeHandler); } @Override public String writeHandlerID() { return writeHandlerID; } @Override public NetSocket write(Buffer data) { ByteBuf buf = data.getByteBuf(); write(buf); return this; } @Override public NetSocket write(String str) { write(Unpooled.copiedBuffer(str, CharsetUtil.UTF_8)); return this; } @Override public NetSocket write(String str, String enc) { if (enc == null) { write(str); } else { write(Unpooled.copiedBuffer(str, Charset.forName(enc))); } return this; } @Override public NetSocket dataHandler(Handler<Buffer> dataHandler) { this.dataHandler = dataHandler; return this; } @Override public NetSocket pause() { paused = true; doPause(); return this; } @Override public NetSocket resume() { if (!paused) { return this; } paused = false; if (pendingData != null) { for (;;) { final Buffer buf = pendingData.poll(); if (buf == null) { break; } async.runOnContext(new VoidHandler() { @Override protected void handle() { handleDataReceived(buf); } }); } } doResume(); return this; } @Override public NetSocket setWriteQueueMaxSize(int maxSize) { doSetWriteQueueMaxSize(maxSize); return this; } @Override public boolean writeQueueFull() { return doWriteQueueFull(); } @Override public NetSocket endHandler(Handler<Void> endHandler) { this.endHandler = endHandler; return this; } @Override public NetSocket drainHandler(Handler<Void> drainHandler) { this.drainHandler = drainHandler; async.runOnContext(new VoidHandler() { public void handle() { callDrainHandler(); //If the channel is already drained, we want to call it immediately } }); return this; } @Override public NetSocket sendFile(String filename) { return sendFile(filename, null); } @Override public NetSocket sendFile(String filename, final Handler<AsyncResult<Void>> resultHandler) { File f = new File(PathAdjuster.adjust(async, filename)); if (f.isDirectory()) { throw new IllegalArgumentException("filename must point to a file and not to a directory"); } ChannelFuture future = super.sendFile(f); if (resultHandler != null) { future.addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture future) throws Exception { final AsyncResult<Void> res; if (future.isSuccess()) { res = new DefaultFutureResult<>((Void) null); } else { res = new DefaultFutureResult<>(future.cause()); } async.runOnContext(new Handler<Void>() { @Override public void handle(Void v) { resultHandler.handle(res); } }); } }); } return this; } @Override public InetSocketAddress remoteAddress() { return super.remoteAddress(); } public InetSocketAddress localAddress() { return super.localAddress(); } @Override public NetSocket exceptionHandler(Handler<Throwable> handler) { this.exceptionHandler = handler; return this; } @Override public NetSocket closeHandler(Handler<Void> handler) { this.closeHandler = handler; return this; } @Override public void close() { if (writeFuture != null) { // Close after all data is written writeFuture.addListener(ChannelFutureListener.CLOSE); channel.flush(); } else { super.close(); } } protected DefaultContext getContext() { return super.getContext(); } protected void handleClosed() { setContext(); if (endHandler != null) { try { endHandler.handle(null); } catch (Throwable t) { handleHandlerException(t); } } super.handleClosed(); if (async.eventBus() != null) { async.eventBus().unregisterHandler(writeHandlerID, writeHandler); } } public void handleInterestedOpsChanged() { setContext(); callDrainHandler(); } void handleDataReceived(Buffer data) { if (paused) { if (pendingData == null) { pendingData = new ArrayDeque<>(); } pendingData.add(data); return; } if (dataHandler != null) { setContext(); try { dataHandler.handle(data); } catch (Throwable t) { handleHandlerException(t); } } } private void write(ByteBuf buff) { writeFuture = super.write(buff); } private void callDrainHandler() { if (drainHandler != null) { if (!writeQueueFull()) { try { drainHandler.handle(null); } catch (Throwable t) { handleHandlerException(t); } } } } @Override public NetSocket ssl(final Handler<Void> handler) { SslHandler sslHandler = channel.pipeline().get(SslHandler.class); if (sslHandler == null) { sslHandler = helper.createSslHandler(async, client); channel.pipeline().addFirst(sslHandler); } sslHandler.handshakeFuture().addListener(new GenericFutureListener<Future<Channel>>() { @Override public void operationComplete(final Future<Channel> future) throws Exception { if (context.isOnCorrectWorker(channel.eventLoop())) { if (future.isSuccess()) { try { async.setContext(context); handler.handle(null); } catch (Throwable t) { context.reportException(t); } } else { context.reportException(future.cause()); } } else { context.execute(new Runnable() { public void run() { if (future.isSuccess()) { handler.handle(null); } else { context.reportException(future.cause()); } } }); } } }); return this; } @Override public boolean isSsl() { return channel.pipeline().get(SslHandler.class) != null; } }