/* * Copyright 2011 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 org.jboss.netty.channel; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.jboss.netty.logging.InternalLogger; import org.jboss.netty.logging.InternalLoggerFactory; import org.jboss.netty.util.internal.ConversionUtil; /** * A {@link ChannelPipeline} that might perform better at the cost of * disabled dynamic insertion and removal of {@link ChannelHandler}s. * An attempt to insert, remove, or replace a handler in this pipeline will * trigger an {@link UnsupportedOperationException}. * * @deprecated use {@link DefaultChannelPipeline} */ @Deprecated public class StaticChannelPipeline implements ChannelPipeline { // FIXME Code duplication with DefaultChannelPipeline static final InternalLogger logger = InternalLoggerFactory.getInstance(StaticChannelPipeline.class); private volatile Channel channel; private volatile ChannelSink sink; private final StaticChannelHandlerContext[] contexts; private final int lastIndex; private final Map<String, StaticChannelHandlerContext> name2ctx = new HashMap<String, StaticChannelHandlerContext>(4); /** * Creates a new pipeline from the specified handlers. * The names of the specified handlers are generated automatically; * the first handler's name is {@code "0"}, the second handler's name is * {@code "1"}, the third handler's name is {@code "2"}, and so on. */ public StaticChannelPipeline(ChannelHandler... handlers) { if (handlers == null) { throw new NullPointerException("handlers"); } if (handlers.length == 0) { throw new IllegalArgumentException("no handlers specified"); } // Get the number of first non-null handlers. StaticChannelHandlerContext[] contexts = new StaticChannelHandlerContext[handlers.length]; int nContexts; for (nContexts = 0; nContexts < contexts.length; nContexts ++) { ChannelHandler h = handlers[nContexts]; if (h == null) { break; } } if (nContexts == contexts.length) { this.contexts = contexts; lastIndex = contexts.length - 1; } else { this.contexts = contexts = new StaticChannelHandlerContext[nContexts]; lastIndex = nContexts - 1; } // Initialize the first non-null handlers only. for (int i = 0; i < nContexts; i ++) { ChannelHandler h = handlers[i]; String name = ConversionUtil.toString(i); StaticChannelHandlerContext ctx = new StaticChannelHandlerContext(i, name, h); contexts[i] = ctx; name2ctx.put(name, ctx); } for (ChannelHandlerContext ctx: contexts) { callBeforeAdd(ctx); callAfterAdd(ctx); } } public ChannelFuture execute(Runnable task) { return getSink().execute(this, task); } public Channel getChannel() { return channel; } public ChannelSink getSink() { ChannelSink sink = this.sink; if (sink == null) { return DefaultChannelPipeline.discardingSink; } return sink; } public void attach(Channel channel, ChannelSink sink) { if (channel == null) { throw new NullPointerException("channel"); } if (sink == null) { throw new NullPointerException("sink"); } if (this.channel != null || this.sink != null) { throw new IllegalStateException("attached already"); } this.channel = channel; this.sink = sink; } public boolean isAttached() { return sink != null; } public void addFirst(String name, ChannelHandler handler) { throw new UnsupportedOperationException(); } public void addLast(String name, ChannelHandler handler) { throw new UnsupportedOperationException(); } public void addBefore(String baseName, String name, ChannelHandler handler) { throw new UnsupportedOperationException(); } public void addAfter(String baseName, String name, ChannelHandler handler) { throw new UnsupportedOperationException(); } public void remove(ChannelHandler handler) { throw new UnsupportedOperationException(); } public ChannelHandler remove(String name) { throw new UnsupportedOperationException(); } public <T extends ChannelHandler> T remove(Class<T> handlerType) { throw new UnsupportedOperationException(); } public ChannelHandler removeFirst() { throw new UnsupportedOperationException(); } public ChannelHandler removeLast() { throw new UnsupportedOperationException(); } public void replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler) { throw new UnsupportedOperationException(); } public ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler) { throw new UnsupportedOperationException(); } public <T extends ChannelHandler> T replace( Class<T> oldHandlerType, String newName, ChannelHandler newHandler) { throw new UnsupportedOperationException(); } private void callBeforeAdd(ChannelHandlerContext ctx) { if (!(ctx.getHandler() instanceof LifeCycleAwareChannelHandler)) { return; } LifeCycleAwareChannelHandler h = (LifeCycleAwareChannelHandler) ctx.getHandler(); try { h.beforeAdd(ctx); } catch (Throwable t) { throw new ChannelHandlerLifeCycleException( h.getClass().getName() + ".beforeAdd() has thrown an exception; not adding.", t); } } private void callAfterAdd(ChannelHandlerContext ctx) { if (!(ctx.getHandler() instanceof LifeCycleAwareChannelHandler)) { return; } LifeCycleAwareChannelHandler h = (LifeCycleAwareChannelHandler) ctx.getHandler(); try { h.afterAdd(ctx); } catch (Throwable t) { boolean removed = false; try { callBeforeRemove(ctx); callAfterRemove(ctx); removed = true; } catch (Throwable t2) { logger.warn("Failed to remove a handler: " + ctx.getName(), t2); } if (removed) { throw new ChannelHandlerLifeCycleException( h.getClass().getName() + ".afterAdd() has thrown an exception; removed.", t); } else { throw new ChannelHandlerLifeCycleException( h.getClass().getName() + ".afterAdd() has thrown an exception; also failed to remove.", t); } } } private void callBeforeRemove(ChannelHandlerContext ctx) { if (!(ctx.getHandler() instanceof LifeCycleAwareChannelHandler)) { return; } LifeCycleAwareChannelHandler h = (LifeCycleAwareChannelHandler) ctx.getHandler(); try { h.beforeRemove(ctx); } catch (Throwable t) { throw new ChannelHandlerLifeCycleException( h.getClass().getName() + ".beforeRemove() has thrown an exception; not removing.", t); } } private void callAfterRemove(ChannelHandlerContext ctx) { if (!(ctx.getHandler() instanceof LifeCycleAwareChannelHandler)) { return; } LifeCycleAwareChannelHandler h = (LifeCycleAwareChannelHandler) ctx.getHandler(); try { h.afterRemove(ctx); } catch (Throwable t) { throw new ChannelHandlerLifeCycleException( h.getClass().getName() + ".afterRemove() has thrown an exception.", t); } } public ChannelHandler getFirst() { return contexts[0].getHandler(); } public ChannelHandler getLast() { return contexts[contexts.length - 1].getHandler(); } public ChannelHandler get(String name) { StaticChannelHandlerContext ctx = name2ctx.get(name); if (ctx == null) { return null; } else { return ctx.getHandler(); } } @SuppressWarnings("unchecked") public <T extends ChannelHandler> T get(Class<T> handlerType) { ChannelHandlerContext ctx = getContext(handlerType); if (ctx == null) { return null; } else { return (T) ctx.getHandler(); } } public ChannelHandlerContext getContext(String name) { if (name == null) { throw new NullPointerException("name"); } return name2ctx.get(name); } public ChannelHandlerContext getContext(ChannelHandler handler) { if (handler == null) { throw new NullPointerException("handler"); } for (StaticChannelHandlerContext ctx: contexts) { if (ctx.getHandler() == handler) { return ctx; } } return null; } public ChannelHandlerContext getContext(Class<? extends ChannelHandler> handlerType) { if (handlerType == null) { throw new NullPointerException("handlerType"); } for (StaticChannelHandlerContext ctx: contexts) { if (handlerType.isAssignableFrom(ctx.getHandler().getClass())) { return ctx; } } return null; } public List<String> getNames() { List<String> list = new ArrayList<String>(); for (StaticChannelHandlerContext ctx: contexts) { list.add(ctx.getName()); } return list; } public Map<String, ChannelHandler> toMap() { Map<String, ChannelHandler> map = new LinkedHashMap<String, ChannelHandler>(); for (StaticChannelHandlerContext ctx: contexts) { map.put(ctx.getName(), ctx.getHandler()); } return map; } /** * Returns the {@link String} representation of this pipeline. */ @Override public String toString() { StringBuilder buf = new StringBuilder(); buf.append(getClass().getSimpleName()); buf.append('{'); for (StaticChannelHandlerContext ctx: contexts) { buf.append('('); buf.append(ctx.getName()); buf.append(" = "); buf.append(ctx.getHandler().getClass().getName()); buf.append(')'); buf.append(", "); } buf.replace(buf.length() - 2, buf.length(), "}"); return buf.toString(); } public void sendUpstream(ChannelEvent e) { StaticChannelHandlerContext head = getActualUpstreamContext(0); if (head == null) { logger.warn( "The pipeline contains no upstream handlers; discarding: " + e); return; } sendUpstream(head, e); } void sendUpstream(StaticChannelHandlerContext ctx, ChannelEvent e) { try { ((ChannelUpstreamHandler) ctx.getHandler()).handleUpstream(ctx, e); } catch (Throwable t) { notifyHandlerException(e, t); } } public void sendDownstream(ChannelEvent e) { StaticChannelHandlerContext tail = getActualDownstreamContext(lastIndex); if (tail == null) { try { getSink().eventSunk(this, e); return; } catch (Throwable t) { notifyHandlerException(e, t); return; } } sendDownstream(tail, e); } void sendDownstream(StaticChannelHandlerContext ctx, ChannelEvent e) { if (e instanceof UpstreamMessageEvent) { throw new IllegalArgumentException("cannot send an upstream event to downstream"); } try { ((ChannelDownstreamHandler) ctx.getHandler()).handleDownstream(ctx, e); } catch (Throwable t) { // Unlike an upstream event, a downstream event usually has an // incomplete future which is supposed to be updated by ChannelSink. // However, if an exception is raised before the event reaches at // ChannelSink, the future is not going to be updated, so we update // here. e.getFuture().setFailure(t); notifyHandlerException(e, t); } } StaticChannelHandlerContext getActualUpstreamContext(int index) { for (int i = index; i < contexts.length; i ++) { StaticChannelHandlerContext ctx = contexts[i]; if (ctx.canHandleUpstream()) { return ctx; } } return null; } StaticChannelHandlerContext getActualDownstreamContext(int index) { for (int i = index; i >= 0; i --) { StaticChannelHandlerContext ctx = contexts[i]; if (ctx.canHandleDownstream()) { return ctx; } } return null; } protected void notifyHandlerException(ChannelEvent e, Throwable t) { if (e instanceof ExceptionEvent) { logger.warn( "An exception was thrown by a user handler " + "while handling an exception event (" + e + ")", t); return; } ChannelPipelineException pe; if (t instanceof ChannelPipelineException) { pe = (ChannelPipelineException) t; } else { pe = new ChannelPipelineException(t); } try { sink.exceptionCaught(this, e, pe); } catch (Exception e1) { logger.warn("An exception was thrown by an exception handler.", e1); } } private final class StaticChannelHandlerContext implements ChannelHandlerContext { private final int index; private final String name; private final ChannelHandler handler; private final boolean canHandleUpstream; private final boolean canHandleDownstream; private volatile Object attachment; StaticChannelHandlerContext( int index, String name, ChannelHandler handler) { if (name == null) { throw new NullPointerException("name"); } if (handler == null) { throw new NullPointerException("handler"); } canHandleUpstream = handler instanceof ChannelUpstreamHandler; canHandleDownstream = handler instanceof ChannelDownstreamHandler; if (!canHandleUpstream && !canHandleDownstream) { throw new IllegalArgumentException( "handler must be either " + ChannelUpstreamHandler.class.getName() + " or " + ChannelDownstreamHandler.class.getName() + '.'); } this.index = index; this.name = name; this.handler = handler; } public Channel getChannel() { return getPipeline().getChannel(); } public ChannelPipeline getPipeline() { return StaticChannelPipeline.this; } public boolean canHandleDownstream() { return canHandleDownstream; } public boolean canHandleUpstream() { return canHandleUpstream; } public ChannelHandler getHandler() { return handler; } public String getName() { return name; } public Object getAttachment() { return attachment; } public void setAttachment(Object attachment) { this.attachment = attachment; } public void sendDownstream(ChannelEvent e) { StaticChannelHandlerContext prev = getActualDownstreamContext(index - 1); if (prev == null) { try { getSink().eventSunk(StaticChannelPipeline.this, e); } catch (Throwable t) { notifyHandlerException(e, t); } } else { StaticChannelPipeline.this.sendDownstream(prev, e); } } public void sendUpstream(ChannelEvent e) { StaticChannelHandlerContext next = getActualUpstreamContext(index + 1); if (next != null) { StaticChannelPipeline.this.sendUpstream(next, e); } } } }