View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  
21  package org.apache.mina.filter.ssl;
22  
23  import static org.junit.Assert.assertEquals;
24  
25  import java.util.ArrayList;
26  import java.util.List;
27  import java.util.concurrent.ExecutorService;
28  import java.util.concurrent.Executors;
29  import java.util.concurrent.Future;
30  
31  import javax.net.ssl.SSLException;
32  
33  import org.apache.mina.core.filterchain.IoFilter.NextFilter;
34  import org.apache.mina.core.session.DummySession;
35  import org.apache.mina.core.session.IdleStatus;
36  import org.apache.mina.core.session.IoSession;
37  import org.apache.mina.core.write.DefaultWriteRequest;
38  import org.apache.mina.core.write.WriteRequest;
39  import org.junit.Before;
40  import org.junit.Test;
41  
42  /**
43   * A test for DIRMINA-1019
44   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
45   */
46  abstract class AbstractNextFilter implements NextFilter {
47      public abstract void messageReceived(IoSession session, Object message);
48      
49      public abstract void filterWrite(IoSession session, WriteRequest writeRequest);
50      
51      // Following are unimplemented as they aren't used in test
52      public void sessionCreated(IoSession session) { }
53  
54      public void sessionOpened(IoSession session) { }
55  
56      public void sessionClosed(IoSession session) { }
57  
58      public void sessionIdle(IoSession session, IdleStatus status) { }
59  
60      public void exceptionCaught(IoSession session, Throwable cause) { }
61  
62      public void inputClosed(IoSession session) { }
63  
64      public void messageSent(IoSession session, WriteRequest writeRequest) { }
65  
66      public void filterClose(IoSession session) { }
67  
68      public String toString() {
69          return null;
70      }
71  };
72  
73  /**
74   * A test for DIRMINA-1019
75   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
76   */
77  public class SslFilterTest {
78      SslHandler test_class;
79      
80      @Before
81      public void init() throws SSLException {
82          test_class = new SslHandler(null, new DummySession());
83      }
84      
85      @Test
86      public void testFlushRaceCondition() {
87          final ExecutorService executor = Executors.newFixedThreadPool(1);
88          final List<Object> message_received_messages = new ArrayList<Object>();
89          final List<WriteRequest> filter_write_requests = new ArrayList<WriteRequest>();
90          
91          final AbstractNextFilterml#AbstractNextFilter">AbstractNextFilter write_filter = new AbstractNextFilter()
92          {
93              @Override
94              public void messageReceived(IoSession session, Object message) { }
95  
96              @Override
97              public void filterWrite(IoSession session, WriteRequest writeRequest) {
98                  filter_write_requests.add(writeRequest);
99              }
100         };
101         
102         AbstractNextFilter#AbstractNextFilter">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 }