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<>();
89          final List<WriteRequest> filter_write_requests = new ArrayList<>();
90          
91          final 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 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                         synchronized(test_class) {
115                             test_class.scheduleFilterWrite(write_filter, new DefaultWriteRequest(new byte[] {}));
116                             test_class.flushFilterWrite();
117                         }
118                     }
119                 });
120                 
121                 try {
122                     write_scheduler.get();
123                 } catch (Exception e) { }
124             }
125 
126             @Override
127             public void filterWrite(IoSession session, WriteRequest writeRequest) { }
128         };
129         
130         synchronized(test_class) {
131             test_class.scheduleMessageReceived(receive_filter, new byte[] {});
132         }
133         
134         test_class.flushMessageReceived();
135 
136         assertEquals(1, message_received_messages.size());
137         assertEquals(1, filter_write_requests.size());
138     }
139 }