Clover Coverage Report - ARESTC 0.1.7-SNAPSHOT
Coverage timestamp: Fri Aug 27 2010 19:12:04 CEST
../../../../img/srcFileCovDistChart5.png 93% of files have more coverage
99   535   47   4.71
32   246   0.47   7
21     2.24  
3    
 
  LocalTestServer       Line # 93 65 0% 29 62 41% 0.40952381
  LocalTestServer.RequestListener       Line # 101 25 0% 10 16 52.9% 0.5294118
  LocalTestServer.RequestListener.Worker       Line # 106 9 0% 8 1 92.3% 0.9230769
 
  (1)
 
1    /*
2    *
3    * (C)opyright 2010, Nikolaos Georgosopoulos
4    *
5    * This file is part of ARESTC.
6    *
7    * ARESTC is free software: you can redistribute it and/or modify it under the
8    * terms of the Lesser General Public License as published by the Free Software
9    * Foundation, either version 3 of the License, or (at your option) any later
10    * version.
11    *
12    * ARESTC is distributed in the hope that it will be useful, but WITHOUT ANY
13    * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14    * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15    *
16    * You should have received a copy of the Lesser General Public License along
17    * with ARESTC. If not, see <http://www.gnu.org/licenses/>.
18    */
19    package net.sf.arestc.testserver;
20   
21    /*
22    * ====================================================================
23    * Licensed to the Apache Software Foundation (ASF) under one
24    * or more contributor license agreements. See the NOTICE file
25    * distributed with this work for additional information
26    * regarding copyright ownership. The ASF licenses this file
27    * to you under the Apache License, Version 2.0 (the
28    * "License"); you may not use this file except in compliance
29    * with the License. You may obtain a copy of the License at
30    *
31    * http://www.apache.org/licenses/LICENSE-2.0
32    *
33    * Unless required by applicable law or agreed to in writing,
34    * software distributed under the License is distributed on an
35    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
36    * KIND, either express or implied. See the License for the
37    * specific language governing permissions and limitations
38    * under the License.
39    * ====================================================================
40    *
41    * This software consists of voluntary contributions made by many
42    * individuals on behalf of the Apache Software Foundation. For more
43    * information on the Apache Software Foundation, please see
44    * <http://www.apache.org/>.
45    *
46    */
47   
48    import java.io.IOException;
49    import java.net.InetSocketAddress;
50    import java.net.ServerSocket;
51    import java.net.Socket;
52    import java.net.SocketAddress;
53    import java.util.Collections;
54    import java.util.HashSet;
55    import java.util.Set;
56    import java.util.concurrent.atomic.AtomicInteger;
57   
58    import javax.net.ssl.SSLContext;
59    import javax.net.ssl.SSLServerSocketFactory;
60   
61    import org.apache.http.ConnectionReuseStrategy;
62    import org.apache.http.HttpException;
63    import org.apache.http.HttpServerConnection;
64    import org.apache.http.impl.DefaultConnectionReuseStrategy;
65    import org.apache.http.impl.DefaultHttpResponseFactory;
66    import org.apache.http.impl.DefaultHttpServerConnection;
67    import org.apache.http.localserver.EchoHandler;
68    import org.apache.http.localserver.RandomHandler;
69    import org.apache.http.params.BasicHttpParams;
70    import org.apache.http.params.CoreConnectionPNames;
71    import org.apache.http.params.CoreProtocolPNames;
72    import org.apache.http.params.HttpParams;
73    import org.apache.http.protocol.BasicHttpContext;
74    import org.apache.http.protocol.BasicHttpProcessor;
75    import org.apache.http.protocol.HttpContext;
76    import org.apache.http.protocol.HttpRequestHandler;
77    import org.apache.http.protocol.HttpRequestHandlerRegistry;
78    import org.apache.http.protocol.HttpService;
79    import org.apache.http.protocol.ResponseConnControl;
80    import org.apache.http.protocol.ResponseContent;
81    import org.apache.http.protocol.ResponseDate;
82    import org.apache.http.protocol.ResponseServer;
83   
84    // TODO: Auto-generated Javadoc
85    /**
86    * Local HTTP server for tests that require one. Based on the
87    * <code>ElementalHttpServer</code> example in HttpCore.
88    *
89    *
90    *
91    * <!-- empty lines to avoid 'svn diff' problems -->
92    */
 
93    public class LocalTestServer {
94   
95    /**
96    * The request listener. Accepts incoming connections and launches a service
97    * thread.
98    *
99    * @see RequestEvent
100    */
 
101    public class RequestListener implements Runnable {
102   
103    /**
104    * A worker for serving incoming requests.
105    */
 
106    public class Worker implements Runnable {
107   
108    /** The httpservice. */
109    private final HttpService httpservice;
110   
111    /** The conn. */
112    private final HttpServerConnection conn;
113   
114    /**
115    * Instantiates a new worker.
116    *
117    * @param httpservice
118    * the httpservice
119    * @param conn
120    * the conn
121    */
 
122  1 toggle public Worker(final HttpService httpservice,
123    final HttpServerConnection conn) {
124   
125  1 this.httpservice = httpservice;
126  1 this.conn = conn;
127    }
128   
129    /*
130    * (non-Javadoc)
131    *
132    * @see java.lang.Runnable#run()
133    */
 
134  1 toggle public void run() {
135  1 final HttpContext context = new BasicHttpContext(null);
136  1 try {
137  11 while (servicedSocket != null && conn.isOpen()
138    && !Thread.interrupted()) {
139  11 httpservice.handleRequest(conn, context);
140    }
141    } catch (final IOException ex) {
142    // ignore silently
143    } catch (final HttpException ex) {
144    // ignore silently
145    } finally {
146  1 workerThreads.remove(Thread.currentThread());
147  1 try {
148  1 conn.shutdown();
149    } catch (final IOException ignore) {
150    }
151    }
152    }
153   
154    } // class Worker
155   
156    /** The workers launched from here. */
157    private final Set<Thread> workerThreads = Collections
158    .synchronizedSet(new HashSet<Thread>());
159   
160    /**
161    * Accept.
162    *
163    * @throws IOException
164    * Signals that an I/O exception has occurred.
165    */
 
166  2 toggle protected void accept() throws IOException {
167    // Set up HTTP connection
168  2 final Socket socket = servicedSocket.accept();
169  1 acceptedConnections.incrementAndGet();
170  1 final DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
171  1 conn.bind(socket, serverParams);
172   
173    // Set up the HTTP service
174  1 final HttpService httpService = new HttpService(httpProcessor,
175    reuseStrategy, new DefaultHttpResponseFactory());
176  1 httpService.setParams(serverParams);
177  1 httpService.setHandlerResolver(handlerRegistry);
178   
179    // Start worker thread
180  1 final Thread t = new Thread(new Worker(httpService, conn));
181  1 workerThreads.add(t);
182  1 t.setDaemon(true);
183  1 t.start();
184   
185    } // accept
186   
187    /**
188    * Cleanup.
189    */
 
190  0 toggle protected void cleanup() {
191  0 final Thread[] threads = workerThreads.toArray(new Thread[0]);
192  0 for (final Thread thread : threads) {
193  0 if (thread != null) {
194  0 thread.interrupt();
195    }
196    }
197    }
198   
199    /*
200    * (non-Javadoc)
201    *
202    * @see java.lang.Runnable#run()
203    */
 
204  1 toggle public void run() {
205   
206  1 try {
207  2 while (servicedSocket != null
208    && listenerThread == Thread.currentThread()
209    && !Thread.interrupted()) {
210  2 try {
211  2 accept();
212    } catch (final Exception e) {
213  0 final ServerSocket ssock = servicedSocket;
214  0 if (ssock != null && !ssock.isClosed()) {
215  0 System.out.println(LocalTestServer.this.toString()
216    + " could not accept");
217  0 e.printStackTrace(System.out);
218    }
219    // otherwise ignore the exception silently
220  0 break;
221    }
222    }
223    } finally {
224  0 cleanup();
225    }
226    }
227   
228    } // class RequestListener
229   
230    /**
231    * The local address to bind to. The host is an IP number rather than
232    * "localhost" to avoid surprises on hosts that map "localhost" to an IPv6
233    * address or something else. The port is 0 to let the system pick one.
234    */
235    public final static InetSocketAddress TEST_SERVER_ADDR = new InetSocketAddress(
236    "localhost",
237    9090);
238   
239    /** The request handler registry. */
240    private final HttpRequestHandlerRegistry handlerRegistry;
241   
242    /** The server-side connection re-use strategy. */
243    private final ConnectionReuseStrategy reuseStrategy;
244   
245    /**
246    * The HTTP processor. If the interceptors are thread safe and the list is
247    * not modified during operation, the processor is thread safe.
248    */
249    private final BasicHttpProcessor httpProcessor;
250   
251    /** The server parameters. */
252    private final HttpParams serverParams;
253   
254    /** Optional SSL context. */
255    private final SSLContext sslcontext;
256   
257    /** The server socket, while being served. */
258    protected volatile ServerSocket servicedSocket;
259   
260    /** The request listening thread, while listening. */
261    protected volatile Thread listenerThread;
262   
263    /** The number of connections this accepted. */
264    private final AtomicInteger acceptedConnections = new AtomicInteger(
265    0);
266   
267    /**
268    * Creates a new test server.
269    *
270    * @param proc
271    * the HTTP processors to be used by the server, or
272    * <code>null</code> to use a {@link #newProcessor default}
273    * processor
274    * @param reuseStrat
275    * the connection reuse strategy to be used by the server, or
276    * <code>null</code> to use {@link #newConnectionReuseStrategy()
277    * default} strategy.
278    * @param params
279    * the parameters to be used by the server, or <code>null</code>
280    * to use {@link #newDefaultParams default} parameters
281    * @param sslcontext
282    * optional SSL context if the server is to leverage SSL/TLS
283    * transport security
284    */
 
285  1 toggle public LocalTestServer(final BasicHttpProcessor proc,
286    final ConnectionReuseStrategy reuseStrat, final HttpParams params,
287    final SSLContext sslcontext) {
288  1 super();
289  1 handlerRegistry = new HttpRequestHandlerRegistry();
290  1 reuseStrategy = reuseStrat != null ? reuseStrat
291    : newConnectionReuseStrategy();
292  1 httpProcessor = proc != null ? proc : newProcessor();
293  1 serverParams = params != null ? params : newDefaultParams();
294  1 this.sslcontext = sslcontext;
295    }
296   
297    /**
298    * Creates a new test server.
299    *
300    * @param proc
301    * the HTTP processors to be used by the server, or
302    * <code>null</code> to use a {@link #newProcessor default}
303    * processor
304    * @param params
305    * the parameters to be used by the server, or <code>null</code>
306    * to use {@link #newDefaultParams default} parameters
307    */
 
308  1 toggle public LocalTestServer(final BasicHttpProcessor proc,
309    final HttpParams params) {
310  1 this(proc, null, params, null);
311    }
312   
313    /**
314    * Await termination.
315    *
316    * @param timeMs
317    * the time ms
318    * @throws InterruptedException
319    * the interrupted exception
320    */
 
321  0 toggle public void awaitTermination(final long timeMs) throws InterruptedException {
322  0 if (listenerThread != null) {
323  0 listenerThread.join(timeMs);
324    }
325    }
326   
327    /**
328    * Returns the number of connections this test server has accepted.
329    *
330    * @return the accepted connection count
331    */
 
332  0 toggle public int getAcceptedConnectionCount() {
333  0 return acceptedConnections.get();
334    }
335   
336    /**
337    * Obtains the local address the server is listening on.
338    *
339    * @return the service address
340    */
 
341  0 toggle public SocketAddress getServiceAddress() {
342  0 final ServerSocket ssock = servicedSocket; // avoid synchronization
343  0 if (ssock == null) {
344  0 throw new IllegalStateException("not running");
345    }
346   
347  0 return ssock.getLocalSocketAddress();
348    }
349   
350    /**
351    * Obtains the hostname of the server.
352    *
353    * @return the hostname
354    */
 
355  0 toggle public String getServiceHostName() {
356  0 final ServerSocket ssock = servicedSocket; // avoid synchronization
357  0 if (ssock == null) {
358  0 throw new IllegalStateException("not running");
359    }
360   
361  0 return ((InetSocketAddress) ssock.getLocalSocketAddress())
362    .getHostName();
363    }
364   
365    /**
366    * Obtains the port this server is servicing.
367    *
368    * @return the service port
369    */
 
370  0 toggle public int getServicePort() {
371  0 final ServerSocket ssock = servicedSocket; // avoid synchronization
372  0 if (ssock == null) {
373  0 throw new IllegalStateException("not running");
374    }
375   
376  0 return ssock.getLocalPort();
377    }
378   
379    /**
380    * New connection reuse strategy.
381    *
382    * @return the connection reuse strategy
383    */
 
384  1 toggle protected ConnectionReuseStrategy newConnectionReuseStrategy() {
385  1 return new DefaultConnectionReuseStrategy();
386    }
387   
388    /**
389    * Obtains a set of reasonable default parameters for a server.
390    *
391    * @return default parameters
392    */
 
393  1 toggle protected HttpParams newDefaultParams() {
394  1 final HttpParams params = new BasicHttpParams();
395  1 params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 60000)
396    .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE,
397    8 * 1024)
398    .setBooleanParameter(
399    CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
400    .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
401    .setParameter(CoreProtocolPNames.ORIGIN_SERVER,
402    "LocalTestServer/1.1");
403  1 return params;
404    }
405   
406    /**
407    * Obtains an HTTP protocol processor with default interceptors.
408    *
409    * @return a protocol processor for server-side use
410    */
 
411  1 toggle protected BasicHttpProcessor newProcessor() {
412   
413  1 final BasicHttpProcessor httpproc = new BasicHttpProcessor();
414  1 httpproc.addInterceptor(new ResponseDate());
415  1 httpproc.addInterceptor(new ResponseServer());
416  1 httpproc.addInterceptor(new ResponseContent());
417  1 httpproc.addInterceptor(new ResponseConnControl());
418   
419  1 return httpproc;
420    }
421   
422    /**
423    * Registers a handler with the local registry.
424    *
425    * @param pattern
426    * the URL pattern to match
427    * @param handler
428    * the handler to apply
429    */
 
430  8 toggle public void register(final String pattern, final HttpRequestHandler handler) {
431  8 handlerRegistry.register(pattern, handler);
432    }
433   
434    /**
435    * Register default handlers.
436    *
437    * {@link #register Registers} a set of default request handlers.
438    *
439    * <pre>
440    * URI pattern Handler
441    * ----------- -------
442    * /echo/* {@link EchoHandler EchoHandler}
443    * /random/* {@link RandomHandler RandomHandler}
444    * </pre>
445    */
 
446  1 toggle public void registerDefaultHandlers() {
447  1 handlerRegistry.register("/echo/*", new EchoHandler());
448  1 handlerRegistry.register("/random/*", new RandomHandler());
449    }
450   
451    /**
452    * Starts this test server. Use {@link #getServicePort getServicePort} to
453    * obtain the port number afterwards.
454    *
455    * @throws Exception
456    * the exception
457    */
 
458  1 toggle public void start() throws Exception {
459  1 if (servicedSocket != null) {
460  0 throw new IllegalStateException(toString() + " already running");
461    }
462   
463  1 ServerSocket ssock;
464  1 if (sslcontext != null) {
465  0 final SSLServerSocketFactory sf = sslcontext
466    .getServerSocketFactory();
467  0 ssock = sf.createServerSocket();
468    } else {
469  1 ssock = new ServerSocket();
470    }
471   
472  1 ssock.setReuseAddress(true); // probably pointless for port '0'
473  1 ssock.bind(TEST_SERVER_ADDR);
474  1 servicedSocket = ssock;
475   
476  1 listenerThread = new Thread(new RequestListener());
477  1 listenerThread.setDaemon(false);
478  1 listenerThread.start();
479    }
480   
481    /**
482    * Stops this test server.
483    *
484    * @throws Exception
485    * the exception
486    */
 
487  0 toggle public void stop() throws Exception {
488  0 if (servicedSocket == null) {
489  0 return; // not running
490    }
491   
492  0 try {
493  0 servicedSocket.close();
494    } catch (final IOException iox) {
495  0 System.out.println("error stopping " + this);
496  0 iox.printStackTrace(System.out);
497    } finally {
498  0 servicedSocket = null;
499    }
500   
501  0 if (listenerThread != null) {
502  0 listenerThread.interrupt();
503    }
504    }
505   
506    /*
507    * (non-Javadoc)
508    *
509    * @see java.lang.Object#toString()
510    */
 
511  0 toggle @Override
512    public String toString() {
513  0 final ServerSocket ssock = servicedSocket; // avoid synchronization
514  0 final StringBuffer sb = new StringBuffer(80);
515  0 sb.append("LocalTestServer/");
516  0 if (ssock == null) {
517  0 sb.append("stopped");
518    } else {
519  0 sb.append(ssock.getLocalSocketAddress());
520    }
521  0 return sb.toString();
522    }
523   
524    /**
525    * Unregisters a handler from the local registry.
526    *
527    * @param pattern
528    * the URL pattern
529    */
 
530  0 toggle public void unregister(final String pattern) {
531  0 handlerRegistry.unregister(pattern);
532    }
533   
534    } // class LocalTestServer
535