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.net.Inet4Address;
23  import java.net.Inet6Address;
24  import java.net.InetAddress;
25  import java.net.InetSocketAddress;
26  import java.net.SocketAddress;
27  import java.nio.channels.DatagramChannel;
28  import java.nio.channels.SelectionKey;
29  import java.nio.channels.Selector;
30  import java.util.Set;
31  import java.util.concurrent.Executor;
32  
33  import org.apache.mina.core.buffer.IoBuffer;
34  import org.apache.mina.core.polling.AbstractPollingConnectionlessIoAcceptor;
35  import org.apache.mina.core.service.IoAcceptor;
36  import org.apache.mina.core.service.IoProcessor;
37  import org.apache.mina.core.service.TransportMetadata;
38  import org.apache.mina.transport.socket.DatagramAcceptor;
39  import org.apache.mina.transport.socket.DatagramSessionConfig;
40  import org.apache.mina.transport.socket.DefaultDatagramSessionConfig;
41  
42  /**
43   * {@link IoAcceptor} for datagram transport (UDP/IP).
44   *
45   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
46   * @org.apache.xbean.XBean
47   */
48  public final class NioDatagramAcceptor extends AbstractPollingConnectionlessIoAcceptor<NioSession, DatagramChannel>
49          implements DatagramAcceptor {
50  
51      private volatile Selector selector;
52  
53      /**
54       * Creates a new instance.
55       */
56      public NioDatagramAcceptor() {
57          super(new DefaultDatagramSessionConfig());
58      }
59  
60      /**
61       * Creates a new instance.
62       */
63      public NioDatagramAcceptor(Executor executor) {
64          super(new DefaultDatagramSessionConfig(), executor);
65      }
66  
67      @Override
68      protected void init() throws Exception {
69          this.selector = Selector.open();
70      }
71  
72      @Override
73      protected void destroy() throws Exception {
74          if (selector != null) {
75              selector.close();
76          }
77      }
78  
79      public TransportMetadata getTransportMetadata() {
80          return NioDatagramSession.METADATA;
81      }
82  
83      @Override
84      public DatagramSessionConfig getSessionConfig() {
85          return (DatagramSessionConfig) super.getSessionConfig();
86      }
87  
88      @Override
89      public InetSocketAddress getLocalAddress() {
90          return (InetSocketAddress) super.getLocalAddress();
91      }
92  
93      @Override
94      public InetSocketAddress getDefaultLocalAddress() {
95          return (InetSocketAddress) super.getDefaultLocalAddress();
96      }
97  
98      public void setDefaultLocalAddress(InetSocketAddress localAddress) {
99          setDefaultLocalAddress((SocketAddress) localAddress);
100     }
101 
102     @Override
103     protected DatagramChannel open(SocketAddress localAddress) throws Exception {
104         final DatagramChannel c = DatagramChannel.open();
105         boolean success = false;
106         try {
107             new NioDatagramSessionConfig(c).setAll(getSessionConfig());
108             c.configureBlocking(false);
109             c.socket().bind(localAddress);
110             c.register(selector, SelectionKey.OP_READ);
111             success = true;
112         } finally {
113             if (!success) {
114                 close(c);
115             }
116         }
117 
118         return c;
119     }
120 
121     @Override
122     protected boolean isReadable(DatagramChannel handle) {
123         SelectionKey key = handle.keyFor(selector);
124 
125         if ((key == null) || (!key.isValid())) {
126             return false;
127         }
128 
129         return key.isReadable();
130     }
131 
132     @Override
133     protected boolean isWritable(DatagramChannel handle) {
134         SelectionKey key = handle.keyFor(selector);
135 
136         if ((key == null) || (!key.isValid())) {
137             return false;
138         }
139 
140         return key.isWritable();
141     }
142 
143     @Override
144     protected SocketAddress localAddress(DatagramChannel handle) throws Exception {
145         InetSocketAddress inetSocketAddress = (InetSocketAddress) handle.socket().getLocalSocketAddress();
146         InetAddress inetAddress = inetSocketAddress.getAddress();
147 
148         if ((inetAddress instanceof Inet6Address) && (((Inet6Address) inetAddress).isIPv4CompatibleAddress())) {
149             // Ugly hack to workaround a problem on linux : the ANY address is always converted to IPV6
150             // even if the original address was an IPV4 address. We do store the two IPV4 and IPV6
151             // ANY address in the map.
152             byte[] ipV6Address = ((Inet6Address) inetAddress).getAddress();
153             byte[] ipV4Address = new byte[4];
154 
155             for (int i = 0; i < 4; i++) {
156                 ipV4Address[i] = ipV6Address[12 + i];
157             }
158 
159             InetAddress inet4Adress = Inet4Address.getByAddress(ipV4Address);
160             return new InetSocketAddress(inet4Adress, inetSocketAddress.getPort());
161         } else {
162             return inetSocketAddress;
163         }
164     }
165 
166     @Override
167     protected NioSession newSession(IoProcessor<NioSession> processor, DatagramChannel handle,
168             SocketAddress remoteAddress) {
169         SelectionKey key = handle.keyFor(selector);
170 
171         if ((key == null) || (!key.isValid())) {
172             return null;
173         }
174 
175         NioDatagramSession newSession = new NioDatagramSession(this, handle, processor, remoteAddress);
176         newSession.setSelectionKey(key);
177 
178         return newSession;
179     }
180 
181     @Override
182     protected SocketAddress receive(DatagramChannel handle, IoBuffer buffer) throws Exception {
183         return handle.receive(buffer.buf());
184     }
185 
186     @Override
187     protected int select() throws Exception {
188         return selector.select();
189     }
190 
191     @Override
192     protected int select(long timeout) throws Exception {
193         return selector.select(timeout);
194     }
195 
196     @Override
197     protected Set<SelectionKey> selectedHandles() {
198         return selector.selectedKeys();
199     }
200 
201     @Override
202     protected int send(NioSession session, IoBuffer buffer, SocketAddress remoteAddress) throws Exception {
203         return ((DatagramChannel) session.getChannel()).send(buffer.buf(), remoteAddress);
204     }
205 
206     @Override
207     protected void setInterestedInWrite(NioSession session, boolean isInterested) throws Exception {
208         SelectionKey key = session.getSelectionKey();
209 
210         if (key == null) {
211             return;
212         }
213 
214         int newInterestOps = key.interestOps();
215 
216         if (isInterested) {
217             newInterestOps |= SelectionKey.OP_WRITE;
218             //newInterestOps &= ~SelectionKey.OP_READ;
219         } else {
220             newInterestOps &= ~SelectionKey.OP_WRITE;
221             //newInterestOps |= SelectionKey.OP_READ;
222         }
223 
224         key.interestOps(newInterestOps);
225     }
226 
227     @Override
228     protected void close(DatagramChannel handle) throws Exception {
229         SelectionKey key = handle.keyFor(selector);
230 
231         if (key != null) {
232             key.cancel();
233         }
234 
235         handle.disconnect();
236         handle.close();
237     }
238 
239     @Override
240     protected void wakeup() {
241         selector.wakeup();
242     }
243 }