ACE-532 - some cleanups and minor improvements:

- make sure that all URLConnections are properly closed in the integration
  tests;
- use a default wait time for URLs to be available (or not);
- this closes #9.



git-svn-id: https://svn.apache.org/repos/asf/ace/trunk@1731916 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.apache.ace.agent.controller.itest/src/org/apache/ace/agent/itest/BaseAgentControllerTest.java b/org.apache.ace.agent.controller.itest/src/org/apache/ace/agent/itest/BaseAgentControllerTest.java
index 5608b31..f33ca69 100644
--- a/org.apache.ace.agent.controller.itest/src/org/apache/ace/agent/itest/BaseAgentControllerTest.java
+++ b/org.apache.ace.agent.controller.itest/src/org/apache/ace/agent/itest/BaseAgentControllerTest.java
@@ -296,11 +296,11 @@
         StubDeploymentServlet servlet = new StubDeploymentServlet(AGENT_ID, package1);
 
         String url = String.format("http://localhost:%d/", TestConstants.PORT);
-        NetUtils.waitForURL(url, 404, 10000);
+        NetUtils.waitForURL_NotFound(url);
 
         m_http.registerServlet("/", servlet, null, null);
 
-        NetUtils.waitForURL(url, 200, 10000);
+        NetUtils.waitForURL(url);
 
         // Tell our agent what controller to use, in this case, we simply disable the controller as we want to invoke
         // everything externally from the AgentControl service...
diff --git a/org.apache.ace.agent.itest/src/org/apache/ace/agent/itest/AgentExtensionTest.java b/org.apache.ace.agent.itest/src/org/apache/ace/agent/itest/AgentExtensionTest.java
index b843fd9..ce39b9a 100644
--- a/org.apache.ace.agent.itest/src/org/apache/ace/agent/itest/AgentExtensionTest.java
+++ b/org.apache.ace.agent.itest/src/org/apache/ace/agent/itest/AgentExtensionTest.java
@@ -114,7 +114,6 @@
     private ServiceRegistration<ConnectionHandler> registerConnectionHandler() {
         return m_bundleContext
             .registerService(ConnectionHandler.class, new ConnectionHandler() {
-
                 @Override
                 public URLConnection getConnection(URL url) throws IOException {
                     return url.openConnection();
diff --git a/org.apache.ace.agent.update.itest/src/org/apache/ace/agent/itest/AgentUpdateTest.java b/org.apache.ace.agent.update.itest/src/org/apache/ace/agent/itest/AgentUpdateTest.java
index e47c3b8..dceda8c 100644
--- a/org.apache.ace.agent.update.itest/src/org/apache/ace/agent/itest/AgentUpdateTest.java
+++ b/org.apache.ace.agent.update.itest/src/org/apache/ace/agent/itest/AgentUpdateTest.java
@@ -137,6 +137,10 @@
         @Override
         protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
             String path = req.getPathInfo();
+            if (path == null) {
+                path = "/";
+            }
+            
             if ("/repository.xml".equals(path)) {
                 PrintWriter w = resp.getWriter();
                 w.println("<?xml version='1.0' encoding='utf-8'?><repository>");
@@ -264,14 +268,14 @@
         m_servlet = new AgentUpdateOBRServlet(currentAgentVersion);
 
         String url = String.format("http://localhost:%d/obr", TestConstants.PORT);
-        NetUtils.waitForURL(url, 404, 10000);
+        NetUtils.waitForURL_NotFound(url);
 
         m_http.registerServlet("/obr", m_servlet, null, null);
         m_http.registerServlet("/auditlog", new DummyAuditLogServlet(), null, null);
         m_http.registerServlet("/deployment", new DeploymentServlet(), null, null);
         m_http.registerServlet("/agent", new DummyAgentVersionServlet(), null, null);
 
-        NetUtils.waitForURL(url, 200, 10000);
+        NetUtils.waitForURL(url);
     }
 
     @Override
diff --git a/org.apache.ace.authentication.itest/src/org/apache/ace/it/authentication/AuthenticationTestBase.java b/org.apache.ace.authentication.itest/src/org/apache/ace/it/authentication/AuthenticationTestBase.java
index 00d443f..7d505de 100644
--- a/org.apache.ace.authentication.itest/src/org/apache/ace/it/authentication/AuthenticationTestBase.java
+++ b/org.apache.ace.authentication.itest/src/org/apache/ace/it/authentication/AuthenticationTestBase.java
@@ -23,18 +23,21 @@
 import java.io.IOException;
 import java.net.HttpURLConnection;
 import java.net.URL;
+import java.net.URLConnection;
 import java.util.Date;
 import java.util.Enumeration;
-
-import junit.framework.Assert;
+import java.util.concurrent.TimeUnit;
 
 import org.apache.ace.connectionfactory.ConnectionFactory;
 import org.apache.ace.it.IntegrationTestBase;
 import org.apache.ace.repository.Repository;
+import org.apache.ace.test.utils.NetUtils;
 import org.osgi.service.log.LogEntry;
 import org.osgi.service.log.LogReaderService;
 import org.osgi.service.useradmin.UserAdmin;
 
+import junit.framework.Assert;
+
 /**
  * Provides a common base class for all authentication integration tests.
  */
@@ -63,11 +66,10 @@
      *             in case of exceptions during the import.
      */
     protected final void importSingleUser(Repository userRepository, String userName, String password) throws Exception {
-        ByteArrayInputStream bis = new ByteArrayInputStream((
-            "<roles>" +
-                "<user name=\"" + userName + "\">" +
-                "<credentials><password>" + password + "</password></credentials>" +
-                "</user>" +
+        ByteArrayInputStream bis = new ByteArrayInputStream(("<roles>" +
+            "<user name=\"" + userName + "\">" +
+            "<credentials><password>" + password + "</password></credentials>" +
+            "</user>" +
             "</roles>").getBytes());
 
         Assert.assertTrue("Committing test user data failed!", userRepository.commit(bis, userRepository.getRange().getHigh()));
@@ -85,34 +87,30 @@
      *            Amount of milliseconds to keep trying to access the URL.
      * @return True if the response of the URL has the specified status code within the specified timeout delay, false
      *         otherwise.
+     * @throws IOException
      * @throws IllegalArgumentException
      *             If the specified URL does not use the HTTP protocol.
      */
-    protected final boolean waitForURL(ConnectionFactory connectionFactory, URL url, int responseCode, int timeout) {
-        long deadline = System.currentTimeMillis() + timeout;
-        while (System.currentTimeMillis() < deadline) {
-            HttpURLConnection connection = null;
-            try {
-                connection = (HttpURLConnection) connectionFactory.createConnection(url);
+    protected final boolean waitForURL(ConnectionFactory connectionFactory, URL url, int responseCode) throws IOException {
+        URLConnection conn = null;
 
-                int respCode = ((HttpURLConnection) connection).getResponseCode();
-                if (respCode == responseCode) {
+        int tries = 4;
+        while (tries-- > 0) {
+            conn = connectionFactory.createConnection(url);
+            try {
+                boolean result = ((HttpURLConnection) conn).getResponseCode() == responseCode;
+                if (result) {
                     return true;
                 }
-                else {
-                    System.err.println("Got response code " + respCode + " for " + url);
+                try {
+                    TimeUnit.MILLISECONDS.sleep(250);
+                }
+                catch (InterruptedException exception) {
+                    return false;
                 }
             }
-            catch (IOException ioe) {
-                if (connection != null) {
-                    connection.disconnect();
-                }
-            }
-            try {
-                Thread.sleep(100);
-            }
-            catch (InterruptedException ie) {
-                return false;
+            finally {
+                NetUtils.closeConnection(conn);
             }
         }
         return false;
diff --git a/org.apache.ace.authentication.itest/src/org/apache/ace/it/authentication/LogAuthenticationTest.java b/org.apache.ace.authentication.itest/src/org/apache/ace/it/authentication/LogAuthenticationTest.java
index d42cafc..8a5a874 100644
--- a/org.apache.ace.authentication.itest/src/org/apache/ace/it/authentication/LogAuthenticationTest.java
+++ b/org.apache.ace.authentication.itest/src/org/apache/ace/it/authentication/LogAuthenticationTest.java
@@ -18,9 +18,11 @@
  */
 package org.apache.ace.it.authentication;
 
+import java.io.BufferedReader;
 import java.io.IOException;
-import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.net.URL;
+import java.net.URLConnection;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -38,6 +40,7 @@
 import org.apache.ace.repository.Repository;
 import org.apache.ace.repository.RepositoryConstants;
 import org.apache.ace.test.constants.TestConstants;
+import org.apache.ace.test.utils.NetUtils;
 import org.apache.felix.dm.Component;
 import org.osgi.framework.Constants;
 import org.osgi.service.http.HttpService;
@@ -87,7 +90,7 @@
         try {
             String baseURL = "http://" + HOST + ":" + TestConstants.PORT;
             URL testURL = new URL(baseURL.concat(AUDITLOG_ENDPOINT));
-            assertTrue("Failed to access auditlog in time!", waitForURL(m_connectionFactory, testURL, 403, 15000));
+            assertTrue("Failed to access auditlog in time!", waitForURL(m_connectionFactory, testURL, 403));
 
             String userName = "d";
             String password = "f";
@@ -101,7 +104,7 @@
                 "authentication.user.name", userName,
                 "authentication.user.password", password);
 
-            assertTrue("Failed to access auditlog in time!", waitForURL(m_connectionFactory, testURL, 200, 15000));
+            assertTrue("Failed to access auditlog in time!", waitForURL(m_connectionFactory, testURL, 200));
         }
         catch (Exception e) {
             printLog(m_logReader);
@@ -148,31 +151,20 @@
     @Override
     protected List<String> getResponse(String request) throws IOException {
         List<String> result = new ArrayList<>();
-        InputStream in = null;
-        try {
-            in = m_connectionFactory.createConnection(new URL(request)).getInputStream();
-            byte[] response = new byte[in.available()];
-            in.read(response);
 
-            StringBuilder element = new StringBuilder();
-            for (byte b : response) {
-                switch (b) {
-                    case '\n':
-                        result.add(element.toString());
-                        element = new StringBuilder();
-                        break;
-                    default:
-                        element.append(b);
+        URLConnection conn = m_connectionFactory.createConnection(new URL(request));
+        try (InputStreamReader in = new InputStreamReader(conn.getInputStream()); BufferedReader reader = new BufferedReader(in)) {
+            String line;
+            do {
+                line = reader.readLine();
+                if (line != null) {
+                    result.add(line);
                 }
             }
+            while (line != null);
         }
         finally {
-            try {
-                in.close();
-            }
-            catch (Exception e) {
-                // no problem.
-            }
+            NetUtils.closeConnection(conn);
         }
         return result;
     }
diff --git a/org.apache.ace.authentication.itest/src/org/apache/ace/it/authentication/ObrAuthenticationTest.java b/org.apache.ace.authentication.itest/src/org/apache/ace/it/authentication/ObrAuthenticationTest.java
index b2ee8c4..7a33c35 100644
--- a/org.apache.ace.authentication.itest/src/org/apache/ace/it/authentication/ObrAuthenticationTest.java
+++ b/org.apache.ace.authentication.itest/src/org/apache/ace/it/authentication/ObrAuthenticationTest.java
@@ -22,6 +22,7 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.net.HttpURLConnection;
 import java.net.URL;
 import java.net.URLConnection;
 import java.util.jar.Attributes;
@@ -123,7 +124,7 @@
 
             URL testURL = new URL(m_obrURL, "index.xml");
 
-            assertTrue("Failed to access OBR in time!", waitForURL(m_connectionFactory, testURL, 403, 15000));
+            assertTrue("Failed to access OBR in time!", waitForURL(m_connectionFactory, testURL, 403));
 
             m_authConfigPID = configureFactory("org.apache.ace.connectionfactory",
                 "authentication.baseURL", m_obrURL.toExternalForm(),
@@ -131,7 +132,7 @@
                 "authentication.user.name", userName,
                 "authentication.user.password", password);
 
-            assertTrue("Failed to access OBR in time!", waitForURL(m_connectionFactory, testURL, 200, 15000));
+            assertTrue("Failed to access OBR in time!", waitForURL(m_connectionFactory, testURL, 200));
         }
         catch (Exception e) {
             printLog(m_logReader);
@@ -154,9 +155,10 @@
      * Test that we can retrieve the 'index.xml' from the OBR.
      */
     public void testAccessObrRepositoryWithCredentialsOk() throws Exception {
+        URL url = new URL("http://localhost:" + TestConstants.PORT + m_endpoint + "/index.xml");
+        URLConnection conn = null;
         try {
-            URL url = new URL("http://localhost:" + TestConstants.PORT + m_endpoint + "/index.xml");
-            URLConnection conn = m_connectionFactory.createConnection(url);
+            conn = m_connectionFactory.createConnection(url);
             assertNotNull(conn);
             Object content = conn.getContent();
             assertNotNull(content);
@@ -164,6 +166,8 @@
         catch (Exception e) {
             printLog(m_logReader);
             throw e;
+        } finally {
+            NetUtils.closeConnection(conn);
         }
     }
 
@@ -178,8 +182,8 @@
             URLConnection conn = url.openConnection();
             assertNotNull(conn);
 
-            // we expect a 401 for this URL...
-            NetUtils.waitForURL(url, 401, 15000);
+            // we expect a 403 for this URL...
+            assertTrue(NetUtils.waitForURL(url, HttpURLConnection.HTTP_FORBIDDEN));
 
             try {
                 // ...causing all other methods on URLConnection to fail...
@@ -189,6 +193,9 @@
             catch (IOException exception) {
                 // Ok; ignored...
             }
+            finally {
+                NetUtils.closeConnection(conn);
+            }
         }
         catch (Exception e) {
             printLog(m_logReader);
@@ -215,8 +222,8 @@
             URLConnection conn = url.openConnection();
             assertNotNull(conn);
 
-            // we expect a 401 for this URL...
-            NetUtils.waitForURL(url, 401, 5000);
+            // we expect a 403 for this URL...
+            assertTrue(NetUtils.waitForURL(url, HttpURLConnection.HTTP_FORBIDDEN));
 
             try {
                 // ...causing all other methods on URLConnection to fail...
@@ -226,6 +233,9 @@
             catch (IOException exception) {
                 // Ok; ignored...
             }
+            finally {
+                NetUtils.closeConnection(conn);
+            }
         }
         catch (Exception e) {
             printLog(m_logReader);
diff --git a/org.apache.ace.client.repository.itest/bnd.bnd b/org.apache.ace.client.repository.itest/bnd.bnd
index 340aabf..6f62820 100644
--- a/org.apache.ace.client.repository.itest/bnd.bnd
+++ b/org.apache.ace.client.repository.itest/bnd.bnd
@@ -34,6 +34,10 @@
 	org.apache.felix.useradmin,\
 	org.apache.felix.useradmin.filestore,\
 	org.apache.felix.log,\
+	org.apache.felix.gogo.runtime,\
+	org.apache.felix.gogo.command,\
+	org.apache.felix.gogo.shell,\
+	org.apache.felix.dependencymanager.shell,\
 	org.apache.ace.test;version=latest,\
 	org.apache.ace.authentication.api;version=latest,\
 	org.apache.ace.connectionfactory;version=latest,\
diff --git a/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/BaseRepositoryAdminTest.java b/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/BaseRepositoryAdminTest.java
index 349f5ff..8fc71f0 100644
--- a/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/BaseRepositoryAdminTest.java
+++ b/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/BaseRepositoryAdminTest.java
@@ -20,7 +20,6 @@
 
 import java.io.IOException;
 import java.net.ConnectException;
-import java.net.HttpURLConnection;
 import java.net.URL;
 import java.util.Arrays;
 import java.util.Dictionary;
@@ -59,6 +58,7 @@
 import org.apache.ace.repository.Repository;
 import org.apache.ace.repository.RepositoryConstants;
 import org.apache.ace.test.constants.TestConstants;
+import org.apache.ace.test.utils.NetUtils;
 import org.apache.felix.dm.Component;
 import org.apache.felix.dm.ComponentState;
 import org.apache.felix.dm.ComponentStateListener;
@@ -139,15 +139,8 @@
 
         // Wait for the endpoint to respond.
         URL repoURL = new URL(baseURL + "index.xml");
-        int response = ((HttpURLConnection) repoURL.openConnection()).getResponseCode();
-        int tries = 0;
-        while ((response != 200) && (tries++ < 50)) {
-            response = ((HttpURLConnection) repoURL.openConnection()).getResponseCode();
-            Thread.sleep(100); // If we get interrupted, there will be a good reason for it.
-        }
-        if (tries == 50) {
-            throw new IOException("The OBR servlet does not seem to be responding well. Last response code: " + response);
-        }
+
+        assertTrue("The OBR servlet does not seem to be responding well!", NetUtils.waitForURL(repoURL));
     }
 
     /* Configure a new repository instance */
diff --git a/org.apache.ace.client.rest.itest/src/org/apache/ace/client/rest/itest/RESTClientTest.java b/org.apache.ace.client.rest.itest/src/org/apache/ace/client/rest/itest/RESTClientTest.java
index 203f9a2..472d7e1 100644
--- a/org.apache.ace.client.rest.itest/src/org/apache/ace/client/rest/itest/RESTClientTest.java
+++ b/org.apache.ace.client.rest.itest/src/org/apache/ace/client/rest/itest/RESTClientTest.java
@@ -317,7 +317,7 @@
             createServerUser();
 
             // Wait until our RESTClientServlet is up and responding...
-            NetUtils.waitForURL(HOST, 200, 1000);
+            NetUtils.waitForURL(HOST);
 
             m_hasBeenSetup = true;
         }
diff --git a/org.apache.ace.deployment.itest/src/org/apache/ace/it/deployment/Ace330Test.java b/org.apache.ace.deployment.itest/src/org/apache/ace/it/deployment/Ace330Test.java
index 168c2b7..74ecef6 100644
--- a/org.apache.ace.deployment.itest/src/org/apache/ace/it/deployment/Ace330Test.java
+++ b/org.apache.ace.deployment.itest/src/org/apache/ace/it/deployment/Ace330Test.java
@@ -94,7 +94,7 @@
     @Override
     protected void configureAdditionalServices() throws Exception {
         // Wait until one of important repositories is online...
-        NetUtils.waitForURL(String.format("%s/repository/query?customer=%s&name=deployment", m_host, TEST_CUSTOMER), 200, 100);
+        NetUtils.waitForURL(String.format("%s/repository/query?customer=%s&name=deployment", m_host, TEST_CUSTOMER));
     }
 
     protected Component[] getDependencies() {
diff --git a/org.apache.ace.repository.itest/src/org/apache/ace/it/repository/RepositoryTest.java b/org.apache.ace.repository.itest/src/org/apache/ace/it/repository/RepositoryTest.java
index be57757..099e33f 100644
--- a/org.apache.ace.repository.itest/src/org/apache/ace/it/repository/RepositoryTest.java
+++ b/org.apache.ace.repository.itest/src/org/apache/ace/it/repository/RepositoryTest.java
@@ -36,6 +36,7 @@
 import org.apache.ace.it.IntegrationTestBase;
 import org.apache.ace.repository.Repository;
 import org.apache.ace.test.constants.TestConstants;
+import org.apache.ace.test.utils.NetUtils;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.cm.Configuration;
@@ -61,13 +62,22 @@
 
         URL url = new URL(m_host, "replication/query?customer=apache&name=test&filter=test");
         HttpURLConnection connection = (HttpURLConnection) url.openConnection();
-        int responseCode = connection.getResponseCode();
-        assertResponseCode(HttpURLConnection.HTTP_BAD_REQUEST, responseCode);
+        try {
+            int responseCode = connection.getResponseCode();
+            assertResponseCode(HttpURLConnection.HTTP_BAD_REQUEST, responseCode);
+        } finally {
+            NetUtils.closeConnection(connection);
+        }
 
         url = new URL(m_host, "repository/query?customer=apache&name=test&filter=test");
+        
         connection = (HttpURLConnection) url.openConnection();
-        responseCode = connection.getResponseCode();
-        assertResponseCode(HttpURLConnection.HTTP_BAD_REQUEST, responseCode);
+        try {
+            int responseCode = connection.getResponseCode();
+            assertResponseCode(HttpURLConnection.HTTP_BAD_REQUEST, responseCode);
+        } finally {
+            NetUtils.closeConnection(connection);
+        }
 
         removeRepository("testInstance");
     }
diff --git a/org.apache.ace.repository.itest/src/org/apache/ace/it/repository/Utils.java b/org.apache.ace.repository.itest/src/org/apache/ace/it/repository/Utils.java
index 5de92d1..abfbde4 100644
--- a/org.apache.ace.repository.itest/src/org/apache/ace/it/repository/Utils.java
+++ b/org.apache.ace.repository.itest/src/org/apache/ace/it/repository/Utils.java
@@ -20,13 +20,15 @@
 package org.apache.ace.it.repository;
 
 import java.io.Closeable;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.net.ConnectException;
 import java.net.HttpURLConnection;
 import java.net.URL;
 
+import org.apache.ace.test.utils.NetUtils;
+
 final class Utils {
 
     private static final int COPY_BUFFER_SIZE = 4096;
@@ -44,22 +46,7 @@
     }
 
     static void closeSilently(HttpURLConnection resource) {
-        if (resource != null) {
-            try {
-                flushStream(resource.getInputStream());
-            }
-            catch (IOException exception) {
-                // Ignore...
-            }
-            try {
-                InputStream es = resource.getErrorStream();
-                if (es != null) {
-                    flushStream(es);
-                }
-            } finally {
-                resource.disconnect();
-            }
-        }
+        NetUtils.closeConnection(resource);
     }
 
     /* copy in to out */
@@ -99,8 +86,9 @@
 
             responseCode = connection.getResponseCode();
         }
-        catch (IOException e) {
-            responseCode = handleIOException(connection);
+        catch (FileNotFoundException e) {
+            // Ignore...
+            responseCode = HttpURLConnection.HTTP_NOT_FOUND;
         }
         finally {
             closeSilently(connection);
@@ -109,22 +97,6 @@
         return responseCode;
     }
 
-    /**
-     * @see http://docs.oracle.com/javase/6/docs/technotes/guides/net/http-keepalive.html
-     */
-    static int handleIOException(HttpURLConnection conn) {
-        int respCode = -2;
-        try {
-            respCode = conn.getResponseCode();
-            flushStream(conn.getErrorStream());
-        }
-        catch (IOException ex) {
-            // deal with the exception
-            ex.printStackTrace();
-        }
-        return respCode;
-    }
-
     static int put(URL host, String endpoint, String customer, String name, String version, InputStream in) throws IOException {
         URL url = new URL(host, endpoint + "?customer=" + customer + "&name=" + name + "&version=" + version);
 
@@ -139,14 +111,13 @@
 
         try (OutputStream out = connection.getOutputStream()) {
             copy(in, out);
-
             out.flush();
 
             rc = connection.getResponseCode();
             flushStream(connection.getInputStream());
         }
         catch (IOException e) {
-            rc = handleIOException(connection);
+            rc = connection.getResponseCode();
         }
         finally {
             closeSilently(in);
@@ -171,9 +142,6 @@
 
             responseCode = connection.getResponseCode();
         }
-        catch (IOException e) {
-            responseCode = handleIOException(connection);
-        }
         finally {
             closeSilently(out);
             closeSilently(connection);
@@ -183,37 +151,8 @@
     }
 
     static void waitForWebserver(URL host) throws IOException {
-        int retries = 1, rc = -1;
-        IOException ioe = null;
-        while (retries++ < 10) {
-            HttpURLConnection connection = openConnection(host);
-            try {
-                rc = connection.getResponseCode();
-                if (rc >= 0) {
-                    return;
-                }
-            }
-            catch (ConnectException e) {
-                ioe = e;
-                try {
-                    Thread.sleep(retries * 50);
-                }
-                catch (InterruptedException ie) {
-                    // We're asked to stop...
-                    return;
-                }
-            }
-            catch (IOException e) {
-                rc = handleIOException(connection);
-            }
-            finally {
-                if (connection != null) {
-                    connection.disconnect();
-                }
-            }
-        }
-        if (ioe != null) {
-            throw ioe;
+        if (!NetUtils.waitForURL(host, 404)) {
+            throw new IOException("URL " + host + " did not respond in time?!");
         }
     }
 
@@ -225,7 +164,6 @@
         conn.setUseCaches(false);
         conn.setConnectTimeout(1000);
         conn.setReadTimeout(1000);
-
         return conn;
     }
 }
diff --git a/org.apache.ace.test/bnd.bnd b/org.apache.ace.test/bnd.bnd
index 8f32414..2e0d13d 100644
--- a/org.apache.ace.test/bnd.bnd
+++ b/org.apache.ace.test/bnd.bnd
@@ -15,7 +15,7 @@
 	org.apache.felix.service.command;resolution:=optional,\
 	*
 
-Bundle-Version: 1.2.0
+Bundle-Version: 1.3.0
 Bundle-Name: Apache ACE Test
 Bundle-Description: Provides base classes and utils for integration tests
 Private-Package: org.apache.ace.it.gogo
diff --git a/org.apache.ace.test/src/org/apache/ace/it/IntegrationTestBase.java b/org.apache.ace.test/src/org/apache/ace/it/IntegrationTestBase.java
index bf6c0f0..f138f6e 100644
--- a/org.apache.ace.test/src/org/apache/ace/it/IntegrationTestBase.java
+++ b/org.apache.ace.test/src/org/apache/ace/it/IntegrationTestBase.java
@@ -26,6 +26,7 @@
 import java.io.InputStream;
 import java.lang.reflect.Method;
 import java.net.URL;
+import java.net.URLConnection;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Dictionary;
@@ -37,6 +38,7 @@
 import java.util.concurrent.TimeUnit;
 
 import org.apache.ace.test.constants.TestConstants;
+import org.apache.ace.test.utils.NetUtils;
 import org.apache.felix.dm.Component;
 import org.apache.felix.dm.ComponentDependencyDeclaration;
 import org.apache.felix.dm.ComponentState;
@@ -430,10 +432,8 @@
      */
     protected List<String> getResponse(URL requestURL) throws IOException {
         List<String> result = new ArrayList<>();
-        InputStream in = null;
-        try {
-            in = requestURL.openConnection().getInputStream();
-
+        URLConnection conn = requestURL.openConnection();
+        try (InputStream in = conn.getInputStream()) {
             final StringBuilder element = new StringBuilder();
             int b;
             while ((b = in.read()) > 0) {
@@ -451,12 +451,7 @@
             }
         }
         finally {
-            try {
-                in.close();
-            }
-            catch (Exception e) {
-                // no problem.
-            }
+            NetUtils.closeConnection(conn);
         }
         return result;
     }
@@ -570,7 +565,7 @@
     protected int countServices(Class<?> type) throws IOException, InvalidSyntaxException {
         return countServices(String.format("(%s=%s)", Constants.OBJECTCLASS, type.getName()));
     }
-    
+
     /**
      * @param filter
      * @return the number of services that match the given filter, &gt;= 0.
@@ -626,8 +621,9 @@
             if (!listener.waitForEmpty(SERVICE_TIMEOUT, SECONDS)) {
                 fail("Not all components were started. Still missing the following:\n" + listener.componentsString());
             }
-            
-            // XXX it appears we run into race conditions between the setup and configuration of our services, use a little delay to get things settled seems to help here...
+
+            // XXX it appears we run into race conditions between the setup and configuration of our services, use a
+            // little delay to get things settled seems to help here...
             TimeUnit.MILLISECONDS.sleep(500);
 
             configureAdditionalServices();
diff --git a/org.apache.ace.test/src/org/apache/ace/test/utils/NetUtils.java b/org.apache.ace.test/src/org/apache/ace/test/utils/NetUtils.java
index f3b5dc1..0400f8a 100644
--- a/org.apache.ace.test/src/org/apache/ace/test/utils/NetUtils.java
+++ b/org.apache.ace.test/src/org/apache/ace/test/utils/NetUtils.java
@@ -19,14 +19,93 @@
 package org.apache.ace.test.utils;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.net.URLConnection;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Class containing utility methods concerning network related stuff.
  */
 public class NetUtils {
+    public static final int DEFAULT_WAIT_TIMEOUT = 2500;
+
+    /**
+     * Closes the given URL connection and ensures that its state is properly flushed.
+     * 
+     * @param connection
+     *            the URL connection to close, may be <code>null</code>.
+     */
+    public static void closeConnection(URLConnection connection) {
+        if (connection instanceof HttpURLConnection) {
+            HttpURLConnection conn = (HttpURLConnection) connection;
+            try {
+                if (conn.getErrorStream() != null) {
+                    flush(conn.getErrorStream());
+                }
+                if (conn.getInputStream() != null) {
+                    flush(conn.getInputStream());
+                }
+            }
+            catch (IOException exception) {
+                // Ignore... Not much we can do about this here...
+            }
+            finally {
+                conn.disconnect();
+            }
+        }
+    }
+
+    /**
+     * Flushes the given input stream by reading its contents until an end-of-file marker is found.
+     * 
+     * @param is
+     *            the input stream to flush, cannot be <code>null</code>.
+     * @throws IOException
+     *             in case of I/O problems reading from the given input stream.
+     */
+    public static void flush(InputStream is) throws IOException {
+        final byte[] buf = new byte[4096];
+        int read = 0;
+        do {
+            read = is.read(buf);
+        }
+        while (read > 0);
+    }
+
+    /**
+     * Waits for a HTTP URL to become 'available', will retry every 100 milliseconds until it is available or timeout
+     * has been exceeded. Available in this context means a status code of "200" is returned when accessing the URL.
+     * 
+     * @param url
+     *            HTTP URL that should be tested for availability.
+     * @return <code>true</code> if the response of the URL has the specified status code within the specified timeout
+     *         delay, <code>false</code> otherwise.
+     * @throws IllegalArgumentException
+     *             If the specified URL does not use the HTTP protocol.
+     */
+    public static boolean waitForURL(String url) throws MalformedURLException {
+        return waitForURL(new URL(url));
+    }
+
+    /**
+     * Waits for a HTTP URL to become 'available', will retry every 100 milliseconds until it is available or timeout
+     * has been exceeded. Available in this context means the specified status code is returned when accessing the URL.
+     * 
+     * @param url
+     *            HTTP URL that should be tested for availability.
+     * @param responseCode
+     *            The response code to be expected on the specified URL when it is available.
+     * @return True if the response of the URL has the specified status code within the specified timeout delay, false
+     *         otherwise.
+     * @throws IllegalArgumentException
+     *             If the specified URL does not use the HTTP protocol.
+     */
+    public static boolean waitForURL(String url, int responseCode) throws MalformedURLException {
+        return waitForURL(new URL(url), responseCode);
+    }
 
     /**
      * Waits for a HTTP URL to become 'available', will retry every 100 milliseconds until it is available or timeout
@@ -49,6 +128,38 @@
 
     /**
      * Waits for a HTTP URL to become 'available', will retry every 100 milliseconds until it is available or timeout
+     * has been exceeded. Available in this context means a status code of "200" is returned when accessing the URL.
+     * 
+     * @param url
+     *            HTTP URL that should be tested for availability.
+     * @return <code>true</code> if the response of the URL has the specified status code within the specified timeout
+     *         delay, <code>false</code> otherwise.
+     * @throws IllegalArgumentException
+     *             If the specified URL does not use the HTTP protocol.
+     */
+    public static boolean waitForURL(URL url) {
+        return waitForURL(url, HttpURLConnection.HTTP_OK, DEFAULT_WAIT_TIMEOUT);
+    }
+
+    /**
+     * Waits for a HTTP URL to become 'available', will retry every 100 milliseconds until it is available or timeout
+     * has been exceeded. Available in this context means the specified status code is returned when accessing the URL.
+     * 
+     * @param url
+     *            HTTP URL that should be tested for availability.
+     * @param responseCode
+     *            The response code to be expected on the specified URL when it is available.
+     * @return True if the response of the URL has the specified status code within the specified timeout delay, false
+     *         otherwise.
+     * @throws IllegalArgumentException
+     *             If the specified URL does not use the HTTP protocol.
+     */
+    public static boolean waitForURL(URL url, int responseCode) {
+        return waitForURL(url, responseCode, DEFAULT_WAIT_TIMEOUT);
+    }
+
+    /**
+     * Waits for a HTTP URL to become 'available', will retry every 100 milliseconds until it is available or timeout
      * has been exceeded. Available in this context means the specified status code is returned when accessing the URL.
      * 
      * @param url
@@ -65,22 +176,31 @@
     public static boolean waitForURL(URL url, int responseCode, int timeout) {
         long deadline = System.currentTimeMillis() + timeout;
         while (System.currentTimeMillis() < deadline) {
+            URLConnection connection = null;
             try {
-                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
-                connection.setRequestMethod("HEAD");
-                int rc = connection.getResponseCode();
+                connection = url.openConnection();
+                ((HttpURLConnection) connection).setRequestMethod("HEAD");
+                connection.setAllowUserInteraction(false);
+                connection.setUseCaches(false);
+
+                int rc = ((HttpURLConnection) connection).getResponseCode();
                 if (rc == responseCode) {
                     return true;
                 }
+                System.out.printf("Waiting for URL %s: %d (want %d)%n", connection.getURL(), rc, responseCode);
             }
             catch (ClassCastException cce) {
-                throw new IllegalArgumentException("Expected url to be an HTTP url, not: " + url.toString(), cce);
+                throw new IllegalArgumentException("Expected url to be an HTTP url, not: " + connection.getURL(), cce);
             }
             catch (IOException ioe) {
                 // retry
             }
+            finally {
+                closeConnection(connection);
+            }
+
             try {
-                Thread.sleep(100);
+                TimeUnit.MILLISECONDS.sleep(250);
             }
             catch (InterruptedException ie) {
                 return false;
@@ -88,4 +208,34 @@
         }
         return false;
     }
+
+    /**
+     * Waits for a HTTP URL to become 'available', will retry every 100 milliseconds until it is available or timeout
+     * has been exceeded. Available in this context means a status code of 404 is returned when accessing the URL.
+     * 
+     * @param url
+     *            HTTP URL that should be tested for availability.
+     * @return <code>true</code> if the response of the URL has the specified status code within the specified timeout
+     *         delay, <code>false</code> otherwise.
+     * @throws IllegalArgumentException
+     *             If the specified URL does not use the HTTP protocol.
+     */
+    public static boolean waitForURL_NotFound(String url) throws MalformedURLException {
+        return waitForURL_NotFound(new URL(url));
+    }
+
+    /**
+     * Waits for a HTTP URL to become 'available', will retry every 100 milliseconds until it is available or timeout
+     * has been exceeded. Available in this context means a status code of 404 is returned when accessing the URL.
+     * 
+     * @param url
+     *            HTTP URL that should be tested for availability.
+     * @return <code>true</code> if the response of the URL has the specified status code within the specified timeout
+     *         delay, <code>false</code> otherwise.
+     * @throws IllegalArgumentException
+     *             If the specified URL does not use the HTTP protocol.
+     */
+    public static boolean waitForURL_NotFound(URL url) {
+        return waitForURL(url, HttpURLConnection.HTTP_NOT_FOUND, DEFAULT_WAIT_TIMEOUT);
+    }
 }
diff --git a/org.apache.ace.test/src/org/apache/ace/test/utils/packageinfo b/org.apache.ace.test/src/org/apache/ace/test/utils/packageinfo
index d96c0b8..9764274 100644
--- a/org.apache.ace.test/src/org/apache/ace/test/utils/packageinfo
+++ b/org.apache.ace.test/src/org/apache/ace/test/utils/packageinfo
@@ -1 +1 @@
-version 1.2.0
\ No newline at end of file
+version 1.3.0
\ No newline at end of file
diff --git a/org.apache.ace.useradmin.itest/src/org/apache/ace/it/useradmin/UserAdminRepositoryTest.java b/org.apache.ace.useradmin.itest/src/org/apache/ace/it/useradmin/UserAdminRepositoryTest.java
index 9487eb2..97e581c 100644
--- a/org.apache.ace.useradmin.itest/src/org/apache/ace/it/useradmin/UserAdminRepositoryTest.java
+++ b/org.apache.ace.useradmin.itest/src/org/apache/ace/it/useradmin/UserAdminRepositoryTest.java
@@ -31,6 +31,7 @@
 import org.apache.ace.range.SortedRangeSet;
 import org.apache.ace.repository.Repository;
 import org.apache.ace.test.constants.TestConstants;
+import org.apache.ace.test.utils.NetUtils;
 import org.apache.felix.dm.Component;
 import org.osgi.service.useradmin.Role;
 import org.osgi.service.useradmin.UserAdmin;
@@ -65,7 +66,7 @@
             assertEquals(200, conn.getResponseCode());
         }
         finally {
-            conn.disconnect();
+            NetUtils.closeConnection(conn);
         }
 
         try {
diff --git a/org.apache.ace.useradmin.itest/src/org/apache/ace/it/useradmin/Utils.java b/org.apache.ace.useradmin.itest/src/org/apache/ace/it/useradmin/Utils.java
index 834ea0d..1228f68 100644
--- a/org.apache.ace.useradmin.itest/src/org/apache/ace/it/useradmin/Utils.java
+++ b/org.apache.ace.useradmin.itest/src/org/apache/ace/it/useradmin/Utils.java
@@ -27,6 +27,8 @@
 import java.net.HttpURLConnection;
 import java.net.URL;
 
+import org.apache.ace.test.utils.NetUtils;
+
 final class Utils {
 
     private static final int COPY_BUFFER_SIZE = 4096;
@@ -44,9 +46,7 @@
     }
 
     static void closeSilently(HttpURLConnection resource) {
-        if (resource != null) {
-            resource.disconnect();
-        }
+        NetUtils.closeConnection(resource);
     }
 
     /* copy in to out */