/* * 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 vnet.sms.gateway.nettytest.embedded; import static org.jboss.netty.channel.Channels.close; import static org.jboss.netty.channel.Channels.disconnect; import static org.jboss.netty.channel.Channels.fireChannelBound; import static org.jboss.netty.channel.Channels.fireChannelClosed; import static org.jboss.netty.channel.Channels.fireChannelConnected; import static org.jboss.netty.channel.Channels.fireChannelDisconnected; import static org.jboss.netty.channel.Channels.fireChannelOpen; import static org.jboss.netty.channel.Channels.fireChannelUnbound; import static org.jboss.netty.channel.Channels.fireMessageReceived; import static org.jboss.netty.channel.Channels.unbind; import static org.jboss.netty.channel.Channels.write; import java.util.concurrent.atomic.AtomicReference; import org.jboss.netty.buffer.ChannelBufferFactory; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelEvent; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.ChannelHandler; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelPipelineException; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.ChannelSink; import org.jboss.netty.channel.ChannelState; import org.jboss.netty.channel.ChannelStateEvent; import org.jboss.netty.channel.ChannelUpstreamHandler; import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.DefaultChannelPipeline; import org.jboss.netty.channel.DefaultExceptionEvent; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.LifeCycleAwareChannelHandler; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.SimpleChannelUpstreamHandler; public class DefaultChannelPipelineEmbedder implements ChannelPipelineEmbedder { private final Channel channel; private final ChannelPipeline pipeline; private final ChannelEventRecordingChannelSink sink = new ChannelEventRecordingChannelSink(); private final DefaultChannelEvents downstreamChannelEvents = new DefaultChannelEvents(); private final DefaultMessageEvents downstreamMessageEvents = new DefaultMessageEvents(); private final DefaultChannelEvents upstreamChannelEvents = new DefaultChannelEvents(); private final DefaultMessageEvents upstreamMessageEvents = new DefaultMessageEvents(); private final AtomicReference<Throwable> thrownException = new AtomicReference<Throwable>(); // ------------------------------------------------------------------------ // Constructors // ------------------------------------------------------------------------ public DefaultChannelPipelineEmbedder(final ChannelHandler... handlers) { this.pipeline = new DefaultChannelPipeline(); this.channel = new EmbeddedChannel(this.pipeline, this.sink); configurePipeline(handlers); } public DefaultChannelPipelineEmbedder( final ChannelPipelineFactory channelPipelineFactory) throws Exception { this.pipeline = channelPipelineFactory.getPipeline(); this.channel = new EmbeddedChannel(this.pipeline, this.sink); configurePipeline(this.pipeline); } public DefaultChannelPipelineEmbedder( final ChannelBufferFactory bufferFactory, final ChannelHandler... handlers) { this(handlers); getChannel().getConfig().setBufferFactory(bufferFactory); } public DefaultChannelPipelineEmbedder( final ChannelBufferFactory bufferFactory, final ChannelPipelineFactory channelPipelineFactory) throws Exception { this(channelPipelineFactory); getChannel().getConfig().setBufferFactory(bufferFactory); } // ------------------------------------------------------------------------ // Internal // ------------------------------------------------------------------------ private void configurePipeline(final ChannelHandler... handlers) { if (handlers == null) { throw new NullPointerException("handlers"); } if (handlers.length == 0) { throw new IllegalArgumentException( "handlers should contain at least one " + ChannelHandler.class.getSimpleName() + '.'); } try { for (int i = 0; i < handlers.length; i++) { final ChannelHandler h = handlers[i]; if (h == null) { throw new NullPointerException("handlers[" + i + "]"); } if (h instanceof LifeCycleAwareChannelHandler) { LifeCycleAwareChannelHandler.class.cast(h).beforeAdd( new EmbeddedChannelHandlerContext(h)); } this.pipeline.addLast(String.valueOf(i), h); if (h instanceof LifeCycleAwareChannelHandler) { LifeCycleAwareChannelHandler.class.cast(h).afterAdd( new EmbeddedChannelHandlerContext(h)); } } this.pipeline.addLast("EXCEPTIONS-RECORDER", new ExceptionRecordingUpstreamChannelHandler()); this.pipeline.addLast("SENT-MESSAGES-RECORDER", new UpstreamChannelEventsRecordingChannelHandler()); } catch (final Exception e) { throw new RuntimeException(e); } } private void configurePipeline(final ChannelPipeline pipe) { pipe.addLast("EXCEPTIONS-RECORDER", new ExceptionRecordingUpstreamChannelHandler()); pipe.addLast("SENT-MESSAGES-RECORDER", new UpstreamChannelEventsRecordingChannelHandler()); } // ------------------------------------------------------------------------ // Open, bind, connect // ------------------------------------------------------------------------ @Override public void openChannel() throws Throwable { if (!this.channel.isOpen()) { fireChannelOpen(this.channel); } if (this.thrownException.get() != null) { throw this.thrownException.getAndSet(null); } } @Override public void bindChannel() throws Throwable { openChannel(); if (!this.channel.isBound()) { fireChannelBound(this.channel, this.channel.getLocalAddress()); } if (this.thrownException.get() != null) { throw this.thrownException.getAndSet(null); } } @Override public void connectChannel() throws Throwable { openChannel(); bindChannel(); if (!this.channel.isConnected()) { fireChannelConnected(this.channel, this.channel.getRemoteAddress()); } if (this.thrownException.get() != null) { throw this.thrownException.getAndSet(null); } } // ------------------------------------------------------------------------ // Receiving messages // ------------------------------------------------------------------------ @Override public boolean receive(final Object input) throws Throwable { fireMessageReceived(getChannel(), input); if (this.thrownException.get() != null) { throw this.thrownException.getAndSet(null); } return !this.upstreamChannelEvents.isEmpty(); } @Override public void injectUpstreamChannelEvent(final ChannelEvent e) { getChannel().getPipeline().sendUpstream(e); } @Override public ChannelEvents upstreamChannelEvents() { return this.upstreamChannelEvents; } @Override public MessageEvents upstreamMessageEvents() { return this.upstreamMessageEvents; } // ------------------------------------------------------------------------ // Sending messages // ------------------------------------------------------------------------ @Override public boolean send(final Object input) throws Throwable { write(getChannel(), input).setSuccess(); if (this.thrownException.get() != null) { throw this.thrownException.getAndSet(null); } return !this.downstreamChannelEvents.isEmpty(); } @Override public ChannelEvents downstreamChannelEvents() { return this.downstreamChannelEvents; } @Override public MessageEvents downstreamMessageEvents() { return this.downstreamMessageEvents; } // ------------------------------------------------------------------------ // Disconnect, unbind, close // ------------------------------------------------------------------------ @Override public void disconnectChannel() throws Throwable { disconnect(this.channel); if (this.channel.isConnected()) { fireChannelDisconnected(this.channel); } if (this.thrownException.get() != null) { throw this.thrownException.getAndSet(null); } } @Override public void unbindChannel() throws Throwable { disconnectChannel(); unbind(this.channel); if (this.channel.isBound()) { fireChannelUnbound(this.channel); } if (this.thrownException.get() != null) { throw this.thrownException.getAndSet(null); } } @Override public void closeChannel() throws Throwable { disconnectChannel(); unbindChannel(); close(this.channel); if (this.channel.isOpen()) { fireChannelClosed(this.channel); } if (this.thrownException.get() != null) { throw this.thrownException.getAndSet(null); } } // ------------------------------------------------------------------------ // Misc // ------------------------------------------------------------------------ @Override public ChannelPipeline getPipeline() { return this.pipeline; } /** * Returns the virtual {@link Channel} which will be used as a mock during * encoding and decoding. */ @Override public final Channel getChannel() { return this.channel; } // ------------------------------------------------------------------------ // Inner classes // ------------------------------------------------------------------------ private final class EmbeddedChannelHandlerContext implements ChannelHandlerContext { private final ChannelHandler h; EmbeddedChannelHandlerContext(final ChannelHandler h) { this.h = h; } @Override public void setAttachment(final Object attachment) { } @Override public void sendUpstream(final ChannelEvent e) { } @Override public void sendDownstream(final ChannelEvent e) { } @Override public ChannelPipeline getPipeline() { return getChannel().getPipeline(); } @Override public String getName() { return null; } @Override public ChannelHandler getHandler() { return this.h; } @Override public Channel getChannel() { return DefaultChannelPipelineEmbedder.this.channel; } @Override public Object getAttachment() { return null; } @Override public boolean canHandleUpstream() { return false; } @Override public boolean canHandleDownstream() { return false; } } private final class UpstreamChannelEventsRecordingChannelHandler implements ChannelUpstreamHandler { UpstreamChannelEventsRecordingChannelHandler() { } @Override public void handleUpstream(final ChannelHandlerContext ctx, final ChannelEvent e) throws Exception { if (e instanceof ChannelStateEvent) { final ChannelStateEvent event = (ChannelStateEvent) e; final EmbeddedChannel channel = (EmbeddedChannel) event .getChannel(); final ChannelState state = event.getState(); final Object value = event.getValue(); switch (state) { case OPEN: if (Boolean.FALSE.equals(value)) { channel.close(); } else { channel.setOpen(); } break; case BOUND: if (value != null) { channel.setBound(); } else { channel.unbind(); } break; case CONNECTED: if (value != null) { channel.setConnected(); } else { channel.disconnect(); } break; } DefaultChannelPipelineEmbedder.this.upstreamChannelEvents .onEvent(e); } else if (e instanceof ExceptionEvent) { // Noop } else if (e instanceof MessageEvent) { DefaultChannelPipelineEmbedder.this.upstreamMessageEvents .onEvent((MessageEvent) e); } else { DefaultChannelPipelineEmbedder.this.upstreamChannelEvents .onEvent(e); } ctx.sendUpstream(e); } } private final class ExceptionRecordingUpstreamChannelHandler extends SimpleChannelUpstreamHandler { ExceptionRecordingUpstreamChannelHandler() { } @Override public void exceptionCaught(final ChannelHandlerContext ctx, final ExceptionEvent e) throws Exception { DefaultChannelPipelineEmbedder.this.thrownException.set(e .getCause()); DefaultChannelPipelineEmbedder.this.upstreamChannelEvents .onExceptionEvent(e); DefaultChannelPipelineEmbedder.this.upstreamMessageEvents .onExceptionEvent(e); } } private final class ChannelEventRecordingChannelSink implements ChannelSink { ChannelEventRecordingChannelSink() { } @Override public void eventSunk(final ChannelPipeline pipeline, final ChannelEvent e) { handleEvent(e); } private void handleEvent(final ChannelEvent e) { if (e instanceof ExceptionEvent) { DefaultChannelPipelineEmbedder.this.thrownException .set(((ExceptionEvent) e).getCause()); DefaultChannelPipelineEmbedder.this.downstreamChannelEvents .onExceptionEvent((ExceptionEvent) e); DefaultChannelPipelineEmbedder.this.downstreamMessageEvents .onExceptionEvent((ExceptionEvent) e); } else if (e instanceof MessageEvent) { DefaultChannelPipelineEmbedder.this.downstreamMessageEvents .onEvent((MessageEvent) e); } else { DefaultChannelPipelineEmbedder.this.downstreamChannelEvents .onEvent(e); } } @Override public void exceptionCaught(final ChannelPipeline pipeline, final ChannelEvent e, final ChannelPipelineException cause) throws Exception { final Throwable actualCause = cause.getCause() != null ? cause .getCause() : cause; DefaultChannelPipelineEmbedder.this.thrownException .set(actualCause); DefaultChannelPipelineEmbedder.this.downstreamChannelEvents .onExceptionEvent(new DefaultExceptionEvent(e.getChannel(), actualCause)); } @Override public ChannelFuture execute(final ChannelPipeline pipeline, final Runnable task) { try { task.run(); return Channels.succeededFuture(pipeline.getChannel()); } catch (final Throwable t) { return Channels.failedFuture(pipeline.getChannel(), t); } } } }