private void replace0(
      DefaultChannelHandlerContext ctx,
      String newName,
      DefaultChannelHandlerContext newCtx,
      boolean forward) {
    boolean sameName = ctx.name().equals(newName);

    DefaultChannelHandlerContext prev = ctx.prev;
    DefaultChannelHandlerContext next = ctx.next;
    newCtx.prev = prev;
    newCtx.next = next;

    callBeforeRemove(ctx);
    callBeforeAdd(newCtx);

    prev.next = newCtx;
    next.prev = newCtx;

    if (!sameName) {
      name2ctx.remove(ctx.name());
    }
    name2ctx.put(newName, newCtx);

    ChannelPipelineException removeException = null;
    ChannelPipelineException addException = null;
    boolean removed = false;
    try {
      callAfterRemove(ctx, forward);
      removed = true;
    } catch (ChannelPipelineException e) {
      removeException = e;
    }

    boolean added = false;
    try {
      callAfterAdd(newCtx);
      added = true;
    } catch (ChannelPipelineException e) {
      addException = e;
    }

    if (!removed && !added) {
      logger.warn(removeException.getMessage(), removeException);
      logger.warn(addException.getMessage(), addException);
      throw new ChannelPipelineException(
          "Both "
              + ctx.handler().getClass().getName()
              + ".afterRemove() and "
              + newCtx.handler().getClass().getName()
              + ".afterAdd() failed; see logs.");
    } else if (!removed) {
      throw removeException;
    } else if (!added) {
      throw addException;
    }
  }
  /** Returns the {@link String} representation of this pipeline. */
  @Override
  public String toString() {
    StringBuilder buf = new StringBuilder();
    buf.append(getClass().getSimpleName());
    buf.append('{');
    DefaultChannelHandlerContext ctx = head.next;
    for (; ; ) {
      if (ctx == tail) {
        break;
      }

      buf.append('(');
      buf.append(ctx.name());
      buf.append(" = ");
      buf.append(ctx.handler().getClass().getName());
      buf.append(')');

      ctx = ctx.next;
      if (ctx == tail) {
        break;
      }

      buf.append(", ");
    }
    buf.append('}');
    return buf.toString();
  }
  private void remove0(DefaultChannelHandlerContext ctx, boolean forward) {
    callBeforeRemove(ctx);

    DefaultChannelHandlerContext prev = ctx.prev;
    DefaultChannelHandlerContext next = ctx.next;
    prev.next = next;
    next.prev = prev;
    name2ctx.remove(ctx.name());

    callAfterRemove(ctx, forward);
  }
 @Override
 public Map<String, ChannelHandler> toMap() {
   Map<String, ChannelHandler> map = new LinkedHashMap<String, ChannelHandler>();
   DefaultChannelHandlerContext ctx = head.next;
   for (; ; ) {
     if (ctx == tail) {
       return map;
     }
     map.put(ctx.name(), ctx.handler());
     ctx = ctx.next;
   }
 }
 @Override
 public List<String> names() {
   List<String> list = new ArrayList<String>();
   DefaultChannelHandlerContext ctx = head.next;
   for (; ; ) {
     if (ctx == null) {
       return list;
     }
     list.add(ctx.name());
     ctx = ctx.next;
   }
 }
  private ChannelHandler replace(
      final DefaultChannelHandlerContext ctx,
      final String newName,
      ChannelHandler newHandler,
      final boolean forward) {

    assert ctx != head && ctx != tail;

    Future<?> future;
    synchronized (this) {
      boolean sameName = ctx.name().equals(newName);
      if (!sameName) {
        checkDuplicateName(newName);
      }

      final DefaultChannelHandlerContext newCtx =
          new DefaultChannelHandlerContext(this, ctx.executor, newName, newHandler);

      if (!newCtx.channel().isRegistered() || newCtx.executor().inEventLoop()) {
        replace0(ctx, newName, newCtx, forward);

        return ctx.handler();
      } else {
        future =
            newCtx
                .executor()
                .submit(
                    new Runnable() {
                      @Override
                      public void run() {
                        synchronized (DefaultChannelPipeline.this) {
                          replace0(ctx, newName, newCtx, forward);
                        }
                      }
                    });
      }
    }

    // Run the following 'waiting' code outside of the above synchronized block
    // in order to avoid deadlock

    waitForFuture(future);

    return ctx.handler();
  }