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  package org.apache.mina.transport.socket.nio;
21  
22  import java.io.IOException;
23  import java.nio.channels.ByteChannel;
24  import java.nio.channels.DatagramChannel;
25  import java.nio.channels.SelectableChannel;
26  import java.nio.channels.SelectionKey;
27  import java.nio.channels.Selector;
28  import java.nio.channels.SocketChannel;
29  import java.util.Iterator;
30  import java.util.Set;
31  import java.util.concurrent.Executor;
32  
33  import org.apache.mina.core.RuntimeIoException;
34  import org.apache.mina.core.buffer.IoBuffer;
35  import org.apache.mina.core.file.FileRegion;
36  import org.apache.mina.core.polling.AbstractPollingIoProcessor;
37  import org.apache.mina.core.session.SessionState;
38  
39  /**
40   * TODO Add documentation
41   * 
42   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
43   */
44  public final class NioProcessor extends AbstractPollingIoProcessor<NioSession> {
45      /** The selector associated with this processor */
46      private Selector selector;
47  
48      /**
49       * 
50       * Creates a new instance of NioProcessor.
51       * 
52       * @param executor
53       */
54      public NioProcessor(Executor executor) {
55          super(executor);
56          
57          try {
58              // Open a new selector
59              selector = Selector.open();
60          } catch (IOException e) {
61              throw new RuntimeIoException("Failed to open a selector.", e);
62          }
63      }
64  
65      @Override
66      protected void dispose0() throws Exception {
67          selector.close();
68      }
69  
70      @Override
71      protected int select(long timeout) throws Exception {
72          return selector.select(timeout);
73      }
74  
75      @Override
76      protected int select() throws Exception {
77          return selector.select();
78      }
79  
80      @Override
81      protected boolean isSelectorEmpty() {
82          return selector.keys().isEmpty();
83      }
84  
85      @Override
86      protected void wakeup() {
87          wakeupCalled.getAndSet(true);
88          selector.wakeup();
89      }
90  
91      @Override
92      protected Iterator<NioSession> allSessions() {
93          return new IoSessionIterator(selector.keys());
94      }
95  
96      @SuppressWarnings("synthetic-access")
97      @Override
98      protected Iterator<NioSession> selectedSessions() {
99          return new IoSessionIterator(selector.selectedKeys());
100     }
101 
102     @Override
103     protected void init(NioSession session) throws Exception {
104         SelectableChannel ch = (SelectableChannel) session.getChannel();
105         ch.configureBlocking(false);
106         session.setSelectionKey(ch.register(selector, SelectionKey.OP_READ,
107                 session));
108     }
109 
110     @Override
111     protected void destroy(NioSession session) throws Exception {
112         ByteChannel ch = session.getChannel();
113         SelectionKey key = session.getSelectionKey();
114         if (key != null) {
115             key.cancel();
116         }
117         ch.close();
118     }
119 
120     /**
121      * In the case we are using the java select() method, this method is used to
122      * trash the buggy selector and create a new one, registering all the
123      * sockets on it.
124      */
125     protected void registerNewSelector() throws IOException {
126         synchronized (selector) {
127             Set<SelectionKey> keys = selector.keys();
128 
129             // Open a new selector
130             Selector newSelector = Selector.open();
131 
132             // Loop on all the registered keys, and register them on the new selector
133             for (SelectionKey key : keys) {
134                 SelectableChannel ch = key.channel();
135                 
136                 // Don't forget to attache the session, and back !
137                 NioSession session = (NioSession)key.attachment();
138                 SelectionKey newKey = ch.register(newSelector, key.interestOps(), session);
139                 session.setSelectionKey( newKey );
140             }
141 
142             // Now we can close the old selector and switch it
143             selector.close();
144             selector = newSelector;
145         }
146     }
147 
148     /**
149      * {@inheritDoc}
150      */
151     protected boolean isBrokenConnection() throws IOException {
152         // A flag set to true if we find a broken session
153         boolean brokenSession = false;
154 
155         synchronized (selector) {
156             // Get the selector keys
157             Set<SelectionKey> keys = selector.keys();
158 
159             // Loop on all the keys to see if one of them
160             // has a closed channel
161             for (SelectionKey key : keys) {
162                 SelectableChannel channel = key.channel();
163 
164                 if ((((channel instanceof DatagramChannel) && ((DatagramChannel) channel)
165                         .isConnected()))
166                         || ((channel instanceof SocketChannel) && ((SocketChannel) channel)
167                                 .isConnected())) {
168                     // The channel is not connected anymore. Cancel
169                     // the associated key then.
170                     key.cancel();
171 
172                     // Set the flag to true to avoid a selector switch
173                     brokenSession = true;
174                 }
175             }
176         }
177 
178         return brokenSession;
179     }
180 
181     /**
182      * {@inheritDoc}
183      */
184     @Override
185     protected SessionState getState(NioSession session) {
186         SelectionKey key = session.getSelectionKey();
187 
188         if (key == null) {
189             // The channel is not yet registred to a selector
190             return SessionState.OPENING;
191         }
192 
193         if (key.isValid()) {
194             // The session is opened
195             return SessionState.OPENED;
196         } else {
197             // The session still as to be closed
198             return SessionState.CLOSING;
199         }
200     }
201 
202     @Override
203     protected boolean isReadable(NioSession session) {
204         SelectionKey key = session.getSelectionKey();
205         return key.isValid() && key.isReadable();
206     }
207 
208     @Override
209     protected boolean isWritable(NioSession session) {
210         SelectionKey key = session.getSelectionKey();
211         return key.isValid() && key.isWritable();
212     }
213 
214     @Override
215     protected boolean isInterestedInRead(NioSession session) {
216         SelectionKey key = session.getSelectionKey();
217         return key.isValid() && (key.interestOps() & SelectionKey.OP_READ) != 0;
218     }
219 
220     @Override
221     protected boolean isInterestedInWrite(NioSession session) {
222         SelectionKey key = session.getSelectionKey();
223         return key.isValid()
224                 && (key.interestOps() & SelectionKey.OP_WRITE) != 0;
225     }
226 
227     /**
228      * {@inheritDoc}
229      */
230     @Override
231     protected void setInterestedInRead(NioSession session, boolean isInterested)
232             throws Exception {
233         SelectionKey key = session.getSelectionKey();
234         int oldInterestOps = key.interestOps();
235         int newInterestOps = oldInterestOps;
236 
237         if (isInterested) {
238             newInterestOps |= SelectionKey.OP_READ;
239         } else {
240             newInterestOps &= ~SelectionKey.OP_READ;
241         }
242 
243         if (oldInterestOps != newInterestOps) {
244             key.interestOps(newInterestOps);
245         }
246     }
247 
248     /**
249      * {@inheritDoc}
250      */
251     @Override
252     protected void setInterestedInWrite(NioSession session, boolean isInterested)
253             throws Exception {
254         SelectionKey key = session.getSelectionKey();
255 
256         if (key == null) {
257             return;
258         }
259         
260         int newInterestOps = key.interestOps();
261 
262         if (isInterested) {
263             newInterestOps |= SelectionKey.OP_WRITE;
264             //newInterestOps &= ~SelectionKey.OP_READ;
265         } else {
266             newInterestOps &= ~SelectionKey.OP_WRITE;
267             //newInterestOps |= SelectionKey.OP_READ;
268         }
269 
270         key.interestOps(newInterestOps);
271     }
272 
273     @Override
274     protected int read(NioSession session, IoBuffer buf) throws Exception {
275         ByteChannel channel = session.getChannel();
276         
277         return session.getChannel().read(buf.buf());
278     }
279 
280     @Override
281     protected int write(NioSession session, IoBuffer buf, int length)
282             throws Exception {
283         if (buf.remaining() <= length) {
284             return session.getChannel().write(buf.buf());
285         }
286 
287         int oldLimit = buf.limit();
288         buf.limit(buf.position() + length);
289         try {
290             return session.getChannel().write(buf.buf());
291         } finally {
292             buf.limit(oldLimit);
293         }
294     }
295 
296     @Override
297     protected int transferFile(NioSession session, FileRegion region, int length)
298             throws Exception {
299         try {
300             return (int) region.getFileChannel().transferTo(
301                     region.getPosition(), length, session.getChannel());
302         } catch (IOException e) {
303             // Check to see if the IOException is being thrown due to
304             // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5103988
305             String message = e.getMessage();
306             if (message != null && message.contains("temporarily unavailable")) {
307                 return 0;
308             }
309 
310             throw e;
311         }
312     }
313 
314     /**
315      * An encapsulating iterator around the {@link Selector#selectedKeys()} or
316      * the {@link Selector#keys()} iterator;
317      */
318     protected static class IoSessionIterator<NioSession> implements
319             Iterator<NioSession> {
320         private final Iterator<SelectionKey> iterator;
321 
322         /**
323          * Create this iterator as a wrapper on top of the selectionKey Set.
324          * 
325          * @param keys
326          *            The set of selected sessions
327          */
328         private IoSessionIterator(Set<SelectionKey> keys) {
329             iterator = keys.iterator();
330         }
331 
332         /**
333          * {@inheritDoc}
334          */
335         public boolean hasNext() {
336             return iterator.hasNext();
337         }
338 
339         /**
340          * {@inheritDoc}
341          */
342         public NioSession next() {
343             SelectionKey key = iterator.next();
344             NioSession nioSession = (NioSession) key.attachment();
345             return nioSession;
346         }
347 
348         /**
349          * {@inheritDoc}
350          */
351         public void remove() {
352             iterator.remove();
353         }
354     }
355 }