001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.mina.filter.keepalive;
021
022import static org.apache.mina.filter.keepalive.KeepAliveRequestTimeoutHandler.EXCEPTION;
023import static org.junit.Assert.assertFalse;
024import static org.junit.Assert.assertNotNull;
025
026import java.net.InetSocketAddress;
027import java.util.concurrent.atomic.AtomicBoolean;
028
029import org.apache.mina.core.buffer.IoBuffer;
030import org.apache.mina.core.future.ConnectFuture;
031import org.apache.mina.core.service.IoHandlerAdapter;
032import org.apache.mina.core.session.IdleStatus;
033import org.apache.mina.core.session.IoSession;
034import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
035import org.apache.mina.transport.socket.nio.NioSocketConnector;
036import org.junit.After;
037import org.junit.Before;
038import org.junit.Test;
039
040/**
041 * Tests {@link KeepAliveFilter} used by the connector with different
042 * interested {@link IdleStatus}es.
043 *
044 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
045 */
046public class KeepAliveFilterTest {
047    // Constants -----------------------------------------------------
048    static final IoBuffer PING = IoBuffer.wrap(new byte[] { 1 });
049
050    static final IoBuffer PONG = IoBuffer.wrap(new byte[] { 2 });
051
052    private static final int INTERVAL = 2;
053
054    private static final int TIMEOUT = 1;
055
056    private int port;
057
058    private NioSocketAcceptor acceptor;
059
060    @Before
061    public void setUp() throws Exception {
062        acceptor = new NioSocketAcceptor();
063        KeepAliveMessageFactory factory = new ServerFactory();
064        KeepAliveFilter filter = new KeepAliveFilter(factory, IdleStatus.BOTH_IDLE);
065        acceptor.getFilterChain().addLast("keep-alive", filter);
066        acceptor.setHandler(new IoHandlerAdapter());
067        acceptor.setDefaultLocalAddress(new InetSocketAddress(0));
068        acceptor.bind();
069        port = acceptor.getLocalAddress().getPort();
070    }
071
072    @After
073    public void tearDown() throws Exception {
074        acceptor.unbind();
075        acceptor.dispose();
076    }
077
078    @Test
079    public void testKeepAliveFilterForReaderIdle() throws Exception {
080        keepAliveFilterForIdleStatus(IdleStatus.READER_IDLE);
081    }
082
083    @Test
084    public void testKeepAliveFilterForBothIdle() throws Exception {
085        keepAliveFilterForIdleStatus(IdleStatus.BOTH_IDLE);
086    }
087
088    @Test
089    public void testKeepAliveFilterForWriterIdle() throws Exception {
090        keepAliveFilterForIdleStatus(IdleStatus.WRITER_IDLE);
091    }
092
093    // Package protected ---------------------------------------------
094
095    // Protected -----------------------------------------------------
096
097    // Private -------------------------------------------------------
098
099    private void keepAliveFilterForIdleStatus(IdleStatus status) throws Exception {
100        NioSocketConnector connector = new NioSocketConnector();
101        KeepAliveFilter filter = new KeepAliveFilter(new ClientFactory(), status, EXCEPTION, INTERVAL, TIMEOUT);
102        filter.setForwardEvent(true);
103        connector.getFilterChain().addLast("keep-alive", filter);
104
105        final AtomicBoolean gotException = new AtomicBoolean(false);
106        connector.setHandler(new IoHandlerAdapter() {
107            @Override
108            public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
109                //cause.printStackTrace();
110                gotException.set(true);
111            }
112
113            @Override
114            public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
115                // Do nothing
116            }
117        });
118
119        ConnectFuture future = connector.connect(new InetSocketAddress("127.0.0.1", port)).awaitUninterruptibly();
120        IoSession session = future.getSession();
121        assertNotNull(session);
122
123        Thread.sleep((INTERVAL + TIMEOUT + 1) * 1000);
124
125        assertFalse("got an exception on the client", gotException.get());
126
127        session.close(true);
128        connector.dispose();
129    }
130
131    static boolean checkRequest(IoBuffer message) {
132        IoBuffer buff = message;
133        boolean check = buff.get() == 1;
134        buff.rewind();
135        return check;
136    }
137
138    static boolean checkResponse(IoBuffer message) {
139        IoBuffer buff = message;
140        boolean check = buff.get() == 2;
141        buff.rewind();
142        return check;
143    }
144
145    // Inner classes -------------------------------------------------
146    private final class ServerFactory implements KeepAliveMessageFactory {
147        /**
148         * Default constructor
149         */
150        public ServerFactory() {
151            super();
152        }
153
154        public Object getRequest(IoSession session) {
155            return null;
156        }
157
158        public Object getResponse(IoSession session, Object request) {
159            return PONG.duplicate();
160        }
161
162        public boolean isRequest(IoSession session, Object message) {
163            if (message instanceof IoBuffer) {
164                return checkRequest((IoBuffer) message);
165            }
166            return false;
167        }
168
169        public boolean isResponse(IoSession session, Object message) {
170            if (message instanceof IoBuffer) {
171                return checkResponse((IoBuffer) message);
172            }
173            return false;
174        }
175    }
176
177    private final class ClientFactory implements KeepAliveMessageFactory {
178        /**
179         * Default constructor
180         */
181        public ClientFactory() {
182            super();
183        }
184
185        public Object getRequest(IoSession session) {
186            return PING.duplicate();
187        }
188
189        public Object getResponse(IoSession session, Object request) {
190            return null;
191        }
192
193        public boolean isRequest(IoSession session, Object message) {
194            if (message instanceof IoBuffer) {
195                return checkRequest((IoBuffer) message);
196            }
197            return false;
198        }
199
200        public boolean isResponse(IoSession session, Object message) {
201            if (message instanceof IoBuffer) {
202                return checkResponse((IoBuffer) message);
203            }
204            return false;
205        }
206    }
207}