View Javadoc

    1   /*
    2    * Copyright 2009 Red Hat, Inc.
    3    *
    4    * Red Hat licenses this file to you under the Apache License, version 2.0
    5    * (the "License"); you may not use this file except in compliance with the
    6    * License.  You may obtain a copy of the License at:
    7    *
    8    *    http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
    13   * License for the specific language governing permissions and limitations
    14   * under the License.
    15   */
    16  package org.jboss.netty.handler.timeout;
    17
    18  import static org.jboss.netty.channel.Channels.*;
    19
    20  import java.util.concurrent.TimeUnit;
    21
    22  import org.jboss.netty.bootstrap.ServerBootstrap;
    23  import org.jboss.netty.channel.ChannelFuture;
    24  import org.jboss.netty.channel.ChannelFutureListener;
    25  import org.jboss.netty.channel.ChannelHandlerContext;
    26  import org.jboss.netty.channel.ChannelPipeline;
    27  import org.jboss.netty.channel.ChannelPipelineFactory;
    28  import org.jboss.netty.channel.Channels;
    29  import org.jboss.netty.channel.MessageEvent;
    30  import org.jboss.netty.channel.SimpleChannelDownstreamHandler;
    31  import org.jboss.netty.channel.ChannelHandler.Sharable;
    32  import org.jboss.netty.util.ExternalResourceReleasable;
    33  import org.jboss.netty.util.HashedWheelTimer;
    34  import org.jboss.netty.util.Timeout;
    35  import org.jboss.netty.util.Timer;
    36  import org.jboss.netty.util.TimerTask;
    37
    38  /**
    39   * Raises a {@link WriteTimeoutException} when no data was written within a
    40   * certain period of time.
    41   *
    42   * <pre>
    43   * public class MyPipelineFactory implements {@link ChannelPipelineFactory} {
    44   *
    45   *     private final {@link Timer} timer;
    46   *
    47   *     public MyPipelineFactory({@link Timer} timer) {
    48   *         this.timer = timer;
    49   *     }
    50   *
    51   *     public {@link ChannelPipeline} getPipeline() {
    52   *         // An example configuration that implements 30-second write timeout:
    53   *         return {@link Channels}.pipeline(
    54   *             <b>new {@link WriteTimeoutHandler}(timer, 30), // timer must be shared.</b>
    55   *             new MyHandler());
    56   *     }
    57   * }
    58   *
    59   * {@link ServerBootstrap} bootstrap = ...;
    60   * {@link Timer} timer = new {@link HashedWheelTimer}();
    61   * ...
    62   * bootstrap.setPipelineFactory(new MyPipelineFactory(timer));
    63   * </pre>
    64   *
    65   * The {@link Timer} which was specified when the {@link ReadTimeoutHandler} is
    66   * created should be stopped manually by calling {@link #releaseExternalResources()}
    67   * or {@link Timer#stop()} when your application shuts down.
    68   *
    69   * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
    70   * @author <a href="http://gleamynode.net/">Trustin Lee</a>
    71   * @version $Rev: 2222 $, $Date: 2010-03-24 14:07:27 +0900 (Wed, 24 Mar 2010) $
    72   *
    73   * @see ReadTimeoutHandler
    74   * @see IdleStateHandler
    75   *
    76   * @apiviz.landmark
    77   * @apiviz.uses org.jboss.netty.util.HashedWheelTimer
    78   * @apiviz.has org.jboss.netty.handler.timeout.TimeoutException oneway - - raises
    79   */
    80  @Sharable
    81  public class WriteTimeoutHandler extends SimpleChannelDownstreamHandler
    82                                   implements ExternalResourceReleasable {
    83
    84      static final WriteTimeoutException EXCEPTION = new WriteTimeoutException();
    85
    86      private final Timer timer;
    87      private final long timeoutMillis;
    88
    89      /**
    90       * Creates a new instance.
    91       *
    92       * @param timer
    93       *        the {@link Timer} that is used to trigger the scheduled event.
    94       *        The recommended {@link Timer} implementation is {@link HashedWheelTimer}.
    95       * @param timeoutSeconds
    96       *        write timeout in seconds
    97       */
    98      public WriteTimeoutHandler(Timer timer, int timeoutSeconds) {
    99          this(timer, timeoutSeconds, TimeUnit.SECONDS);
    100     }
    101
    102     /**
    103      * Creates a new instance.
    104      *
    105      * @param timer
    106      *        the {@link Timer} that is used to trigger the scheduled event.
    107      *        The recommended {@link Timer} implementation is {@link HashedWheelTimer}.
    108      * @param timeout
    109      *        write timeout
    110      * @param unit
    111      *        the {@link TimeUnit} of {@code timeout}
    112      */
    113     public WriteTimeoutHandler(Timer timer, long timeout, TimeUnit unit) {
    114         if (timer == null) {
    115             throw new NullPointerException("timer");
    116         }
    117         if (unit == null) {
    118             throw new NullPointerException("unit");
    119         }
    120
    121         this.timer = timer;
    122         if (timeout <= 0) {
    123             timeoutMillis = 0;
    124         } else {
    125             timeoutMillis = Math.max(unit.toMillis(timeout), 1);
    126         }
    127     }
    128
    129     /**
    130      * Stops the {@link Timer} which was specified in the constructor of this
    131      * handler.  You should not call this method if the {@link Timer} is in use
    132      * by other objects.
    133      */
    134     public void releaseExternalResources() {
    135         timer.stop();
    136     }
    137
    138     protected long getTimeoutMillis(@SuppressWarnings("unused") MessageEvent e) {
    139         return timeoutMillis;
    140     }
    141
    142     @Override
    143     public void writeRequested(ChannelHandlerContext ctx, MessageEvent e)
    144             throws Exception {
    145
    146         long timeoutMillis = getTimeoutMillis(e);
    147         if (timeoutMillis > 0) {
    148             // Set timeout only when getTimeoutMillis() returns a positive value.
    149             ChannelFuture future = e.getFuture();
    150             final Timeout timeout = timer.newTimeout(
    151                     new WriteTimeoutTask(ctx, future),
    152                     timeoutMillis, TimeUnit.MILLISECONDS);
    153
    154             future.addListener(new TimeoutCanceller(timeout));
    155         }
    156
    157         super.writeRequested(ctx, e);
    158     }
    159
    160     protected void writeTimedOut(ChannelHandlerContext ctx) throws Exception {
    161         Channels.fireExceptionCaught(ctx, EXCEPTION);
    162     }
    163
    164     private final class WriteTimeoutTask implements TimerTask {
    165
    166         private final ChannelHandlerContext ctx;
    167         private final ChannelFuture future;
    168
    169         WriteTimeoutTask(ChannelHandlerContext ctx, ChannelFuture future) {
    170             this.ctx = ctx;
    171             this.future = future;
    172         }
    173
    174         public void run(Timeout timeout) throws Exception {
    175             if (timeout.isCancelled()) {
    176                 return;
    177             }
    178
    179             if (!ctx.getChannel().isOpen()) {
    180                 return;
    181             }
    182
    183             // Mark the future as failure
    184             if (future.setFailure(EXCEPTION)) {
    185                 // If succeeded to mark as failure, notify the pipeline, too.
    186                 try {
    187                     writeTimedOut(ctx);
    188                 } catch (Throwable t) {
    189                     fireExceptionCaught(ctx, t);
    190                 }
    191             }
    192         }
    193     }
    194
    195     /**
    196      * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
    197      * @author <a href="http://gleamynode.net/">Trustin Lee</a>
    198      * @version $Rev: 2222 $, $Date: 2010-03-24 14:07:27 +0900 (Wed, 24 Mar 2010) $
    199      */
    200     private static final class TimeoutCanceller implements ChannelFutureListener {
    201         private final Timeout timeout;
    202
    203         TimeoutCanceller(Timeout timeout) {
    204             this.timeout = timeout;
    205         }
    206
    207         public void operationComplete(ChannelFuture future) throws Exception {
    208             timeout.cancel();
    209         }
    210     }
    211 }