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.tcp;
21  
22  import java.io.IOException;
23  import java.net.InetSocketAddress;
24  import java.util.HashMap;
25  import java.util.Map;
26  
27  import org.apache.mina.core.BenchmarkServer;
28  import org.apache.mina.core.CounterFilter;
29  import org.jboss.netty.bootstrap.ServerBootstrap;
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.WriteCompletionEvent;
43  import org.jboss.netty.channel.group.ChannelGroup;
44  import org.jboss.netty.channel.group.DefaultChannelGroup;
45  import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
46  
47  /**
48   * A Netty 3 TCP Server.
49   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
50   */
51  public class Netty3TcpBenchmarkServer 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 = Netty3TcpBenchmarkServer.class.getName() + ".state";
64  
65      private static final String LENGTH_ATTRIBUTE = Netty3TcpBenchmarkServer.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 NioServerSocketChannelFactory();
99          ServerBootstrap bootstrap = new ServerBootstrap(factory);
100         bootstrap.setOption("receiveBufferSize", 128 * 1024);
101         bootstrap.setOption("tcpNoDelay", true);
102         bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
103             public ChannelPipeline getPipeline() throws Exception {
104                 return Channels.pipeline(new SimpleChannelUpstreamHandler() {
105                     @Override
106                     public void childChannelOpen(ChannelHandlerContext ctx, ChildChannelStateEvent e) throws Exception {
107                         System.out.println("childChannelOpen");
108                         setAttribute(ctx, STATE_ATTRIBUTE, State.WAIT_FOR_FIRST_BYTE_LENGTH);
109                     }
110 
111                     @Override
112                     public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
113                         System.out.println("channelOpen");
114                         setAttribute(ctx, STATE_ATTRIBUTE, State.WAIT_FOR_FIRST_BYTE_LENGTH);
115                         allChannels.add(ctx.getChannel());
116                     }
117 
118                     public void writeComplete(ChannelHandlerContext ctx, WriteCompletionEvent e) throws Exception {
119                         CounterFilter.messageSent.getAndIncrement();
120                     }
121 
122                     @Override
123                     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
124                         if (e.getMessage() instanceof ChannelBuffer) {
125                             ChannelBuffer buffer = (ChannelBuffer) e.getMessage();
126 
127                             State state = (State) getAttribute(ctx, STATE_ATTRIBUTE);
128                             int length = 0;
129                             if (getAttributesMap(ctx).containsKey(LENGTH_ATTRIBUTE)) {
130                                 length = (Integer) getAttribute(ctx, LENGTH_ATTRIBUTE);
131                             }
132                             while (buffer.readableBytes() > 0) {
133                                 switch (state) {
134                                 case WAIT_FOR_FIRST_BYTE_LENGTH:
135                                     length = (buffer.readByte() & 255) << 24;
136                                     state = State.WAIT_FOR_SECOND_BYTE_LENGTH;
137                                     break;
138                                 case WAIT_FOR_SECOND_BYTE_LENGTH:
139                                     length += (buffer.readByte() & 255) << 16;
140                                     state = State.WAIT_FOR_THIRD_BYTE_LENGTH;
141                                     break;
142                                 case WAIT_FOR_THIRD_BYTE_LENGTH:
143                                     length += (buffer.readByte() & 255) << 8;
144                                     state = State.WAIT_FOR_FOURTH_BYTE_LENGTH;
145                                     break;
146                                 case WAIT_FOR_FOURTH_BYTE_LENGTH:
147                                     length += (buffer.readByte() & 255);
148                                     state = State.READING;
149                                     if ((length == 0) && (buffer.readableBytes() == 0)) {
150                                         ctx.getChannel().write(ACK.slice());
151                                         state = State.WAIT_FOR_FIRST_BYTE_LENGTH;
152                                     }
153                                     break;
154                                 case READING:
155                                     int remaining = buffer.readableBytes();
156                                     if (length > remaining) {
157                                         length -= remaining;
158                                         buffer.skipBytes(remaining);
159                                     } else {
160                                         buffer.skipBytes(length);
161                                         ctx.getChannel().write(ACK.slice());
162                                         state = State.WAIT_FOR_FIRST_BYTE_LENGTH;
163                                         length = 0;
164                                     }
165                                 }
166                             }
167                             setAttribute(ctx, STATE_ATTRIBUTE, state);
168                             setAttribute(ctx, LENGTH_ATTRIBUTE, length);
169                         }
170                     }
171 
172                     @Override
173                     public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
174                         allChannels.remove(ctx.getChannel());
175                     }
176 
177                     @Override
178                     public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
179                         e.getCause().printStackTrace();
180                     }
181                 });
182             }
183         });
184         allChannels.add(bootstrap.bind(new InetSocketAddress(port)));
185     }
186 
187     /**
188      * {@inheritedDoc}
189      */
190     public void stop() throws IOException {
191         allChannels.disconnect().awaitUninterruptibly();
192         factory.releaseExternalResources();
193     }
194 }