The Server


ServerTest:

 package sis.search; import junit.framework.*; import sis.util.*; public class ServerTest extends TestCase {    private int numberOfResults = 0;    private Server server;    private static final long TIMEOUT = 3000L;    private static final String[] URLS = {       SearchTest.URL, SearchTest.URL, SearchTest.URL };       // 1    protected void setUp() throws Exception {       TestUtil.delete(SearchTest.FILE);       LineWriter.write(SearchTest.FILE, SearchTest.TEST_HTML);       ResultsListener listener = new ResultsListener() {     // 2          public void executed(Search search) {             numberOfResults++;          }};       server = new Server(listener);    }    protected void tearDown() throws Exception {       TestUtil.delete(SearchTest.FILE);    }    public void testSearch() throws Exception {       long start = System.currentTimeMillis();       for (String url: URLS)                                  // 3          server.add(new Search(url, "xxx"));       long elapsed = System.currentTimeMillis() - start;       long averageLatency = elapsed / URLS.length;       assertTrue(averageLatency < 20);                        // 4       assertTrue(waitForResults());                           // 5    }    private boolean waitForResults() {       long start = System.currentTimeMillis();       while (numberOfResults < URLS.length) {          try {Thread.sleep(1); }          catch (InterruptedException e) {}          if (System.currentTimeMillis() - start > TIMEOUT)             return false;       }       return true;    } } 

The test first constructs a list of URL strings (line 1).

The setup method constructs a ResultsListener object (line 2). You pass this object as a parameter when constructing a new Server instance.

Your test, as a client, will only be to pass search requests to the server. In the interest of responsiveness, you are designing the server so the client will not have to wait for each request to complete. But the client will still need to know the results each time theserver gets around to processing a search. A frequently used mechanism is a callback.

The term callback is a holdover from the language C, which allows you to create pointers to functions. Once you have a function pointer, you can pass this pointer to other functions like any other reference. The receiving code can then call back to the function located in the originating code, using the function pointer.

In Java, one effective way to implement a callback is to pass an anonymous inner class instance as a parameter to a method. The interface Results-Listener defines an executed method:

 package sis.search; public interface ResultsListener {    public void executed(Search search); } 

In testSearch, you pass an anonymous inner class instance of ResultsListener to the constructor of Server. The Server object holds on to the ResultsListener reference and sends it the executed message when the search has completed execution.

Callbacks are frequently referred to as listeners in Java implementations. A listener interface defines methods that you want called when something happens. User interface classes frequently use listeners to notify other code when an event occurs. For example, you can configure a Java Swing class with a event listener that lets you know when a user clicks on the button to close a window. This allows you to tie up any loose ends before the window actually closes.

In the test, you use the ResultsListener instance simply to track the total number of search executions that complete.

Once you create a Server object, you use a loop (line 3) to iterate through the list of URLs. You use each URL to construct a Search object (the search text is irrelevant). In the interest of demonstrating that Java rapidly dispatches each request, you track the elapsed execution time to create and add a search to the server. You use a subsequent assertion (line 4) to show that the average latency (delay in response time) is an arbitrarily small 20 milliseconds or less.

Since the server will be executing multiple searches in a separate thread, the likelihood is that the code in the JUnit test will complete executing before the searches do. You need a mechanism to hold up processing until the searches are completeuntil the number of searches executed is the same as the number of URLs searched.



Agile Java. Crafting Code with Test-Driven Development
Agile Javaв„ў: Crafting Code with Test-Driven Development
ISBN: 0131482394
EAN: 2147483647
Year: 2003
Pages: 391
Authors: Jeff Langr

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net