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.core.nio.udp;
21  
22  import java.io.IOException;
23  import java.net.InetSocketAddress;
24  import java.net.SocketAddress;
25  import java.util.HashMap;
26  import java.util.Map;
27  
28  import org.apache.mina.core.BenchmarkServer;
29  import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
30  import org.jboss.netty.buffer.ChannelBuffer;
31  import org.jboss.netty.buffer.ChannelBuffers;
32  import org.jboss.netty.channel.ChannelFactory;
33  import org.jboss.netty.channel.ChannelHandlerContext;
34  import org.jboss.netty.channel.ChannelPipeline;
35  import org.jboss.netty.channel.ChannelPipelineFactory;
36  import org.jboss.netty.channel.ChannelStateEvent;
37  import org.jboss.netty.channel.Channels;
38  import org.jboss.netty.channel.ChildChannelStateEvent;
39  import org.jboss.netty.channel.ExceptionEvent;
40  import org.jboss.netty.channel.MessageEvent;
41  import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
42  import org.jboss.netty.channel.group.ChannelGroup;
43  import org.jboss.netty.channel.group.DefaultChannelGroup;
44  import org.jboss.netty.channel.socket.nio.NioDatagramChannelFactory;
45  
46  /**
47   * A Netty 3 based UDP server
48   * 
49   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
50   */
51  public class Netty3UdpBenchmarkServer implements BenchmarkServer {
52  
53      private static enum State {
54          WAIT_FOR_FIRST_BYTE_LENGTH, WAIT_FOR_SECOND_BYTE_LENGTH, WAIT_FOR_THIRD_BYTE_LENGTH, WAIT_FOR_FOURTH_BYTE_LENGTH, READING
55      }
56  
57      private static final ChannelBuffer ACK = ChannelBuffers.buffer(1);
58  
59      static {
60          ACK.writeByte(0);
61      }
62  
63      private static final String STATE_ATTRIBUTE = Netty3UdpBenchmarkServer.class.getName() + ".state";
64  
65      private static final String LENGTH_ATTRIBUTE = Netty3UdpBenchmarkServer.class.getName() + ".length";
66  
67      private ChannelFactory factory;
68  
69      private ChannelGroup allChannels = new DefaultChannelGroup();
70  
71      /**
72       * Allocate a map as attachment for storing attributes.
73       * 
74       * @param ctx the channel context
75       * @return the map from the attachment
76       */
77      protected static Map<String, Object> getAttributesMap(ChannelHandlerContext ctx) {
78          Map<String, Object> map = (Map<String, Object>) ctx.getAttachment();
79          if (map == null) {
80              map = new HashMap<String, Object>();
81              ctx.setAttachment(map);
82          }
83          return map;
84      }
85  
86      private static void setAttribute(ChannelHandlerContext ctx, String name, Object value) {
87          getAttributesMap(ctx).put(name, value);
88      }
89  
90      private static Object getAttribute(ChannelHandlerContext ctx, String name) {
91          return getAttributesMap(ctx).get(name);
92      }
93  
94      /**
95       * {@inheritDoc}
96       */
97      public void start(int port) throws IOException {
98          factory = new NioDatagramChannelFactory();
99          ConnectionlessBootstrap bootstrap = new ConnectionlessBootstrap(factory);
100         bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
101             public ChannelPipeline getPipeline() throws Exception {
102                 return Channels.pipeline(new SimpleChannelUpstreamHandler() {
103                     @Override
104                     public void childChannelOpen(ChannelHandlerContext ctx, ChildChannelStateEvent e) throws Exception {
105                         System.out.println("childChannelOpen");
106                         setAttribute(ctx, STATE_ATTRIBUTE, State.WAIT_FOR_FIRST_BYTE_LENGTH);
107                     }
108 
109                     @Override
110                     public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
111                         System.out.println("channelOpen");
112                         setAttribute(ctx, STATE_ATTRIBUTE, State.WAIT_FOR_FIRST_BYTE_LENGTH);
113                         allChannels.add(ctx.getChannel());
114                     }
115 
116                     @Override
117                     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
118                         if (e.getMessage() instanceof ChannelBuffer) {
119                             ChannelBuffer buffer = (ChannelBuffer) e.getMessage();
120 
121                             State state = (State) getAttribute(ctx, STATE_ATTRIBUTE);
122                             int length = 0;
123                             if (getAttributesMap(ctx).containsKey(LENGTH_ATTRIBUTE)) {
124                                 length = (Integer) getAttribute(ctx, LENGTH_ATTRIBUTE);
125                             }
126                             while (buffer.readableBytes() > 0) {
127                                 switch (state) {
128                                 case WAIT_FOR_FIRST_BYTE_LENGTH:
129                                     length = (buffer.readByte() & 255) << 24;
130                                     state = State.WAIT_FOR_SECOND_BYTE_LENGTH;
131                                     break;
132                                 case WAIT_FOR_SECOND_BYTE_LENGTH:
133                                     length += (buffer.readByte() & 255) << 16;
134                                     state = State.WAIT_FOR_THIRD_BYTE_LENGTH;
135                                     break;
136                                 case WAIT_FOR_THIRD_BYTE_LENGTH:
137                                     length += (buffer.readByte() & 255) << 8;
138                                     state = State.WAIT_FOR_FOURTH_BYTE_LENGTH;
139                                     break;
140                                 case WAIT_FOR_FOURTH_BYTE_LENGTH:
141                                     length += (buffer.readByte() & 255);
142                                     state = State.READING;
143                                     if ((length == 0) && (buffer.readableBytes() == 0)) {
144                                         ctx.getChannel().write(ACK.slice());
145                                         state = State.WAIT_FOR_FIRST_BYTE_LENGTH;
146                                     }
147                                     break;
148                                 case READING:
149                                     int remaining = buffer.readableBytes();
150                                     if (length > remaining) {
151                                         length -= remaining;
152                                         buffer.skipBytes(remaining);
153                                     } else {
154                                         buffer.skipBytes(length);
155                                         SocketAddress remoteAddress = e.getRemoteAddress();
156                                         ctx.getChannel().write(ACK.slice(), remoteAddress);
157                                         state = State.WAIT_FOR_FIRST_BYTE_LENGTH;
158                                         length = 0;
159                                     }
160                                 }
161                             }
162                             setAttribute(ctx, STATE_ATTRIBUTE, state);
163                             setAttribute(ctx, LENGTH_ATTRIBUTE, length);
164                         }
165                     }
166 
167                     @Override
168                     public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
169                         allChannels.remove(ctx.getChannel());
170                     }
171 
172                     @Override
173                     public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
174                         e.getCause().printStackTrace();
175                     }
176                 });
177             }
178         });
179         allChannels.add(bootstrap.bind(new InetSocketAddress(port)));
180     }
181 
182     /**
183      * {@inheritedDoc}
184      */
185     public void stop() throws IOException {
186         allChannels.disconnect().awaitUninterruptibly();
187         factory.shutdown();
188         factory.releaseExternalResources();
189     }
190 }