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 */
020
021package org.apache.mina.filter.ssl;
022
023import static org.junit.Assert.assertEquals;
024
025import java.util.ArrayList;
026import java.util.List;
027import java.util.concurrent.ExecutorService;
028import java.util.concurrent.Executors;
029import java.util.concurrent.Future;
030
031import javax.net.ssl.SSLException;
032
033import org.apache.mina.core.filterchain.IoFilter.NextFilter;
034import org.apache.mina.core.session.DummySession;
035import org.apache.mina.core.session.IdleStatus;
036import org.apache.mina.core.session.IoSession;
037import org.apache.mina.core.write.DefaultWriteRequest;
038import org.apache.mina.core.write.WriteRequest;
039import org.junit.Before;
040import org.junit.Test;
041
042/**
043 * A test for DIRMINA-1019
044 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
045 */
046abstract class AbstractNextFilter implements NextFilter {
047    public abstract void messageReceived(IoSession session, Object message);
048    
049    public abstract void filterWrite(IoSession session, WriteRequest writeRequest);
050    
051    // Following are unimplemented as they aren't used in test
052    public void sessionCreated(IoSession session) { }
053
054    public void sessionOpened(IoSession session) { }
055
056    public void sessionClosed(IoSession session) { }
057
058    public void sessionIdle(IoSession session, IdleStatus status) { }
059
060    public void exceptionCaught(IoSession session, Throwable cause) { }
061
062    public void inputClosed(IoSession session) { }
063
064    public void messageSent(IoSession session, WriteRequest writeRequest) { }
065
066    public void filterClose(IoSession session) { }
067
068    public String toString() {
069        return null;
070    }
071};
072
073/**
074 * A test for DIRMINA-1019
075 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
076 */
077public class SslFilterTest {
078    SslHandler test_class;
079    
080    @Before
081    public void init() throws SSLException {
082        test_class = new SslHandler(null, new DummySession());
083    }
084    
085    @Test
086    public void testFlushRaceCondition() {
087        final ExecutorService executor = Executors.newFixedThreadPool(1);
088        final List<Object> message_received_messages = new ArrayList<Object>();
089        final List<WriteRequest> filter_write_requests = new ArrayList<WriteRequest>();
090        
091        final AbstractNextFilter write_filter = new AbstractNextFilter()
092        {
093            @Override
094            public void messageReceived(IoSession session, Object message) { }
095
096            @Override
097            public void filterWrite(IoSession session, WriteRequest writeRequest) {
098                filter_write_requests.add(writeRequest);
099            }
100        };
101        
102        AbstractNextFilter receive_filter = new AbstractNextFilter()
103        {
104            @Override
105            public void messageReceived(IoSession session, Object message) {
106                message_received_messages.add(message);
107                
108                // This is where the race condition occurs. If a thread calls SslHandler.scheduleFilterWrite(),
109                // followed by SslHandler.flushScheduledEvents(), the queued event will not be processed as
110                // the current thread owns the SslHandler.sslLock and has already "dequeued" all the queued
111                // filterWriteEventQueue.
112                Future<?> write_scheduler = executor.submit(new Runnable() {
113                    public void run() {
114                        test_class.scheduleFilterWrite(write_filter, new DefaultWriteRequest(new byte[] {}));
115                        test_class.flushScheduledEvents();
116                    }
117                });
118                
119                try {
120                    write_scheduler.get();
121                } catch (Exception e) { }
122            }
123
124            @Override
125            public void filterWrite(IoSession session, WriteRequest writeRequest) { }
126        };
127        
128        test_class.scheduleMessageReceived(receive_filter, new byte[] {});
129        test_class.flushScheduledEvents();
130        
131        assertEquals(1, message_received_messages.size());
132        assertEquals(1, filter_write_requests.size());
133    }
134}