1   /***
2    *
3    * Copyright 2004 Hiram Chirino
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.activeio.net.benchmark;
18  
19  import java.io.IOException;
20  import java.lang.reflect.InvocationTargetException;
21  import java.net.URI;
22  import java.net.URISyntaxException;
23  import java.util.HashMap;
24  
25  import org.activeio.AcceptListener;
26  import org.activeio.AsynchChannel;
27  import org.activeio.AsynchChannelListener;
28  import org.activeio.AsynchChannelServer;
29  import org.activeio.Channel;
30  import org.activeio.ChannelFactory;
31  import org.activeio.Packet;
32  import org.activeio.adapter.SynchToAsynchChannelAdapter;
33  import org.activeio.packet.EOSPacket;
34  import org.activeio.stats.CountStatisticImpl;
35  import org.apache.commons.beanutils.BeanUtils;
36  
37  import EDU.oswego.cs.dl.util.concurrent.Latch;
38  
39  /***
40   * Implements a simple tcp echo server for use in benchmarking 
41   * activeio channel implementations.
42   * 
43   * @version $Revision$
44   */
45  public class Server implements Runnable, AcceptListener {
46  
47      private URI url;
48      private Latch shutdownLatch;
49      private long requestDelay = 0;
50      private long sampleInterval = 1000;
51      
52      private final CountStatisticImpl activeConnectionsCounter = new CountStatisticImpl("activeConnectionsCounter","The number of active connection attached to the server.");
53      private final CountStatisticImpl echoedBytesCounter = new CountStatisticImpl("echoedBytesCounter","The number of bytes that have been echoed by the server.");
54  
55      public static void main(String[] args) throws URISyntaxException, IllegalAccessException, InvocationTargetException {
56  
57          Server server = new Server();
58          
59          HashMap options = new HashMap();       
60          for( int i=0; i < args.length; i++ ) {
61              
62              String option = args[i];
63              if( !option.startsWith("-") || option.length()<2 || i+1 >= args.length ) {
64                  System.out.println("Invalid usage.");
65                  return;
66              }
67              
68              option = option.substring(1);
69              options.put(option, args[++i]);            
70          }        
71          BeanUtils.populate(server, options);
72          
73          System.out.println();
74          System.out.println("Server starting with the following options: ");
75          System.out.println(" url="+server.getUrl());
76          System.out.println(" sampleInterval="+server.getSampleInterval());
77          System.out.println(" requestDelay="+server.getRequestDelay());
78          System.out.println();
79          server.run();
80  
81      }
82  
83      private void printSampleData() {
84          long now = System.currentTimeMillis();
85          float runDuration = (now - activeConnectionsCounter.getStartTime())/1000f;
86          System.out.println("Active connections: "+activeConnectionsCounter.getCount());
87          System.out.println("Echoed bytes: " + (echoedBytesCounter.getCount()/1024f) + " kb");
88          echoedBytesCounter.reset();
89      }
90      
91  
92      public void run() {
93          try {
94              
95              activeConnectionsCounter.reset();
96              echoedBytesCounter.reset();
97              
98              shutdownLatch = new Latch();
99                  
100             ChannelFactory factory = new ChannelFactory();
101             AsynchChannelServer server = factory.bindAsynchChannel(url);
102             System.out.println("Server accepting connections on: "+server.getConnectURI());
103             server.setAcceptListener(this);
104             server.start();
105             
106             while(!shutdownLatch.attempt(sampleInterval)) {
107                 printSampleData();
108             }
109             
110             System.out.println("Stopping server.");
111             server.stop(1000*5);
112             server.dispose();
113             
114         } catch (IOException e) {
115             e.printStackTrace();
116         } catch (InterruptedException e) {
117         }
118     }
119 
120     public String getUrl() {
121         return url.toString();
122     }
123 
124     public void setUrl(String url) throws URISyntaxException {
125         this.url = new URI(url);
126     }
127 
128     class ServerConnectionHandler implements AsynchChannelListener {
129 
130         private final AsynchChannel asynchChannel;
131         private boolean disposed;
132         
133         public ServerConnectionHandler(AsynchChannel asynchChannel) {
134             this.asynchChannel = asynchChannel;
135             activeConnectionsCounter.increment();
136         }
137 
138         public void onPacket(Packet packet) {
139             
140             if( packet == EOSPacket.EOS_PACKET ) {
141                 System.out.println("Peer disconnected.");
142                 dispose();
143                 return;
144             }
145             
146             try {
147                 if( requestDelay > 0 ) {
148                     Thread.sleep(requestDelay);
149                 }
150                 
151                 echoedBytesCounter.add(packet.remaining());
152                 asynchChannel.write(packet);
153                 asynchChannel.flush();
154                 
155             } catch (IOException e) {
156                 onPacketError(e);
157             } catch (InterruptedException e) {
158                 System.out.println("Interrupted... Shutting down.");
159                 dispose();
160             }
161         }
162 
163         public void onPacketError(IOException error) {
164             error.printStackTrace();
165             dispose();
166         }
167 
168         private void dispose() {
169             if( !disposed ) {
170                 asynchChannel.dispose();
171                 activeConnectionsCounter.decrement();
172                 disposed=true;
173             }
174         }
175     }
176     
177     public void onAccept(Channel channel) {
178         try {
179             
180             AsynchChannel asynchChannel = SynchToAsynchChannelAdapter.adapt(channel);
181             asynchChannel.setAsynchChannelListener(new ServerConnectionHandler(asynchChannel));
182             asynchChannel.start();
183             
184         } catch (IOException e) {
185             onAcceptError(e);
186         }
187     }
188 
189     public void onAcceptError(IOException error) {
190         error.printStackTrace();
191         shutdownLatch.release();
192     }
193     
194     /***
195      * @return Returns the requestDelay.
196      */
197     public long getRequestDelay() {
198         return requestDelay;
199     }
200     /***
201      * @param requestDelay The requestDelay to set.
202      */
203     public void setRequestDelay(long requestDelay) {
204         this.requestDelay = requestDelay;
205     }
206     /***
207      * @return Returns the sampleInterval.
208      */
209     public long getSampleInterval() {
210         return sampleInterval;
211     }
212     /***
213      * @param sampleInterval The sampleInterval to set.
214      */
215     public void setSampleInterval(long sampleInterval) {
216         this.sampleInterval = sampleInterval;
217     }
218 }