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

io.netty.handler.timeout.IdleStateHandler Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 32.0.0.Final
Show newest version
/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you 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.netty.handler.timeout;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPromise;
import io.netty.util.concurrent.EventExecutor;

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * Triggers an {@link IdleStateEvent} when a {@link Channel} has not performed
 * read, write, or both operation for a while.
 *
 * 

Supported idle states

* * * * * * * * * * * * * * * * *
PropertyMeaning
{@code readerIdleTime}an {@link IdleStateEvent} whose state is {@link IdleState#READER_IDLE} * will be triggered when no read was performed for the specified period of * time. Specify {@code 0} to disable.
{@code writerIdleTime}an {@link IdleStateEvent} whose state is {@link IdleState#WRITER_IDLE} * will be triggered when no write was performed for the specified period of * time. Specify {@code 0} to disable.
{@code allIdleTime}an {@link IdleStateEvent} whose state is {@link IdleState#ALL_IDLE} * will be triggered when neither read nor write was performed for the * specified period of time. Specify {@code 0} to disable.
* *
 * // An example that sends a ping message when there is no outbound traffic
 * // for 30 seconds.  The connection is closed when there is no inbound traffic
 * // for 60 seconds.
 *
 * public class MyChannelInitializer extends {@link ChannelInitializer}<{@link Channel}> {
 *     {@code @Override}
 *     public void initChannel({@link Channel} channel) {
 *         channel.pipeline().addLast("idleStateHandler", new {@link IdleStateHandler}(60, 30, 0));
 *         channel.pipeline().addLast("myHandler", new MyHandler());
 *     }
 * }
 *
 * // Handler should handle the {@link IdleStateEvent} triggered by {@link IdleStateHandler}.
 * public class MyHandler extends {@link ChannelDuplexHandler} {
 *     {@code @Override}
 *     public void userEventTriggered({@link ChannelHandlerContext} ctx, {@link Object} evt) throws {@link Exception} {
 *         if (evt instanceof {@link IdleStateEvent}) {
 *             {@link IdleStateEvent} e = ({@link IdleStateEvent}) evt;
 *             if (e.state() == {@link IdleState}.READER_IDLE) {
 *                 ctx.close();
 *             } else if (e.state() == {@link IdleState}.WRITER_IDLE) {
 *                 ctx.writeAndFlush(new PingMessage());
 *             }
 *         }
 *     }
 * }
 *
 * {@link ServerBootstrap} bootstrap = ...;
 * ...
 * bootstrap.childHandler(new MyChannelInitializer());
 * ...
 * 
* * @see ReadTimeoutHandler * @see WriteTimeoutHandler */ public class IdleStateHandler extends ChannelDuplexHandler { private static final long MIN_TIMEOUT_NANOS = TimeUnit.MILLISECONDS.toNanos(1); private final long readerIdleTimeNanos; private final long writerIdleTimeNanos; private final long allIdleTimeNanos; volatile ScheduledFuture readerIdleTimeout; volatile long lastReadTime; private boolean firstReaderIdleEvent = true; volatile ScheduledFuture writerIdleTimeout; volatile long lastWriteTime; private boolean firstWriterIdleEvent = true; volatile ScheduledFuture allIdleTimeout; private boolean firstAllIdleEvent = true; private volatile int state; // 0 - none, 1 - initialized, 2 - destroyed /** * Creates a new instance firing {@link IdleStateEvent}s. * * @param readerIdleTimeSeconds * an {@link IdleStateEvent} whose state is {@link IdleState#READER_IDLE} * will be triggered when no read was performed for the specified * period of time. Specify {@code 0} to disable. * @param writerIdleTimeSeconds * an {@link IdleStateEvent} whose state is {@link IdleState#WRITER_IDLE} * will be triggered when no write was performed for the specified * period of time. Specify {@code 0} to disable. * @param allIdleTimeSeconds * an {@link IdleStateEvent} whose state is {@link IdleState#ALL_IDLE} * will be triggered when neither read nor write was performed for * the specified period of time. Specify {@code 0} to disable. */ public IdleStateHandler( int readerIdleTimeSeconds, int writerIdleTimeSeconds, int allIdleTimeSeconds) { this(readerIdleTimeSeconds, writerIdleTimeSeconds, allIdleTimeSeconds, TimeUnit.SECONDS); } /** * Creates a new instance firing {@link IdleStateEvent}s. * * @param readerIdleTime * an {@link IdleStateEvent} whose state is {@link IdleState#READER_IDLE} * will be triggered when no read was performed for the specified * period of time. Specify {@code 0} to disable. * @param writerIdleTime * an {@link IdleStateEvent} whose state is {@link IdleState#WRITER_IDLE} * will be triggered when no write was performed for the specified * period of time. Specify {@code 0} to disable. * @param allIdleTime * an {@link IdleStateEvent} whose state is {@link IdleState#ALL_IDLE} * will be triggered when neither read nor write was performed for * the specified period of time. Specify {@code 0} to disable. * @param unit * the {@link TimeUnit} of {@code readerIdleTime}, * {@code writeIdleTime}, and {@code allIdleTime} */ public IdleStateHandler( long readerIdleTime, long writerIdleTime, long allIdleTime, TimeUnit unit) { if (unit == null) { throw new NullPointerException("unit"); } if (readerIdleTime <= 0) { readerIdleTimeNanos = 0; } else { readerIdleTimeNanos = Math.max(unit.toNanos(readerIdleTime), MIN_TIMEOUT_NANOS); } if (writerIdleTime <= 0) { writerIdleTimeNanos = 0; } else { writerIdleTimeNanos = Math.max(unit.toNanos(writerIdleTime), MIN_TIMEOUT_NANOS); } if (allIdleTime <= 0) { allIdleTimeNanos = 0; } else { allIdleTimeNanos = Math.max(unit.toNanos(allIdleTime), MIN_TIMEOUT_NANOS); } } /** * Return the readerIdleTime that was given when instance this class in milliseconds. * */ public long getReaderIdleTimeInMillis() { return TimeUnit.NANOSECONDS.toMillis(readerIdleTimeNanos); } /** * Return the writerIdleTime that was given when instance this class in milliseconds. * */ public long getWriterIdleTimeInMillis() { return TimeUnit.NANOSECONDS.toMillis(writerIdleTimeNanos); } /** * Return the allIdleTime that was given when instance this class in milliseconds. * */ public long getAllIdleTimeInMillis() { return TimeUnit.NANOSECONDS.toMillis(allIdleTimeNanos); } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { if (ctx.channel().isActive() && ctx.channel().isRegistered()) { // channelActvie() event has been fired already, which means this.channelActive() will // not be invoked. We have to initialize here instead. initialize(ctx); } else { // channelActive() event has not been fired yet. this.channelActive() will be invoked // and initialization will occur there. } } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { destroy(); } @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { // Initialize early if channel is active already. if (ctx.channel().isActive()) { initialize(ctx); } super.channelRegistered(ctx); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { // This method will be invoked only if this handler was added // before channelActive() event is fired. If a user adds this handler // after the channelActive() event, initialize() will be called by beforeAdd(). initialize(ctx); super.channelActive(ctx); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { destroy(); super.channelInactive(ctx); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { lastReadTime = System.nanoTime(); firstReaderIdleEvent = firstAllIdleEvent = true; ctx.fireChannelRead(msg); } @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { promise.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { lastWriteTime = System.nanoTime(); firstWriterIdleEvent = firstAllIdleEvent = true; } }); ctx.write(msg, promise); } private void initialize(ChannelHandlerContext ctx) { // Avoid the case where destroy() is called before scheduling timeouts. // See: https://github.com/netty/netty/issues/143 switch (state) { case 1: case 2: return; } state = 1; EventExecutor loop = ctx.executor(); lastReadTime = lastWriteTime = System.nanoTime(); if (readerIdleTimeNanos > 0) { readerIdleTimeout = loop.schedule( new ReaderIdleTimeoutTask(ctx), readerIdleTimeNanos, TimeUnit.NANOSECONDS); } if (writerIdleTimeNanos > 0) { writerIdleTimeout = loop.schedule( new WriterIdleTimeoutTask(ctx), writerIdleTimeNanos, TimeUnit.NANOSECONDS); } if (allIdleTimeNanos > 0) { allIdleTimeout = loop.schedule( new AllIdleTimeoutTask(ctx), allIdleTimeNanos, TimeUnit.NANOSECONDS); } } private void destroy() { state = 2; if (readerIdleTimeout != null) { readerIdleTimeout.cancel(false); readerIdleTimeout = null; } if (writerIdleTimeout != null) { writerIdleTimeout.cancel(false); writerIdleTimeout = null; } if (allIdleTimeout != null) { allIdleTimeout.cancel(false); allIdleTimeout = null; } } /** * Is called when an {@link IdleStateEvent} should be fired. This implementation calls * {@link ChannelHandlerContext#fireUserEventTriggered(Object)}. */ protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception { ctx.fireUserEventTriggered(evt); } private final class ReaderIdleTimeoutTask implements Runnable { private final ChannelHandlerContext ctx; ReaderIdleTimeoutTask(ChannelHandlerContext ctx) { this.ctx = ctx; } @Override public void run() { if (!ctx.channel().isOpen()) { return; } long currentTime = System.nanoTime(); long lastReadTime = IdleStateHandler.this.lastReadTime; long nextDelay = readerIdleTimeNanos - (currentTime - lastReadTime); if (nextDelay <= 0) { // Reader is idle - set a new timeout and notify the callback. readerIdleTimeout = ctx.executor().schedule(this, readerIdleTimeNanos, TimeUnit.NANOSECONDS); try { IdleStateEvent event; if (firstReaderIdleEvent) { firstReaderIdleEvent = false; event = IdleStateEvent.FIRST_READER_IDLE_STATE_EVENT; } else { event = IdleStateEvent.READER_IDLE_STATE_EVENT; } channelIdle(ctx, event); } catch (Throwable t) { ctx.fireExceptionCaught(t); } } else { // Read occurred before the timeout - set a new timeout with shorter delay. readerIdleTimeout = ctx.executor().schedule(this, nextDelay, TimeUnit.NANOSECONDS); } } } private final class WriterIdleTimeoutTask implements Runnable { private final ChannelHandlerContext ctx; WriterIdleTimeoutTask(ChannelHandlerContext ctx) { this.ctx = ctx; } @Override public void run() { if (!ctx.channel().isOpen()) { return; } long currentTime = System.nanoTime(); long lastWriteTime = IdleStateHandler.this.lastWriteTime; long nextDelay = writerIdleTimeNanos - (currentTime - lastWriteTime); if (nextDelay <= 0) { // Writer is idle - set a new timeout and notify the callback. writerIdleTimeout = ctx.executor().schedule( this, writerIdleTimeNanos, TimeUnit.NANOSECONDS); try { IdleStateEvent event; if (firstWriterIdleEvent) { firstWriterIdleEvent = false; event = IdleStateEvent.FIRST_WRITER_IDLE_STATE_EVENT; } else { event = IdleStateEvent.WRITER_IDLE_STATE_EVENT; } channelIdle(ctx, event); } catch (Throwable t) { ctx.fireExceptionCaught(t); } } else { // Write occurred before the timeout - set a new timeout with shorter delay. writerIdleTimeout = ctx.executor().schedule(this, nextDelay, TimeUnit.NANOSECONDS); } } } private final class AllIdleTimeoutTask implements Runnable { private final ChannelHandlerContext ctx; AllIdleTimeoutTask(ChannelHandlerContext ctx) { this.ctx = ctx; } @Override public void run() { if (!ctx.channel().isOpen()) { return; } long currentTime = System.nanoTime(); long lastIoTime = Math.max(lastReadTime, lastWriteTime); long nextDelay = allIdleTimeNanos - (currentTime - lastIoTime); if (nextDelay <= 0) { // Both reader and writer are idle - set a new timeout and // notify the callback. allIdleTimeout = ctx.executor().schedule( this, allIdleTimeNanos, TimeUnit.NANOSECONDS); try { IdleStateEvent event; if (firstAllIdleEvent) { firstAllIdleEvent = false; event = IdleStateEvent.FIRST_ALL_IDLE_STATE_EVENT; } else { event = IdleStateEvent.ALL_IDLE_STATE_EVENT; } channelIdle(ctx, event); } catch (Throwable t) { ctx.fireExceptionCaught(t); } } else { // Either read or write occurred before the timeout - set a new // timeout with shorter delay. allIdleTimeout = ctx.executor().schedule(this, nextDelay, TimeUnit.NANOSECONDS); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy