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.apache.mina.filter.FilterEvent;
40  import org.junit.Before;
41  import org.junit.Test;
42  
43  /**
44   * A test for DIRMINA-1019
45   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
46   */
47  abstract class AbstractNextFilter implements NextFilter {
48      public abstract void messageReceived(IoSession session, Object message);
49      
50      public abstract void filterWrite(IoSession session, WriteRequest writeRequest);
51      
52      // Following are unimplemented as they aren't used in test
53      public void sessionCreated(IoSession session) { }
54  
55      public void sessionOpened(IoSession session) { }
56  
57      public void sessionClosed(IoSession session) { }
58  
59      public void sessionIdle(IoSession session, IdleStatus status) { }
60  
61      public void exceptionCaught(IoSession session, Throwable cause) { }
62  
63      public void inputClosed(IoSession session) { }
64  
65      public void messageSent(IoSession session, WriteRequest writeRequest) { }
66  
67      public void filterClose(IoSession session) { }
68  
69      public void event(IoSession session, FilterEvent event) { }
70  
71      public String toString() {
72          return null;
73      }
74  };
75  
76  /**
77   * A test for DIRMINA-1019
78   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
79   */
80  public class SslFilterTest {
81      SslHandler test_class;
82      
83      @Before
84      public void init() throws SSLException {
85          test_class = new SslHandler(null, new DummySession());
86      }
87      
88      @Test
89      public void testFlushRaceCondition() {
90          final ExecutorService executor = Executors.newFixedThreadPool(1);
91          final List<Object> message_received_messages = new ArrayList<Object>();
92          final List<WriteRequest> filter_write_requests = new ArrayList<WriteRequest>();
93          
94          final AbstractNextFilter write_filter = new AbstractNextFilter()
95          {
96              @Override
97              public void messageReceived(IoSession session, Object message) { }
98  
99              @Override
100             public void filterWrite(IoSession session, WriteRequest writeRequest) {
101                 filter_write_requests.add(writeRequest);
102             }
103         };
104         
105         AbstractNextFilter receive_filter = new AbstractNextFilter()
106         {
107             @Override
108             public void messageReceived(IoSession session, Object message) {
109                 message_received_messages.add(message);
110                 
111                 // This is where the race condition occurs. If a thread calls SslHandler.scheduleFilterWrite(),
112                 // followed by SslHandler.flushScheduledEvents(), the queued event will not be processed as
113                 // the current thread owns the SslHandler.sslLock and has already "dequeued" all the queued
114                 // filterWriteEventQueue.
115                 Future<?> write_scheduler = executor.submit(new Runnable() {
116                     public void run() {
117                         test_class.scheduleFilterWrite(write_filter, new DefaultWriteRequest(new byte[] {}));
118                         test_class.flushScheduledEvents();
119                     }
120                 });
121                 
122                 try {
123                     write_scheduler.get();
124                 } catch (Exception e) { }
125             }
126 
127             @Override
128             public void filterWrite(IoSession session, WriteRequest writeRequest) { }
129         };
130         
131         test_class.scheduleMessageReceived(receive_filter, new byte[] {});
132         test_class.flushScheduledEvents();
133         
134         assertEquals(1, message_received_messages.size());
135         assertEquals(1, filter_write_requests.size());
136     }
137 }