public DefaultChannelPipeline(Channel channel) { if (channel == null) { throw new NullPointerException("channel"); } this.channel = channel; tail = new DefaultChannelHandlerContext(this, null, generateName(TAIL_HANDLER), TAIL_HANDLER); HeadHandler headHandler; switch (channel.metadata().bufferType()) { case BYTE: headHandler = new ByteHeadHandler(); break; case MESSAGE: headHandler = new MessageHeadHandler(); break; default: throw new Error("unknown buffer type: " + channel.metadata().bufferType()); } head = new DefaultChannelHandlerContext(this, null, generateName(headHandler), headHandler, true); head.next = tail; tail.prev = head; unsafe = channel.unsafe(); }
/** 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 DefaultChannelHandlerContext remove( final DefaultChannelHandlerContext ctx, final boolean forward) { assert ctx != head && ctx != tail; DefaultChannelHandlerContext context; Future<?> future; synchronized (this) { if (!ctx.channel().isRegistered() || ctx.executor().inEventLoop()) { remove0(ctx, forward); return ctx; } else { future = ctx.executor() .submit( new Runnable() { @Override public void run() { synchronized (DefaultChannelPipeline.this) { remove0(ctx, forward); } } }); context = ctx; } } // Run the following 'waiting' code outside of the above synchronized block // in order to avoid deadlock waitForFuture(future); return context; }
@Override public ChannelPipeline addAfter( EventExecutorGroup group, String baseName, final String name, ChannelHandler handler) { final DefaultChannelHandlerContext ctx; final DefaultChannelHandlerContext newCtx; synchronized (this) { ctx = getContextOrDie(baseName); checkDuplicateName(name); newCtx = new DefaultChannelHandlerContext(this, group, name, handler); if (!newCtx.channel().isRegistered() || newCtx.executor().inEventLoop()) { addAfter0(name, ctx, newCtx); return this; } } // Run the following 'waiting' code outside of the above synchronized block // in order to avoid deadlock newCtx.executeOnEventLoop( new Runnable() { @Override public void run() { synchronized (DefaultChannelPipeline.this) { checkDuplicateName(name); addAfter0(name, ctx, newCtx); } } }); return this; }
@Override public ChannelHandler first() { DefaultChannelHandlerContext first = head.next; if (first == null) { return null; } return first.handler(); }
@Override public ChannelHandler last() { DefaultChannelHandlerContext last = tail.prev; if (last == head) { return null; } return last.handler(); }
@Override public void fireChannelUnregistered() { head.fireChannelUnregistered(); // Free all buffers if channel is closed and unregistered. if (!channel.isOpen()) { head.invokeFreeInboundBuffer(); } }
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; } }
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 void addBefore0( final String name, DefaultChannelHandlerContext ctx, DefaultChannelHandlerContext newCtx) { newCtx.prev = ctx.prev; newCtx.next = ctx; callBeforeAdd(newCtx); ctx.prev.next = newCtx; ctx.prev = newCtx; name2ctx.put(name, newCtx); callAfterAdd(newCtx); }
private void addFirst0(String name, DefaultChannelHandlerContext newCtx) { DefaultChannelHandlerContext nextCtx = head.next; newCtx.prev = head; newCtx.next = nextCtx; callBeforeAdd(newCtx); head.next = newCtx; nextCtx.prev = newCtx; name2ctx.put(name, newCtx); callAfterAdd(newCtx); }
private void addLast0(final String name, DefaultChannelHandlerContext newCtx) { DefaultChannelHandlerContext prev = tail.prev; newCtx.prev = prev; newCtx.next = tail; callBeforeAdd(newCtx); prev.next = newCtx; tail.prev = newCtx; name2ctx.put(name, newCtx); callAfterAdd(newCtx); }
@Override public void fireChannelActive() { firedChannelActive = true; head.fireChannelActive(); if (channel.config().isAutoRead()) { channel.read(); } if (fireInboundBufferUpdatedOnActivation) { fireInboundBufferUpdatedOnActivation = false; head.fireInboundBufferUpdated(); } }
private void addAfter0( final String name, DefaultChannelHandlerContext ctx, DefaultChannelHandlerContext newCtx) { checkDuplicateName(name); newCtx.prev = ctx; newCtx.next = ctx.next; callBeforeAdd(newCtx); ctx.next.prev = newCtx; ctx.next = newCtx; name2ctx.put(name, newCtx); callAfterAdd(newCtx); }
@Override public void fireInboundBufferSuspended() { head.fireInboundBufferSuspended(); if (channel.config().isAutoRead()) { read(); } }
@Override public ChannelHandlerContext context(Class<? extends ChannelHandler> handlerType) { if (handlerType == null) { throw new NullPointerException("handlerType"); } DefaultChannelHandlerContext ctx = head.next; for (; ; ) { if (ctx == null) { return null; } if (handlerType.isAssignableFrom(ctx.handler().getClass())) { return ctx; } ctx = ctx.next; } }
@Override public void fireChannelInactive() { // Some implementations such as EmbeddedChannel can trigger inboundBufferUpdated() // after deactivation, so it's safe not to revert the firedChannelActive flag here. // Also, all known transports never get re-activated. // firedChannelActive = false; head.fireChannelInactive(); }
@Override public void fireInboundBufferUpdated() { if (!firedChannelActive) { fireInboundBufferUpdatedOnActivation = true; return; } head.fireInboundBufferUpdated(); }
@Override public ChannelHandlerContext context(ChannelHandler handler) { if (handler == null) { throw new NullPointerException("handler"); } DefaultChannelHandlerContext ctx = head.next; for (; ; ) { if (ctx == null) { return null; } if (ctx.handler() == handler) { return ctx; } 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(); }
private void callAfterRemove(final DefaultChannelHandlerContext ctx, boolean forward) { final ChannelHandler handler = ctx.handler(); // Notify the complete removal. try { handler.afterRemove(ctx); } catch (Throwable t) { throw new ChannelPipelineException( ctx.handler().getClass().getName() + ".afterRemove() has thrown an exception.", t); } if (forward) { ctx.forwardBufferContent(); } else { ctx.clearBuffer(); } ctx.removed = true; // Free all buffers before completing removal. if (!channel.isRegistered()) { ctx.freeHandlerBuffersAfterRemoval(); } }
@Override public ChannelFuture close(ChannelPromise promise) { return tail.close(promise); }
@Override public ChannelFuture disconnect(ChannelPromise promise) { return tail.disconnect(promise); }
@Override public ChannelFuture connect( SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { return tail.connect(remoteAddress, localAddress, promise); }
@Override public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) { return tail.bind(localAddress, promise); }
@Override public ChannelFuture write(Object message) { return tail.write(message); }
@Override public ChannelFuture flush() { return tail.flush(); }
@Override public ChannelFuture deregister() { return tail.deregister(); }