10

I'm trying to simulate tests of various run-throughs of my program, setting up a Jetty server in a @Before method and closing it down in an @After.

My first test will run successfully, but upon attempting to POST data in following tests com.sun.jersey.api.client.ClientHandlerException: java.net.SocketException: Software caused connection abort: recv failed occurs. Is there any way I can get my Server (and Client?) to shut down cleanly between tests?

My Before and After code is as follows:

@Before
public void startServer() {
    try {
        server = new Server(8080);
        ServletContextHandler root = new ServletContextHandler(server, "/ingest", ServletContextHandler.SESSIONS);

        root.addServlet(new Servlet(), "/*");

        server.start();

        client = new Client();
        client.setChunkedEncodingSize(16 * 1024);

        FileInputStream stream = new FileInputStream(testFile);
        try {
            client.resource(uri).type(MediaType.APPLICATION_OCTET_STREAM).post(stream);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            Closeables.closeQuietly(stream);
            client.destroy();
        }
    } catch (Exception e) {
        e.printStackTrace();
        fail("Unexpected Exception when starting up server.");
    }
}

@After
public void shutDown() {
    if (output.exists()) {
        output.delete();
    }
    try {
        server.stop();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
Quetzalcoatl
  • 3,037
  • 2
  • 18
  • 27

6 Answers6

8

Best practice in testing scenarios is to not hard code the port. That only leads to conflicts when running elsewhere, especially on CI systems that have even a moderate load or variety of projects.

in Jetty 9 (same idea in 6, 7, 8)

_server = new Server();
_connector = new ServerConnector(_server);  
_server.setConnectors(new Connector[] { _connector });
_server.start();
int port = _connector.getLocalPort();
jesse mcconnell
  • 7,102
  • 1
  • 22
  • 33
2

It turns out that what I had was in fact working, however due to the asynchronous nature of the server.stop(), my new server was attempting to instantiate before the previous server's shut down thread had completely executed.

A simple Thread.sleep(n) after the server.stop() gives the server the time it needs to shut down between tests. Unfortunately, the server seems to prematurely claim that it has stopped thus preventing an exact solution through checking the server state - but perhaps there is something to poll on the server; possibly examining the thread pool could provide a consistent result?

In any case, as this is only for testing purposes, merely starting the server in the @BeforeClass and shutting it down in @AfterClass prevents the whole server shut down kerfuffle, but beware of then starting another server on the same port in your test suite.

Quetzalcoatl
  • 3,037
  • 2
  • 18
  • 27
0

My guess is that it was getting a port conflict. We actually do this for our tests, and surprisingly the performance hit isn't that bad. We began by starting a single server before all tests as the answer suggests, but we had to switch to support mutation testing. One downside to relying on Maven is that you have to start it up on the side to run a single test in an IDE.

For anyone interested, our implementation is here: embedded-test-jetty. It runs multiple servers at once on different ports(for parallel testing), checks port availability, supports SSL, etc.

Kyle Winter
  • 306
  • 2
  • 4
  • 1
    imho, there's no point in asking if anyone is interested in a code solution, if you think it's relevant, just put it up, don't ask :). If it's too long, you could put the link to your repo. – bool.dev Sep 29 '12 at 04:17
  • wasn't sure when we'd get around to moving it out of our internal repo - but it's up there now :) – Kyle Winter Oct 07 '12 at 21:42
0

I handle this using a couple of things. First, after each test, make sure your server is shutdown, and join() on it. Either do this in @After or @AfterClass depending on what you are doing.

server.stop();
server.join();

Next, before each test, make sure the port is available. I use the snippet available at Sockets: Discover port availability using Java

Then, the setup code becomes

public static void waitForPort(int port) {
    while( !available(port) ) {
        try { Thread.sleep(PORT_SLEEP_MILLIS); } 
        catch (InterruptedException e) {}
    }
}

@Before
public void setUp() throws Exception {
    waitForPort(9876);
    waitForPort(9877);

    // Make sure the ports are clear
    Thread.sleep(500);
}

The little extra sleep at the end ensures that the port is available; because just checking that it is available might make the system not reuse it. Another option is to just set SO_REUSEADDR when you are opening the port after having checked it.

Community
  • 1
  • 1
Andrew Mao
  • 35,740
  • 23
  • 143
  • 224
-1

Try:

server = new Server();
    SocketConnector connector = new SocketConnector();
    connector.setPort(8080);
    server.setConnectors(new Connector[] { connector });
    WebAppContext context = new WebAppContext();
    context.setServer(server);
    context.setContextPath("/your-context");
    context.setWar("path to war");
    server.addHandler(context);
    Thread monitor = new MonitorThread();
    monitor.start();
    server.start();
    server.join();

then somewhere you say:

    server.stop()

Helpful article:
http://www.codeproject.com/Articles/128145/Run-Jetty-Web-Server-Within-Your-Application

Edmon
  • 4,752
  • 4
  • 32
  • 42
-1

I realise that this doesn't directly answer your question... but starting and stopping a server in @Before and @After methods is inefficient when you have more than one integration test that requires a server to be running, as the server would be restarted for every test.

You may want to consider starting and stopping your server around your entire suite of tests. If you are using Maven for builds, you can do this with the combination of failsafe and Jetty plugins.

Kkkev
  • 4,716
  • 5
  • 27
  • 43
  • See http://maven.apache.org/plugins/maven-failsafe-plugin/usage.html for an example of using Jetty with Failsafe. – Kkkev Aug 13 '12 at 22:16