Java tutorial
/* * Copyright (c) 2015 The Jupiter Project * * 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 org.jupiter.transport.netty.handler.connector; import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.util.Timeout; import io.netty.util.Timer; import io.netty.util.TimerTask; import org.jupiter.common.util.internal.logging.InternalLogger; import org.jupiter.common.util.internal.logging.InternalLoggerFactory; import org.jupiter.transport.channel.JChannelGroup; import org.jupiter.transport.netty.channel.NettyChannel; import org.jupiter.transport.netty.handler.ChannelHandlerHolder; import java.net.SocketAddress; import java.util.concurrent.TimeUnit; /** * Connections watchdog. * * jupiter * org.jupiter.transport.netty.handler.connector * * @author jiachun.fjc */ @ChannelHandler.Sharable public abstract class ConnectionWatchdog extends ChannelInboundHandlerAdapter implements TimerTask, ChannelHandlerHolder { private static final InternalLogger logger = InternalLoggerFactory.getInstance(ConnectionWatchdog.class); private static final int ST_STARTED = 1; private static final int ST_STOPPED = 2; private final Bootstrap bootstrap; private final Timer timer; private final SocketAddress remoteAddress; private final JChannelGroup group; private volatile int state; private int attempts; public ConnectionWatchdog(Bootstrap bootstrap, Timer timer, SocketAddress remoteAddress, JChannelGroup group) { this.bootstrap = bootstrap; this.timer = timer; this.remoteAddress = remoteAddress; this.group = group; start(); } public boolean isStarted() { return state == ST_STARTED; } public void start() { state = ST_STARTED; } public void stop() { state = ST_STOPPED; } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { Channel ch = ctx.channel(); if (group != null) { group.add(NettyChannel.attachChannel(ch)); } attempts = 0; logger.info("Connects with {}.", ch); ctx.fireChannelActive(); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { boolean doReconnect = isReconnectNeeded(); if (doReconnect) { if (attempts < 12) { attempts++; } long timeout = 2 << attempts; timer.newTimeout(this, timeout, TimeUnit.MILLISECONDS); } logger.warn("Disconnects with {}, address: {}, reconnect: {}.", ctx.channel(), remoteAddress, doReconnect); ctx.fireChannelInactive(); } @Override public void run(Timeout timeout) throws Exception { if (!isReconnectNeeded()) { logger.warn("Cancel reconnecting with {}.", remoteAddress); return; } ChannelFuture future; synchronized (bootstrap) { bootstrap.handler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline().addLast(handlers()); } }); future = bootstrap.connect(remoteAddress); } future.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture f) throws Exception { boolean succeed = f.isSuccess(); logger.warn("Reconnects with {}, {}.", remoteAddress, succeed ? "succeed" : "failed"); if (!succeed) { f.channel().pipeline().fireChannelInactive(); } } }); } private boolean isReconnectNeeded() { return isStarted() && (group == null || (group.size() < group.getCapacity())); } }