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.nio;
21  
22  import java.io.IOException;
23  import java.net.SocketAddress;
24  import java.nio.ByteBuffer;
25  import java.nio.channels.DatagramChannel;
26  
27  import org.apache.mina.api.IoService;
28  import org.apache.mina.service.idlechecker.IdleChecker;
29  import org.apache.mina.session.WriteRequest;
30  import org.apache.mina.transport.udp.UdpSessionConfig;
31  import org.slf4j.Logger;
32  import org.slf4j.LoggerFactory;
33  
34  /**
35   * A UDP session based on NIO
36   * 
37   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
38   */
39  public class NioUdpSession extends AbstractNioSession implements SelectorListener {
40  
41      private static final Logger LOG = LoggerFactory.getLogger(NioUdpSession.class);
42  
43      private static final boolean IS_DEBUG = LOG.isDebugEnabled();
44  
45      private final SocketAddress localAddress;
46  
47      private final SocketAddress remoteAddress;
48  
49      /**
50       * The selector loop in charge of generating read/write events for this session Used only for UDP client session.
51       */
52      private SelectorLoop selectorLoop = null;
53  
54      /** the socket configuration */
55      private final UdpSessionConfig configuration;
56  
57      /**
58       * For server handled UDP sessions
59       */
60      /* No qualifier */NioUdpSession(IoService service, IdleChecker idleChecker, DatagramChannel datagramChannel,
61              SocketAddress localAddress, SocketAddress remoteAddress) {
62          super(service, datagramChannel, idleChecker);
63          this.localAddress = localAddress;
64          this.remoteAddress = remoteAddress;
65          this.config = service.getSessionConfig();
66          this.configuration = (UdpSessionConfig) this.config;
67      }
68  
69      /**
70       * For client handled UDP sessions
71       */
72      /* No qualifier */NioUdpSession(IoService service, IdleChecker idleChecker, DatagramChannel datagramChannel,
73              SocketAddress localAddress, SocketAddress remoteAddress, NioSelectorLoop selectorLoop) {
74          super(service, datagramChannel, idleChecker);
75          this.selectorLoop = selectorLoop;
76          this.localAddress = localAddress;
77          this.remoteAddress = remoteAddress;
78          this.config = service.getSessionConfig();
79          this.configuration = (UdpSessionConfig) this.config;
80      }
81  
82      /**
83       * {@inheritDoc}
84       */
85      @Override
86      protected void channelClose() {
87          LOG.debug("channelClose");
88          // No inner socket to close for UDP server, but some for UDP client
89          if (channel != null) {
90              try {
91                  selectorLoop.unregister(this, channel);
92                  channel.close();
93              } catch (final IOException e) {
94                  LOG.error("Exception while closing the channel : ", e);
95                  processException(e);
96              }
97          }
98          processSessionClosed();
99      }
100 
101     /**
102      * {@inheritDoc}
103      */
104     @Override
105     public void flushWriteQueue() {
106         // register for write
107         if (selectorLoop != null) {
108             selectorLoop.modifyRegistration(false, !isReadSuspended(), true, this, channel, true);
109         }
110     }
111 
112     /**
113      * {@inheritDoc}
114      */
115     @Override
116     public SocketAddress getRemoteAddress() {
117         return remoteAddress;
118     }
119 
120     /**
121      * {@inheritDoc}
122      */
123     @Override
124     public SocketAddress getLocalAddress() {
125         return localAddress;
126     }
127 
128     /**
129      * {@inheritDoc}
130      */
131     /*
132      * @Override public IoFuture<Void> close(boolean immediately) { switch (state) { case CREATED:
133      * LOG.error("Session {} not opened", this); throw new IllegalStateException("cannot close an not opened session");
134      * case CONNECTED: case CLOSING: if (immediately) { state = SessionState.CLOSED; } else { // we wait for the write
135      * queue to be depleted state = SessionState.CLOSING; } break; case CLOSED: LOG.warn("Already closed session {}",
136      * this); break; default: throw new IllegalStateException("not implemented session state : " + state); } return
137      * closeFuture; }
138      */
139 
140     /**
141      * {@inheritDoc}
142      */
143     @Override
144     public void suspendRead() {
145         // TODO
146         throw new IllegalStateException("not implemented");
147     }
148 
149     /**
150      * {@inheritDoc}
151      */
152     @Override
153     public void suspendWrite() {
154         // TODO
155         throw new IllegalStateException("not implemented");
156     }
157 
158     /**
159      * {@inheritDoc}
160      */
161     @Override
162     public void resumeRead() {
163         // TODO
164         throw new IllegalStateException("not implemented");
165     }
166 
167     /**
168      * {@inheritDoc}
169      */
170     @Override
171     public void resumeWrite() {
172         // TODO
173         throw new IllegalStateException("not implemented");
174     }
175 
176     /**
177      * {@inheritDoc}
178      */
179     @Override
180     public boolean isReadSuspended() {
181         // TODO
182         return false;
183     }
184 
185     /**
186      * {@inheritDoc}
187      */
188     @Override
189     public boolean isWriteSuspended() {
190         // TODO
191         return false;
192     }
193 
194     /**
195      * {@inheritDoc}
196      */
197     @Override
198     public UdpSessionConfig getConfig() {
199         return configuration;
200     }
201 
202     /**
203      * Called when the session received a datagram.
204      * 
205      * @param readBuffer the received datagram
206      */
207     void receivedDatagram(ByteBuffer readBuffer) {
208         processMessageReceived(readBuffer);
209         idleChecker.sessionRead(this, System.currentTimeMillis());
210     }
211 
212     /**
213      * {@inheritDoc}
214      */
215     @Override
216     protected int writeDirect(Object message) {
217         try {
218             // Check that we can write into the channel
219             if (!isRegisteredForWrite()) {
220                 // We don't have pending writes
221                 // First, connect if we aren't already connected
222                 if (!((DatagramChannel) channel).isConnected()) {
223                     ((DatagramChannel) channel).connect(remoteAddress);
224                 }
225 
226                 // And try to write the data. We will either write them all,
227                 // or none
228                 return ((DatagramChannel) channel).write((ByteBuffer) message);
229             } else {
230                 LOG.debug("Cannot write");
231                 return -1;
232             }
233         } catch (final IOException e) {
234             LOG.error("Exception while reading : ", e);
235             processException(e);
236 
237             return -1;
238         }
239     }
240 
241     /**
242      * {@inheritDoc}
243      */
244     @Override
245     protected ByteBuffer convertToDirectBuffer(WriteRequest writeRequest, boolean createNew) {
246         // Here, we don't create a new DirectBuffer. We let the underlying layer do the job for us
247         return (ByteBuffer) writeRequest.getMessage();
248     }
249 
250     /**
251      * Set this session status as connected. To be called by the processor selecting/polling this session.
252      */
253     void setConnected() {
254         if (!isCreated()) {
255             throw new IllegalStateException("Trying to open a non created session");
256         }
257 
258         state = SessionState.CONNECTED;
259         processSessionOpen();
260     }
261 
262     @Override
263     public void ready(boolean accept, boolean connect, boolean read, ByteBuffer readBuffer, boolean write) {
264         if (IS_DEBUG) {
265             LOG.debug("session {} ready for accept={}, connect={}, read={}, write={}", new Object[] { this, accept,
266                                     connect, read, write });
267         }
268 
269         if (read) {
270             if (IS_DEBUG) {
271                 LOG.debug("readable datagram for UDP service : {}", this);
272             }
273 
274             // Read everything we can up to the buffer size
275             try {
276                 readBuffer.clear();
277                 ((DatagramChannel) channel).receive(readBuffer);
278                 readBuffer.flip();
279 
280                 int readbytes = readBuffer.remaining();
281 
282                 if (IS_DEBUG) {
283                     LOG.debug("read {} bytes", readbytes);
284                 }
285 
286                 if (readbytes <= 0) {
287                     // session closed by the remote peer
288                     if (IS_DEBUG) {
289                         LOG.debug("session closed by the remote peer");
290                     }
291 
292                     close(true);
293                 } else {
294                     receivedDatagram(readBuffer);
295                 }
296             } catch (IOException e) {
297                 processException(e);
298             }
299 
300         }
301 
302         if (write) {
303             processWrite(selectorLoop);
304         }
305         if (accept) {
306             throw new IllegalStateException("accept event should never occur on NioUdpSession");
307         }
308 
309     }
310 
311 }