Java tutorial
/* * Copyright (c) 2011-2012 by the original author or authors. * * 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 io.dyn.net.tcp; import static io.dyn.el.SpelExpression.*; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.nio.channels.ClosedChannelException; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import io.dyn.core.EventedBase; import io.dyn.core.Events; import io.dyn.core.Lifecycle; import io.dyn.core.Tasks; import io.dyn.core.handler.CompletionHandler; import io.dyn.core.handler.Handler; import io.dyn.core.log.Logger; import io.dyn.core.sys.Sys; import io.dyn.net.nio.Buffer; import io.netty.bootstrap.ClientBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipelineFactory; import io.netty.channel.Channels; import io.netty.channel.socket.nio.NioClientSocketChannelFactory; /** * @author Jon Brisbin <jon@jbrisbin.com> */ public abstract class TcpClient<T extends TcpClient<? super T>> extends EventedBase<T> implements Lifecycle<T> { protected final Logger log = Logger.logger(getClass()); protected String host = "localhost"; protected int port = 3000; protected boolean keepAlive = true; protected int socketTimeout = 2000; protected AtomicBoolean started = new AtomicBoolean(false); private ClientBootstrap bootstrap = new ClientBootstrap( new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool(Tasks.newThreadFactory(getClass().getSimpleName().toLowerCase())), Sys.PROCESSORS)); protected ChannelFuture channelFuture; { on($("#{#this.endsWith('Exception')}"), new Handler<Throwable>() { @Override public void handle(Throwable t, Object... args) { if (!(t instanceof ClosedChannelException)) { String s = t.getMessage(); if (null != s) { switch (s) { case "Broken pipe": case "Connection reset by peer": break; default: log.error(t); } } } } }); } public String host() { return host; } @SuppressWarnings({ "unchecked" }) public T host(String host) { this.host = host; return (T) this; } public int port() { return port; } @SuppressWarnings({ "unchecked" }) public T port(int port) { this.port = port; return (T) this; } public boolean keepAlive() { return keepAlive; } @SuppressWarnings({ "unchecked" }) public T keepAlive(boolean keepAlive) { this.keepAlive = keepAlive; return (T) this; } public int socketTimeout() { return socketTimeout; } @SuppressWarnings({ "unchecked" }) public T socketTimeout(int socketTimeout) { this.socketTimeout = socketTimeout; return (T) this; } @SuppressWarnings({ "unchecked" }) @Override public T start() { if (!started.get()) { on(Lifecycle.STOP, new CompletionHandler() { @Override protected void complete() { if (channelFuture.awaitUninterruptibly(15, TimeUnit.SECONDS)) { channelFuture.getChannel().close(); } started.set(false); } }); bootstrap.setOption("child.keepAlive", keepAlive); bootstrap.setOption("child.receiveBufferSize", Buffer.SMALL_BUFFER_SIZE); bootstrap.setPipelineFactory(new ChannelPipelineFactory() { @Override public ChannelPipeline getPipeline() throws Exception { final ChannelPipeline pipeline = Channels.pipeline(); TcpClient.this.configurePipeline(pipeline); return pipeline; } }); try { channelFuture = bootstrap.connect(new InetSocketAddress(InetAddress.getByName(host), port)); channelFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { if (channelFuture.isSuccess()) { started.set(true); event(Lifecycle.START); } else { Throwable t = channelFuture.getCause(); event(Events.classToEventExpression(t.getClass()), t); } } }); } catch (UnknownHostException e) { event(Events.classToEventExpression(e.getClass()), e); } } return (T) this; } @SuppressWarnings({ "unchecked" }) @Override public T stop() { if (started.get()) { channelFuture.getChannel().close().addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { started.set(false); event(Lifecycle.STOP); } }); } return (T) this; } protected abstract void configurePipeline(ChannelPipeline pipeline); }