Savarese.Org
BareHTTP 1.0.1 Unit Test Coverage
[all classes][org.savarese.barehttp]

COVERAGE SUMMARY FOR SOURCE FILE [HTTPServer.java]

nameclass, %method, %block, %line, %
HTTPServer.java100% (3/3)95%  (21/22)85%  (238/279)85%  (57.5/68)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class HTTPServer$Server100% (1/1)100% (3/3)81%  (54/67)79%  (11/14)
HTTPServer$Server (HTTPServer, int, int, InetAddress): void 100% (1/1)60%  (15/25)80%  (4/5)
call (): Void 100% (1/1)92%  (35/38)71%  (5/7)
close (): void 100% (1/1)100% (4/4)100% (2/2)
     
class HTTPServer100% (1/1)94%  (16/17)85%  (162/190)84%  (39.5/47)
HTTPServer (String): void 0%   (0/1)0%   (0/6)0%   (0/2)
setMaxConnections (int): void 100% (1/1)67%  (10/15)75%  (3/4)
stop (long, TimeUnit): boolean 100% (1/1)71%  (22/31)82%  (6.5/8)
getBoundAddress (): InetAddress 100% (1/1)80%  (8/10)67%  (2/3)
getBoundPort (): int 100% (1/1)85%  (11/13)67%  (2/3)
start (): void 100% (1/1)90%  (37/41)83%  (5/6)
HTTPServer (String, int, int): void 100% (1/1)100% (24/24)100% (9/9)
decrementConnectionCount (): int 100% (1/1)100% (8/8)100% (1/1)
getBindAddress (): InetAddress 100% (1/1)100% (3/3)100% (1/1)
getConnectionCount (): int 100% (1/1)100% (3/3)100% (1/1)
getDocumentRoot (): String 100% (1/1)100% (3/3)100% (1/1)
getMaxConnections (): int 100% (1/1)100% (3/3)100% (1/1)
getPort (): int 100% (1/1)100% (3/3)100% (1/1)
incrementConnectionCount (): int 100% (1/1)100% (8/8)100% (1/1)
isRunning (): boolean 100% (1/1)100% (11/11)100% (1/1)
setBindAddress (InetAddress): void 100% (1/1)100% (4/4)100% (2/2)
setPort (int): void 100% (1/1)100% (4/4)100% (2/2)
     
class HTTPServer$Session100% (1/1)100% (2/2)100% (22/22)100% (7/7)
HTTPServer$Session (HTTPServer, HTTPSession): void 100% (1/1)100% (9/9)100% (3/3)
call (): Void 100% (1/1)100% (13/13)100% (4/4)

1/*
2 * $Id$
3 *
4 * Copyright 2001,2006 Daniel F. Savarese
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.savarese.org/software/ApacheLicense-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18 
19package org.savarese.barehttp;
20 
21import java.io.*;
22import java.net.*;
23import java.util.concurrent.*;
24 
25/**
26 * Implements a server that listens for incoming client connections
27 * and services each with {@link HTTPSession} instances.  A port
28 * number, bind address, and maximum number of client connections to
29 * service may be specified.
30 *
31 * @author <a href="http://www.savarese.org/">Daniel F. Savarese</a>
32 */
33public class HTTPServer {
34 
35  /**
36   * The default maximum number of concurrent client connections (10)
37   * that will be accepted if not specified.
38   */
39  public static final int DEFAULT_MAX_CONNECTIONS = 10;
40 
41  /**
42   * The default port number (8080) to bind to if not specified.
43   */
44  public static final int DEFAULT_PORT = 8080;
45 
46  InetAddress bindAddress;
47  int httpPort, maxConnections, backlog, connectionCount;
48  String documentRoot;
49  ExecutorService executor;
50  Server server;
51 
52  synchronized int incrementConnectionCount() {
53    return ++connectionCount;
54  }
55 
56  synchronized int decrementConnectionCount() {
57    return --connectionCount;
58  }
59 
60  /**
61   * Same as HTTPServer(documentRoot, DEFAULT_PORT, DEFAULT_MAX_CONNECTIONS);
62   */
63  public HTTPServer(String documentRoot) {
64    this(documentRoot, DEFAULT_PORT, DEFAULT_MAX_CONNECTIONS);
65  }
66 
67  /**
68   * Creates an HTTPServer instance.
69   *
70   * @param root The fully qualified document root directory pathname.
71   * @param port The port number the server should bind to.
72   * @param maxConnections The maximum number of client connections the
73   * server should accept.
74   */
75  public HTTPServer(String root, int port, int maxConnections) {
76    setPort(port);
77    setMaxConnections(maxConnections);
78    setBindAddress(null);
79    connectionCount = 0;
80    documentRoot    = root;
81    executor        = null;
82    server          = null;
83  }
84 
85  /**
86   * Returns the document root directory pathname.
87   *
88   * @return The document root directory pathname.
89   */
90  public String getDocumentRoot() {
91    return documentRoot;
92  }
93 
94  /**
95   * Sets the port number the server should bind to.  By default, the
96   * server binds to {@link #DEFAULT_PORT}.  The new port takes effect
97   * the next time {@link #start} is invoked (after a {@link #stop} if
98   * already running).
99   *
100   * @param port The port number the server should bind to.
101   */
102  public synchronized void setPort(int port) {
103    httpPort = port;
104  }
105 
106  /**
107   * The port number the server will bind to.
108   *
109   * @return The port number the server will bind to.
110   */
111  public int getPort() {
112    return httpPort;
113  }
114 
115  /**
116   * If the server is running, returns the port number currently bound
117   * to.  Otherwise, returns -1.
118   *
119   * @return The port number currently bound to or -1 if not bound.
120   */
121  public synchronized int getBoundPort() {
122    if(httpPort == 0 && server != null)
123      return server.socket.getLocalPort();
124    return -1;
125  }
126 
127  /**
128   * Sets the maximum number of concurrent client connections the
129   * server should accept.
130   *
131   * @param maxConnections The maximum number of concurrent client
132   * connections the server should accept.
133   */
134  public synchronized void setMaxConnections(int maxConnections) {
135    this.maxConnections = backlog = maxConnections;
136    if(maxConnections > (DEFAULT_MAX_CONNECTIONS << 1))
137      backlog  = maxConnections >> 1;
138  }
139 
140  /**
141   * Returns the maximum number of concurrent client connections that
142   * will be accepted.
143   *
144   * @return The maximum number of concurrent client connections that
145   * will be accepted.
146   */
147  public int getMaxConnections() {
148    return maxConnections;
149  }
150 
151  /**
152   * Returns the number of client connections currently established.
153   *
154   * @return The number of client connections currently established.
155   */
156  public synchronized int getConnectionCount() {
157    return connectionCount;
158  }
159 
160  /**
161   * Sets the network interface address the server should bind to.  By
162   * default, the server binds to the wildcard address.  The new bind
163   * address takes effect the next time {@link #start} is invoked
164   * (after a {@link #stop} if already running).
165   *
166   * @param bindAddr The network interface the server should bind to.
167   * It may be null to reset to the wildcard.
168   */
169  public synchronized void setBindAddress(InetAddress bindAddr) {
170    bindAddress = bindAddr;
171  }
172 
173  /**
174   * Returns the network interface address the server will bind to.  A
175   * null return value signifies the wildcard address.
176   *
177   * @return The network interface address the server will bind to.
178   */
179  public InetAddress getBindAddress() {
180    return bindAddress;
181  }
182 
183  /**
184   * If the server is running, returns the address currently bound
185   * to.  Otherwise, returns null.
186   *
187   * @return The port number currently bound to or -1 if not bound.
188   */
189  public synchronized InetAddress getBoundAddress() {
190    if(server != null)
191      return server.socket.getInetAddress();
192    return null;
193  }
194 
195  /**
196   * Returns true if the server is in a running state, false if not.
197   *
198   * @return True if the server is in a running state, false if not.
199   */
200  public synchronized boolean isRunning() {
201    return (executor != null && !executor.isTerminated());
202  }
203 
204  final class Session implements Callable<Void> {
205    HTTPSession session;
206 
207    Session(HTTPSession session) {
208      this.session = session;
209    }
210 
211    public Void call() throws Exception {
212      incrementConnectionCount();
213      session.execute();
214      decrementConnectionCount();
215      return null;
216    }
217  }
218 
219  class Server implements Callable<Void> {
220    ServerSocket socket;
221 
222    public Server(int port, int backlog, InetAddress address) 
223      throws IOException
224    {
225      if(address != null)
226        socket = new ServerSocket(port, backlog, bindAddress);
227      else
228        socket = new ServerSocket(port, backlog);
229    }
230 
231    public Void call() throws Exception {
232      try {
233        while(true) {
234          Socket client = socket.accept();
235 
236          if(getConnectionCount() >= maxConnections) {
237            // Ungracefully close connection.
238            client.close();
239            continue;
240          }
241 
242          executor.submit(new Session(new HTTPSession(documentRoot,
243                                                   client.getInputStream(),
244                                                   client.getOutputStream())));
245        }
246      } finally {
247        return null;
248      }
249    }
250 
251    public void close() throws IOException {
252      socket.close();
253    }
254 
255  }
256 
257  /**
258   * Starts listening for incoming connectons in an asynchronously
259   * initiated thread.  The method returns immediately after the
260   * listening thread is established, making the HTTPServer instance
261   * an active object.
262   *
263   * @throws IOException If the server socket cannot be bound.
264   * @throws IllegalStateException If the server is already running.
265   */
266  public synchronized void start() throws IOException, IllegalStateException {
267    if(isRunning())
268      throw new IllegalStateException();
269 
270    server = new Server(httpPort, backlog, bindAddress);
271    executor =
272      new ThreadPoolExecutor(0, maxConnections + 1, 60L, TimeUnit.SECONDS,
273                             new SynchronousQueue<Runnable>());
274    executor.submit(server);
275  }
276 
277  /**
278   * Schedules termination of the server, closes the server socket,
279   * and waits for the specified amount of time or until the server is
280   * terminated before returning.
281   *
282   * @param timeout The maximum amount of time to wait for termination.
283   * @param unit The unit of time for the timeout.
284   * @return True if the server terminated before the method returned,
285   * false if not.  If false is returned, the server will not be
286   * completely terminated untl {@link #isRunning} returns false.
287   * Subsequent calls to stop will have no effect while terminating.
288   */
289  public synchronized boolean stop(long timeout, TimeUnit unit)
290    throws IOException
291  {
292    if(server != null) {
293      boolean result = false;
294 
295      try {
296        executor.shutdown();
297        server.close();
298        result = executor.awaitTermination(timeout, unit);
299      } finally { 
300        server = null;
301        return result;
302      }
303    }
304 
305    return isRunning();
306  }
307 
308}

[all classes][org.savarese.barehttp]
Savarese.Org