/* * JBoss, Home of Professional Open Source. * * Copyright 2013 Red Hat, Inc. and/or its affiliates, and individual * contributors as indicated by the @author tags. * * Licensed 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.xnio.mock; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.xnio.AbstractIoFuture; import org.xnio.ChannelListener; import org.xnio.ChannelListeners; import org.xnio.ChannelPipe; import org.xnio.FailedIoFuture; import org.xnio.FinishedIoFuture; import org.xnio.IoFuture; import org.xnio.LocalSocketAddress; import org.xnio.MessageConnection; import org.xnio.OptionMap; import org.xnio.StreamConnection; import org.xnio.XnioExecutor; import org.xnio.XnioIoFactory; import org.xnio.XnioIoThread; import org.xnio.XnioWorker; import org.xnio.channels.BoundChannel; import org.xnio.channels.StreamChannel; import org.xnio.channels.StreamSinkChannel; import org.xnio.channels.StreamSourceChannel; /** * {@link XnioExecutor} mock. * * @author <a href="mailto:frainone@redhat.com">Flavia Rainone</a> * */ public class XnioIoThreadMock extends XnioIoThread implements XnioExecutor { private Runnable command; private static final Runnable CLOSE_THREAD = new Runnable() {public void run() {}}; public XnioIoThreadMock(final XnioWorker worker) { super(worker, 0, "XNIO IO THREAD MOCK"); } @Override public void run() { while(true) { Runnable currentCommand; synchronized(this) { currentCommand = command; command = null; } if (currentCommand != null) { currentCommand.run(); } if (currentCommand == CLOSE_THREAD) { break; } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); throw new RuntimeException(e); } } } @Override public void execute(final Runnable command) { assert this.isAlive(): "Before executing a commnad, the thread must be started."; if (Thread.currentThread() == this) { command.run(); return; } final CountDownLatch latch = new CountDownLatch(1); synchronized (this) { while (this.command != null) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); throw new RuntimeException(e); } } assert this.isAlive(): "Thread is closed."; this.command = new Runnable() { public void run() { try { command.run(); } finally { latch.countDown(); } } }; } try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); throw new RuntimeException(e); } } @Override public Key executeAfter(Runnable command, long time, TimeUnit unit) { throw new RuntimeException("Not implemented"); } @Override public Key executeAtInterval(final Runnable command, final long time, final TimeUnit unit) { throw new RuntimeException("Not implemented"); } @Override protected IoFuture<StreamConnection> acceptLocalStreamConnection(LocalSocketAddress destination, ChannelListener<? super StreamConnection> openListener, ChannelListener<? super BoundChannel> bindListener, OptionMap optionMap) { return internalAcceptStream(destination, openListener, bindListener, optionMap, XnioWorkerMock.LOCAL_CHANNEL_INFO); } @Override protected IoFuture<StreamConnection> acceptTcpStreamConnection(InetSocketAddress destination, ChannelListener<? super StreamConnection> openListener, ChannelListener<? super BoundChannel> bindListener, OptionMap optionMap) { return internalAcceptStream(destination, openListener, bindListener, optionMap, XnioWorkerMock.TCP_CHANNEL_INFO); } private IoFuture<StreamConnection> internalAcceptStream(SocketAddress destination, ChannelListener<? super StreamConnection> openListener, ChannelListener<? super BoundChannel> bindListener, OptionMap optionMap, String channelInfo) { switch(getWorkerMock().getConnectBehavior()) { case SUCCEED: { final ConduitMock conduit = new ConduitMock(getWorker(), this); final StreamConnectionMock connection = new StreamConnectionMock(conduit); connection.setPeerAddress(destination); ChannelListeners.invokeChannelListener(connection, bindListener); connection.setOptionMap(optionMap); connection.setInfo(channelInfo); ChannelListeners.invokeChannelListener(connection, openListener); return new FinishedIoFuture<StreamConnection>(connection); } case FAIL: return new FailedIoFuture<StreamConnection>(new IOException("dummy exception")); case CANCEL: return new AbstractIoFuture<StreamConnection>() { { setCancelled(); } }; default: throw new IllegalStateException("Unexpected ConnectBehavior"); } } protected IoFuture<StreamConnection> internalOpenStreamConnection(final SocketAddress bindAddress, final SocketAddress destinationAddress, final ChannelListener<? super StreamConnection> openListener, final ChannelListener<? super BoundChannel> bindListener, final OptionMap optionMap, final String channelInfo) { switch(getWorkerMock().getConnectBehavior()) { case SUCCEED: { final ConduitMock conduit = new ConduitMock(getWorker(), this); final StreamConnectionMock connection = new StreamConnectionMock(conduit); connection.setLocalAddress(bindAddress); connection.setPeerAddress(destinationAddress); ChannelListeners.invokeChannelListener(connection, bindListener); connection.setOptionMap(optionMap); connection.setInfo(channelInfo); ChannelListeners.invokeChannelListener(connection, openListener); return new FinishedIoFuture<StreamConnection>(connection); } case FAIL: return new FailedIoFuture<StreamConnection>(new IOException("dummy exception")); case CANCEL: return new AbstractIoFuture<StreamConnection>() { { setCancelled(); } }; default: throw new IllegalStateException("Unexpected ConnectBehavior"); } } @Override protected IoFuture<StreamConnection> openTcpStreamConnection(InetSocketAddress bindAddress, InetSocketAddress destinationAddress, ChannelListener<? super StreamConnection> openListener, ChannelListener<? super BoundChannel> bindListener, OptionMap optionMap) { return internalConnectStream(bindAddress, destinationAddress, openListener, bindListener, optionMap, XnioWorkerMock.TCP_CHANNEL_INFO); } @Override protected IoFuture<StreamConnection> openLocalStreamConnection(LocalSocketAddress bindAddress, LocalSocketAddress destinationAddress, ChannelListener<? super StreamConnection> openListener, ChannelListener<? super BoundChannel> bindListener, OptionMap optionMap) { return internalConnectStream(bindAddress, destinationAddress, openListener, bindListener, optionMap, XnioWorkerMock.LOCAL_CHANNEL_INFO); } protected IoFuture<StreamConnection> internalConnectStream(final SocketAddress bindAddress, final SocketAddress destinationAddress, final ChannelListener<? super StreamConnection> openListener, final ChannelListener<? super BoundChannel> bindListener, final OptionMap optionMap, final String channelInfo) { switch(getWorkerMock().getConnectBehavior()) { case SUCCEED: { ConduitMock conduit = new ConduitMock(getWorkerMock(), this); StreamConnectionMock connection = new StreamConnectionMock(conduit); connection.setLocalAddress(bindAddress); connection.setPeerAddress(destinationAddress); ChannelListeners.invokeChannelListener(connection, bindListener); conduit.setWorker(getWorker()); connection.setOptionMap(optionMap); connection.setInfo(channelInfo); ChannelListeners.invokeChannelListener(connection, openListener); return new FinishedIoFuture<StreamConnection>(connection); } case FAIL: return new FailedIoFuture<StreamConnection>(new IOException("dummy exception")); case CANCEL: return new AbstractIoFuture<StreamConnection>() { { setCancelled(); } }; default: throw new IllegalStateException("Unexpected ConnectBehavior"); } } @Override protected IoFuture<MessageConnection> openLocalMessageConnection(LocalSocketAddress bindAddress, LocalSocketAddress destinationAddress, ChannelListener<? super MessageConnection> openListener, OptionMap optionMap) { switch(getWorkerMock().getConnectBehavior()) { case SUCCEED: { MessageConnectionMock messageConnection = new MessageConnectionMock(this, bindAddress, destinationAddress, optionMap); messageConnection.setInfo(XnioWorkerMock.LOCAL_CHANNEL_INFO); ChannelListeners.invokeChannelListener(messageConnection, openListener); return new FinishedIoFuture<MessageConnection>(messageConnection); } case FAIL: return new FailedIoFuture<MessageConnection>(new IOException("dummy exception")); case CANCEL: return new AbstractIoFuture<MessageConnection>() { { setCancelled(); } }; default: throw new IllegalStateException("Unexpected ConnectBehavior"); } } protected IoFuture<MessageConnection> acceptLocalMessageConnection(LocalSocketAddress destination, ChannelListener<? super MessageConnection> openListener, ChannelListener<? super BoundChannel> bindListener, OptionMap optionMap) { return openLocalMessageConnection(null, destination, openListener, optionMap); } @Override public ChannelPipe<StreamChannel, StreamChannel> createFullDuplexPipe() throws IOException { throw new RuntimeException("method not implemented by mock"); } @Override public ChannelPipe<StreamConnection, StreamConnection> createFullDuplexPipeConnection(final XnioIoFactory peer) throws IOException { throw new RuntimeException("method not implemented by mock"); } @Override public ChannelPipe<StreamSourceChannel, StreamSinkChannel> createHalfDuplexPipe(final XnioIoFactory peer) throws IOException { throw new RuntimeException("method not implemented by mock"); } private XnioWorkerMock getWorkerMock() { return (XnioWorkerMock) super.getWorker(); } public void closeIoThread() { execute(CLOSE_THREAD); } }