Author: dulvac Date: Tue May 16 12:34:17 2017 New Revision: 1795303 URL: http://svn.apache.org/viewvc?rev=1795303&view=rev Log: SLING-6853 Improve polling capabilities in o.a.s.testing.clients thanks @volteanu for the contribution!
Modified: sling/trunk/testing/http/clients/pom.xml sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/SlingClient.java sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/html/package-info.java sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/BundlesInstaller.java sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/OsgiConsoleClient.java sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/OsgiInstanceConfig.java sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/package-info.java sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/package-info.java sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/TimeoutsProvider.java sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/config/InstanceConfig.java sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/config/impl/InstanceConfigCacheImpl.java sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/AbstractPoller.java sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/PathPoller.java sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/Poller.java sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/package-info.java Modified: sling/trunk/testing/http/clients/pom.xml URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/pom.xml?rev=1795303&r1=1795302&r2=1795303&view=diff ============================================================================== --- sling/trunk/testing/http/clients/pom.xml (original) +++ sling/trunk/testing/http/clients/pom.xml Tue May 16 12:34:17 2017 @@ -28,7 +28,7 @@ </parent> <artifactId>org.apache.sling.testing.clients</artifactId> - <version>1.0.2-SNAPSHOT</version> + <version>1.1.0-SNAPSHOT</version> <packaging>bundle</packaging> <name>Apache Sling Testing Clients</name> @@ -149,5 +149,12 @@ <artifactId>org.apache.sling.hapi.client</artifactId> <version>1.0.0</version> </dependency> + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpclient</artifactId> + <version>4.5.3</version> + <classifier>tests</classifier> + <scope>test</scope> + </dependency> </dependencies> </project> Modified: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/SlingClient.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/SlingClient.java?rev=1795303&r1=1795302&r2=1795303&view=diff ============================================================================== --- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/SlingClient.java (original) +++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/SlingClient.java Tue May 16 12:34:17 2017 @@ -30,19 +30,20 @@ import org.apache.http.entity.ContentTyp import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; - import org.apache.sling.testing.clients.interceptors.DelayRequestInterceptor; import org.apache.sling.testing.clients.interceptors.TestDescriptionInterceptor; import org.apache.sling.testing.clients.util.FormEntityBuilder; import org.apache.sling.testing.clients.util.HttpUtils; import org.apache.sling.testing.clients.util.JsonUtils; import org.apache.sling.testing.clients.util.poller.AbstractPoller; +import org.apache.sling.testing.clients.util.poller.Polling; import org.codehaus.jackson.JsonNode; import java.io.File; import java.net.URI; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeoutException; import static org.apache.http.HttpStatus.SC_CREATED; import static org.apache.http.HttpStatus.SC_OK; @@ -212,12 +213,16 @@ public class SlingClient extends Abstrac /** * <p>Checks whether a path exists or not by making a GET request to that path with the {@code json extension} </p> * <p>It polls the server and waits until the path exists </p> + * + * @deprecated use {@link #waitExists(String, long, long)} instead. + * * @param path path to be checked * @param waitMillis time to wait between retries * @param retryCount number of retries before throwing an exception * @throws ClientException if the path was not found * @throws InterruptedException to mark this operation as "waiting" */ + @Deprecated public void waitUntilExists(final String path, final long waitMillis, int retryCount) throws ClientException, InterruptedException { AbstractPoller poller = new AbstractPoller(waitMillis, retryCount) { @@ -244,6 +249,34 @@ public class SlingClient extends Abstrac } /** + * <p>Waits until a path exists by making successive GET requests to that path with the {@code json extension} </p> + * <p>Polls the server until the path exists or until timeout is reached </p> + * @param path path to be checked + * @param timeout max total time to wait, in milliseconds + * @param delay time to wait between checks, in milliseconds + * @throws TimeoutException if the path was not found before timeout + * @throws InterruptedException to mark this operation as "waiting", should be rethrown by callers + * @since 1.1.0 + */ + public void waitExists(final String path, final long timeout, final long delay) + throws TimeoutException, InterruptedException { + + Polling p = new Polling() { + @Override + public Boolean call() throws Exception { + return exists(path); + } + + @Override + protected String message() { + return "Path " + path + " does not exist after %1$d ms"; + } + }; + + p.poll(timeout, delay); + } + + /** * Sets String component property on a node. * * @param nodePath path to the node to be edited @@ -303,12 +336,14 @@ public class SlingClient extends Abstrac * Returns the JSON content of a node already mapped to a {@link org.codehaus.jackson.JsonNode}.<br> * Waits max 10 seconds for the node to be created. * + * @deprecated use {@link #waitExists(String, long, long)} and {@link #doGetJson(String, int, int...)} instead * @param path the path to the content node * @param depth the number of levels to go down the tree, -1 for infinity * @return a {@link org.codehaus.jackson.JsonNode} mapping to the requested content node. * @throws ClientException if something fails during request/response processing * @throws InterruptedException to mark this operation as "waiting" */ + @Deprecated public JsonNode getJsonNode(String path, int depth) throws ClientException, InterruptedException { return getJsonNode(path, depth, 500, 20); } @@ -316,6 +351,7 @@ public class SlingClient extends Abstrac /** * Returns JSON format of a content node already mapped to a {@link org.codehaus.jackson.JsonNode}. * + * @deprecated use {@link #waitExists(String, long, long)} and {@link #doGetJson(String, int, int...)} instead * @param path the path to the content node * @param depth the number of levels to go down the tree, -1 for infinity * @param waitMillis how long it should wait between requests @@ -326,6 +362,7 @@ public class SlingClient extends Abstrac * @throws ClientException if something fails during request/response cycle * @throws InterruptedException to mark this operation as "waiting" */ + @Deprecated public JsonNode getJsonNode(String path, int depth, final long waitMillis, final int retryNumber, int... expectedStatus) throws ClientException, InterruptedException { @@ -347,6 +384,31 @@ public class SlingClient extends Abstrac } /** + * Returns the {@link org.codehaus.jackson.JsonNode} object corresponding to a content node. + * + * @param path the path to the content node + * @param depth the number of levels to go down the tree, -1 for infinity + * @param expectedStatus list of allowed HTTP Status to be returned. If not set, 200 (OK) is assumed. + * + * @return a {@link org.codehaus.jackson.JsonNode} mapping to the requested content node. + * @throws ClientException if the path does not exist or something fails during request/response cycle + * @since 1.1.0 + */ + public JsonNode doGetJson(String path, int depth, int... expectedStatus) throws ClientException { + + // check for infinity + if (depth == -1) { + path += ".infinity.json"; + } else { + path += "." + depth + ".json"; + } + + // request the JSON for the node + SlingHttpResponse response = this.doGet(path, HttpUtils.getExpectedStatus(SC_OK, expectedStatus)); + return JsonUtils.getJsonNodeFromString(response.getContent()); + } + + /** * Uploads a file to the repository. It creates a leaf node typed {@code nt:file}. The intermediary nodes are created with * type "sling:OrderedFolder" if parameter {@code createFolders} is true * @@ -413,33 +475,31 @@ public class SlingClient extends Abstrac } /** - * Get uuid from any repository path + * Get the UUID of a repository path * - * @param repPath path in repository - * @return uuid as String + * @param path path in repository + * @return uuid as String or null if path does not exist * @throws ClientException if something fails during request/response cycle - * @throws InterruptedException to mark this operation as "waiting" */ - public String getUUID(String repPath) throws ClientException, InterruptedException { - // TODO review if this check is necessary. Maybe rewrite getJsonNode to wait only if requested - if (!exists(repPath)) { + public String getUUID(String path) throws ClientException { + if (!exists(path)) { return null; } - JsonNode jsonNode = getJsonNode(repPath, -1); + JsonNode jsonNode = doGetJson(path, -1); return getUUId(jsonNode); } /** - * Get uuid from any repository path + * Get the UUID from a node that was already parsed in a {@link JsonNode} * - * @param jsonNode {@link JsonNode} in repository - * @return uuid as String or null if jsonNode is null or if the uuid was not found + * @param jsonNode {@link JsonNode} object of the repository node + * @return UUID as String or null if jsonNode is null or if the UUID was not found * @throws ClientException if something fails during request/response cycle */ + // TODO make this method static public String getUUId(JsonNode jsonNode) throws ClientException { - // TODO review if this check is necessary. Maybe rewrite getJsonNode to wait only if requested if (jsonNode == null) { - return null; // node does not exist + return null; } JsonNode uuidNode = jsonNode.get("jcr:uuid"); Modified: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/html/package-info.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/html/package-info.java?rev=1795303&r1=1795302&r2=1795303&view=diff ============================================================================== --- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/html/package-info.java (original) +++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/html/package-info.java Tue May 16 12:34:17 2017 @@ -17,7 +17,7 @@ * under the License. */ -@Version("2.1.0") +@Version("2.2.0") package org.apache.sling.testing.clients.html; import org.osgi.annotation.versioning.Version; Modified: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/BundlesInstaller.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/BundlesInstaller.java?rev=1795303&r1=1795302&r2=1795303&view=diff ============================================================================== --- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/BundlesInstaller.java (original) +++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/BundlesInstaller.java Tue May 16 12:34:17 2017 @@ -17,13 +17,14 @@ package org.apache.sling.testing.clients.osgi; import org.apache.sling.testing.clients.ClientException; -import org.apache.sling.testing.clients.util.poller.AbstractPoller; +import org.apache.sling.testing.clients.util.poller.Polling; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.util.List; +import java.util.concurrent.TimeoutException; /** @@ -40,31 +41,34 @@ public class BundlesInstaller { /** * Checks if a bundle is installed or not. Does not retry. - * @param bundleFile - * @return - * @throws ClientException - * @throws IOException - * @throws InterruptedException - */ - public boolean isInstalled(File bundleFile) throws InterruptedException, IOException { - final String bundleSymbolicName = OsgiConsoleClient.getBundleSymbolicName(bundleFile); - log.debug("Checking if installed: " + bundleSymbolicName); - boolean installed = osgiConsoleClient.checkBundleInstalled(bundleSymbolicName, 1000, 1); - // if this succeeds, then there's no need to install again - if (installed) { + * @param bundleFile bundle file + * @return true if the bundle is installed + * @throws ClientException if the state of the bundle could not be determined + */ + public boolean isInstalled(File bundleFile) throws ClientException { + String bundleSymbolicName = ""; + try { + bundleSymbolicName = OsgiConsoleClient.getBundleSymbolicName(bundleFile); + log.debug("Checking if installed: " + bundleSymbolicName); + + osgiConsoleClient.getBundleState(bundleSymbolicName); log.debug("Already installed: " + bundleSymbolicName); return true; - } else { + } catch (ClientException e) { log.debug("Not yet installed: " + bundleSymbolicName); return false; + } catch (IOException e) { + log.debug("Failed to retrieve bundle symbolic name from file. ", e); + throw new ClientException("Failed to retrieve bundle symbolic name from file. ", e); } } /** * Check if the installed version matches the one of the bundle (file) - * @param bundleFile - * @return - * @throws Exception + * @param bundleFile bundle file + * @return true if the bundle is installed and has the same version + * @throws ClientException if the installed version cannot be retrieved + * @throws IOException if the file version cannot be read */ public boolean isInstalledWithSameVersion(File bundleFile) throws ClientException, IOException { final String bundleSymbolicName = OsgiConsoleClient.getBundleSymbolicName(bundleFile); @@ -81,11 +85,11 @@ public class BundlesInstaller { /** * Install a list of bundles supplied as Files - * @param toInstall - * @param startBundles - * @throws Exception + * @param toInstall list ob bundles to install + * @param startBundles whether to start the bundles + * @throws ClientException if an error occurs during installation */ - public void installBundles(List<File> toInstall, boolean startBundles) throws ClientException, IOException, InterruptedException { + public void installBundles(List<File> toInstall, boolean startBundles) throws ClientException, IOException { for(File f : toInstall) { final String bundleSymbolicName = OsgiConsoleClient.getBundleSymbolicName(f); if (isInstalled(f)) { @@ -112,12 +116,11 @@ public class BundlesInstaller { /** * Uninstall a list of bundles supplied as Files - * @param toUninstall - * @throws ClientException - * @throws IOException - * @throws InterruptedException + * @param toUninstall bundles to uninstall + * @throws ClientException if one of the requests failed + * @throws IOException if the files cannot be read from disk */ - public void uninstallBundles(List<File> toUninstall) throws ClientException, IOException, InterruptedException { + public void uninstallBundles(List<File> toUninstall) throws ClientException, IOException { for(File f : toUninstall) { final String bundleSymbolicName = OsgiConsoleClient.getBundleSymbolicName(f); if (isInstalled(f)) { @@ -137,11 +140,12 @@ public class BundlesInstaller { /** * Wait for all bundles specified in symbolicNames list to be installed in the OSGi web console. + * @deprecated use {@link #waitBundlesInstalled(List, long)} * @param symbolicNames the list of names for the bundles * @param timeoutSeconds how many seconds to wait - * @return - * @throws Exception + * @return true if all the bundles were installed */ + @Deprecated public boolean waitForBundlesInstalled(List<String> symbolicNames, int timeoutSeconds) throws ClientException, InterruptedException { log.info("Checking that the following bundles are installed (timeout {} seconds): {}", timeoutSeconds, symbolicNames); for (String symbolicName : symbolicNames) { @@ -152,66 +156,50 @@ public class BundlesInstaller { } /** - * Start all the bundles in a {{List}} - * @param symbolicNames the list of bundles to start - * @param timeoutSeconds number of seconds until it times out - * @throws ClientException - * @throws InterruptedException - */ - public void startAllBundles(final List<String> symbolicNames, int timeoutSeconds) throws ClientException, InterruptedException { - log.info("Starting bundles (timeout {} seconds): {}", timeoutSeconds, symbolicNames); - class StartAllBundlesPoller extends AbstractPoller { - private ClientException exception; - public StartAllBundlesPoller(List<String> symbolicNames, long waitInterval, long waitCount) { - super(waitInterval, waitCount); - } + * Wait for multiple bundles to be installed in the OSGi web console. + * @param symbolicNames the list bundles to be checked + * @param timeout max total time to wait for all bundles, in ms, before throwing {@code TimeoutException} + * @throws TimeoutException if the timeout was reached before all the bundles were installed + * @throws InterruptedException to mark this operation as "waiting", callers should rethrow it + */ + public void waitBundlesInstalled(List<String> symbolicNames, long timeout) + throws InterruptedException, TimeoutException { + log.info("Checking that the following bundles are installed (timeout {} ms): {}", timeout, symbolicNames); + long start = System.currentTimeMillis(); + for (String symbolicName : symbolicNames) { + osgiConsoleClient.waitBundleInstalled(symbolicName, timeout, 500); - @Override - public boolean call() { - for (String bundle : symbolicNames) { - final String state; - try { - state = osgiConsoleClient.getBundleState(bundle); - if (!state.equalsIgnoreCase(ACTIVE_STATE)) { - osgiConsoleClient.startBundle(bundle); - } - } catch (ClientException e) { - this.exception = e; - return false; - } - } - return true; + if (System.currentTimeMillis() > start + timeout) { + throw new TimeoutException("Waiting for bundles did not finish in " + timeout + " ms."); } + } + } + /** + * Start all the bundles in a {{List}} + * @param symbolicNames the list of bundles to start + * @param timeout total max time to wait for all the bundles, in ms + * @throws TimeoutException if the timeout is reached before all the bundles are started + * @throws InterruptedException to mark this operation as "waiting", callers should rethrow it + */ + public void startAllBundles(final List<String> symbolicNames, int timeout) throws InterruptedException, TimeoutException { + log.info("Starting bundles (timeout {} seconds): {}", timeout, symbolicNames); + + Polling p = new Polling() { @Override - public boolean condition() { + public Boolean call() throws Exception { + boolean allActive = true; for (String bundle : symbolicNames) { - final String state; - try { - state = osgiConsoleClient.getBundleState(bundle); - if (!state.equalsIgnoreCase(ACTIVE_STATE)) { - return false; - } - } catch (ClientException e) { - this.exception = e; - return false; + String state = osgiConsoleClient.getBundleState(bundle); + if (!state.equalsIgnoreCase(ACTIVE_STATE)) { + osgiConsoleClient.startBundle(bundle); + allActive = false; } } - return true; + return allActive; } + }; - public ClientException getException() { - return exception; - } - } - StartAllBundlesPoller poller = new StartAllBundlesPoller(symbolicNames, 1000, timeoutSeconds); - if (!poller.callUntilCondition()) { - throw new ClientException("Some bundles did not start or timed out", poller.getException()); - } - + p.poll(timeout, 500); } - - - - -} \ No newline at end of file +} Modified: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/OsgiConsoleClient.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/OsgiConsoleClient.java?rev=1795303&r1=1795302&r2=1795303&view=diff ============================================================================== --- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/OsgiConsoleClient.java (original) +++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/OsgiConsoleClient.java Tue May 16 12:34:17 2017 @@ -24,14 +24,14 @@ import org.apache.sling.commons.json.JSO import org.apache.sling.commons.json.JSONException; import org.apache.sling.commons.json.JSONObject; import org.apache.sling.testing.clients.ClientException; -import org.apache.sling.testing.clients.SlingHttpResponse; -import org.apache.sling.testing.clients.util.JsonUtils; -import org.apache.sling.testing.clients.util.poller.AbstractPoller; import org.apache.sling.testing.clients.SlingClient; import org.apache.sling.testing.clients.SlingClientConfig; +import org.apache.sling.testing.clients.SlingHttpResponse; import org.apache.sling.testing.clients.util.FormEntityBuilder; import org.apache.sling.testing.clients.util.HttpUtils; +import org.apache.sling.testing.clients.util.JsonUtils; import org.apache.sling.testing.clients.util.poller.PathPoller; +import org.apache.sling.testing.clients.util.poller.Polling; import org.codehaus.jackson.JsonNode; import org.osgi.framework.Constants; import org.slf4j.Logger; @@ -41,7 +41,12 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URI; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeoutException; import java.util.jar.JarInputStream; import java.util.jar.Manifest; @@ -204,11 +209,12 @@ public class OsgiConsoleClient extends S } return props; } - /** * Returns a map of all properties set for the config referenced by the PID, where the map keys * are the property names. The method waits until the configuration has been set. * + * @deprecated use {@link #waitGetConfiguration(long, String, int...)} + * * @param waitCount The number of maximum wait intervals of 500ms. * Between each wait interval, the method polls the backend to see if the configuration ahs been set. * @param pid pid @@ -217,11 +223,36 @@ public class OsgiConsoleClient extends S * @throws ClientException if the response status does not match any of the expectedStatus * @throws InterruptedException to mark this operation as "waiting" */ + @Deprecated public Map<String, Object> getConfigurationWithWait(long waitCount, String pid, int... expectedStatus) throws ClientException, InterruptedException { - ConfigurationPoller poller = new ConfigurationPoller(500L, waitCount, pid, expectedStatus); - if (!poller.callUntilCondition()) - return getConfiguration(pid, expectedStatus); + ConfigurationPoller poller = new ConfigurationPoller(pid, expectedStatus); + try { + poller.poll(500L * waitCount, 500); + } catch (TimeoutException e) { + throw new ClientException("Cannot retrieve configuration.", e); + } + return poller.getConfig(); + } + + /** + * Returns a map of all properties set for the config referenced by the PID, where the map keys + * are the property names. The method waits until the configuration has been set. + * + * @param timeout Maximum time to wait for the configuration to be available, in ms. + * @param pid service pid + * @param expectedStatus expected response status + * @return the config properties + * @throws ClientException if the response status does not match any of the expectedStatus + * @throws InterruptedException to mark this operation as "waiting" + * @throws TimeoutException if the timeout was reached + */ + public Map<String, Object> waitGetConfiguration(long timeout, String pid, int... expectedStatus) + throws ClientException, InterruptedException, TimeoutException { + + ConfigurationPoller poller = new ConfigurationPoller(pid, expectedStatus); + poller.poll(timeout, 500); + return poller.getConfig(); } @@ -277,6 +308,8 @@ public class OsgiConsoleClient extends S * Sets properties of a config referenced by its PID. the properties to be edited are passed as * a map of property (name,value) pairs. The method waits until the configuration has been set. * + * @deprecated use {@link #waitEditConfiguration(long, String, String, Map, int...)} + * * @param waitCount The number of maximum wait intervals of 500ms. * Between each wait interval, the method polls the backend to see if the configuration ahs been set. * @param PID Persistent identity string @@ -287,6 +320,7 @@ public class OsgiConsoleClient extends S * @throws ClientException if the response status does not match any of the expectedStatus * @throws InterruptedException to mark this operation as "waiting" */ + @Deprecated public String editConfigurationWithWait(int waitCount, String PID, String factoryPID, Map<String, Object> configProperties, int... expectedStatus) throws ClientException, InterruptedException { String pid = editConfiguration(PID, factoryPID, configProperties, expectedStatus); @@ -295,6 +329,28 @@ public class OsgiConsoleClient extends S } /** + * Sets properties of a config referenced by its PID. the properties to be edited are passed as + * a map of property (name,value) pairs. The method waits until the configuration has been set. + * + * @param timeout Max time to wait for the configuration to be set, in ms + * @param PID Persistent identity string + * @param factoryPID Factory persistent identity string or {@code null} + * @param configProperties map of properties + * @param expectedStatus expected response status + * @return the pid + * @throws ClientException if the response status does not match any of the expectedStatus + * @throws InterruptedException to mark this operation as "waiting" + * @throws TimeoutException if the timeout was reached + */ + public String waitEditConfiguration(long timeout, String PID, String factoryPID, Map<String, Object> configProperties, + int... expectedStatus) + throws ClientException, InterruptedException, TimeoutException { + String pid = editConfiguration(PID, factoryPID, configProperties, expectedStatus); + waitGetConfiguration(timeout, pid); + return pid; + } + + /** * Delete the config referenced by the PID * * @param pid pid @@ -319,7 +375,7 @@ public class OsgiConsoleClient extends S /** * Uninstall a bundle - * @param symbolicName + * @param symbolicName bundle symbolic name * @return the sling response * @throws ClientException */ @@ -344,11 +400,11 @@ public class OsgiConsoleClient extends S /** * Install a bundle using the Felix webconsole HTTP interface, with a specific start level - * @param f - * @param startBundle - * @param startLevel + * @param f bundle file + * @param startBundle whether to start or just install the bundle + * @param startLevel start level * @return the sling response - * @throws ClientException + * @throws ClientException if the request failed */ public SlingHttpResponse installBundle(File f, boolean startBundle, int startLevel) throws ClientException { // Setup request for Felix Webconsole bundle install @@ -370,7 +426,24 @@ public class OsgiConsoleClient extends S } /** + * Check that specified bundle is installed and retries every {{waitTime}} milliseconds, until the + * bundle is installed or the number of retries was reached + * @deprecated does not respect polling practices; use {@link #waitBundleInstalled(String, long, long)} instead + * @param symbolicName the name of the bundle + * @param waitTime How many milliseconds to wait between retries + * @param retries the number of retries + * @return true if the bundle was installed until the retries stop, false otherwise + * @throws InterruptedException + */ + @Deprecated + public boolean checkBundleInstalled(String symbolicName, int waitTime, int retries) throws InterruptedException { + final String path = getBundlePath(symbolicName, ".json"); + return new PathPoller(this, path, waitTime, retries).callAndWait(); + } + + /** * Install a bundle using the Felix webconsole HTTP interface and wait for it to be installed + * @deprecated {@link #waitInstallBundle(File, boolean, int, long, long)} * @param f the bundle file * @param startBundle whether to start the bundle or not * @param startLevel the start level of the bundle. negative values mean default start level @@ -379,6 +452,7 @@ public class OsgiConsoleClient extends S * @return true if the bundle was successfully installed, false otherwise * @throws ClientException */ + @Deprecated public boolean installBundleWithRetry(File f, boolean startBundle, int startLevel, int waitTime, int retries) throws ClientException, InterruptedException { installBundle(f, startBundle, startLevel); @@ -390,24 +464,51 @@ public class OsgiConsoleClient extends S } /** - * Check that specified bundle is installed and retries every {{waitTime}} milliseconds, until the - * bundle is installed or the number of retries was reached - * @param symbolicName the name of the bundle - * @param waitTime How many milliseconds to wait between retries - * @param retries the number of retries - * @return true if the bundle was installed until the retries stop, false otherwise + * Install a bundle using the Felix webconsole HTTP interface and wait for it to be installed + * @param f the bundle file + * @param startBundle whether to start the bundle or not + * @param startLevel the start level of the bundle. negative values mean default start level + * @param timeout how much to wait for the bundle to be installed before throwing a {@code TimeoutException} + * @param delay time to wait between checks of the state + * @throws ClientException + * @throws TimeoutException if the bundle did not install before timeout was reached * @throws InterruptedException */ - public boolean checkBundleInstalled(String symbolicName, int waitTime, int retries) throws InterruptedException { - final String path = getBundlePath(symbolicName, ".json"); - return new PathPoller(this, path, waitTime, retries).callAndWait(); + public void waitInstallBundle(File f, boolean startBundle, int startLevel, long timeout, long delay) + throws ClientException, InterruptedException, TimeoutException { + + installBundle(f, startBundle, startLevel); + try { + waitBundleInstalled(getBundleSymbolicName(f), timeout, delay); + } catch (IOException e) { + throw new ClientException("Cannot get bundle symbolic name", e); + } + } + + public void waitBundleInstalled(final String symbolicName, final long timeout, final long delay) + throws TimeoutException, InterruptedException { + + final String path = getBundlePath(symbolicName); + Polling p = new Polling() { + @Override + public Boolean call() throws Exception { + return exists(path); + } + + @Override + protected String message() { + return "Bundle " + symbolicName + " did not install in %1$ ms"; + } + }; + + p.poll(timeout, delay); } /** * Get the id of the bundle - * @param symbolicName - * @return - * @throws Exception + * @param symbolicName bundle symbolic name + * @return the id + * @throws ClientException if the id cannot be retrieved */ public long getBundleId(String symbolicName) throws ClientException { final JSONObject bundle = getBundleData(symbolicName); @@ -420,8 +521,8 @@ public class OsgiConsoleClient extends S /** * Get the version of the bundle - * @param symbolicName - * @return + * @param symbolicName bundle symbolic name + * @return bundle version * @throws ClientException */ public String getBundleVersion(String symbolicName) throws ClientException { @@ -435,9 +536,9 @@ public class OsgiConsoleClient extends S /** * Get the state of the bundle - * @param symbolicName - * @return - * @throws Exception + * @param symbolicName bundle symbolic name + * @return the state of the bundle + * @throws ClientException if the state cannot be retrieved */ public String getBundleState(String symbolicName) throws ClientException { final JSONObject bundle = getBundleData(symbolicName); @@ -460,13 +561,16 @@ public class OsgiConsoleClient extends S this.doPost(path, FormEntityBuilder.create().addParameter("action", "start").build(), SC_OK); } + /** * Starts a bundle and waits for it to be started + * @deprecated use {@link #waitStartBundle(String, long, long)} * @param symbolicName the name of the bundle * @param waitTime How many milliseconds to wait between retries * @param retries the number of retries * @throws ClientException, InterruptedException */ + @Deprecated public void startBundlewithWait(String symbolicName, int waitTime, int retries) throws ClientException, InterruptedException { // start a bundle @@ -476,6 +580,20 @@ public class OsgiConsoleClient extends S } /** + * Starts a bundle and waits for it to be started + * @param symbolicName the name of the bundle + * @param timeout max time to wait for the bundle to start, in ms + * @param delay time to wait between status checks, in ms + * @throws ClientException, InterruptedException, TimeoutException + */ + public void waitStartBundle(String symbolicName, long timeout, long delay) + throws ClientException, InterruptedException, TimeoutException { + startBundle(symbolicName); + // FIXME this should wait for the started state + waitBundleInstalled(symbolicName, timeout, delay); + } + + /** * Calls PackageAdmin.refreshPackages to force re-wiring of all the bundles. * @throws ClientException */ @@ -499,12 +617,25 @@ public class OsgiConsoleClient extends S return URL_BUNDLES + "/" + symbolicName; } + /** + * Returns a data structure like: + * + * { + * "status" : "Bundle information: 173 bundles in total - all 173 bundles active.", + * "s" : [173,171,2,0,0], + * "data": [{ + * "id":0, + * "name":"System Bundle", + * "fragment":false, + * "stateRaw":32, + * "state":"Active", + * "version":"3.0.7", + * "symbolicName":"org.apache.felix.framework", + * "category":"" + * }] + * } + */ private JSONObject getBundleData(String symbolicName) throws ClientException { - // This returns a data structure like - // {"status":"Bundle information: 173 bundles in total - all 173 bundles active.","s":[173,171,2,0,0],"data": - // [ - // {"id":0,"name":"System Bundle","fragment":false,"stateRaw":32,"state":"Active","version":"3.0.7","symbolicName":"org.apache.felix.framework","category":""}, - // ]} final String path = getBundlePath(symbolicName, ".json"); final String content = this.doGet(path, SC_OK).getContent(); @@ -536,9 +667,9 @@ public class OsgiConsoleClient extends S // /** - * Get the symbolic name from a bundle file - * @param bundleFile - * @return + * Get the symbolic name from a bundle file by looking at the manifest + * @param bundleFile bundle file + * @return the name extracted from the manifest * @throws IOException */ public static String getBundleSymbolicName(File bundleFile) throws IOException { @@ -557,9 +688,9 @@ public class OsgiConsoleClient extends S } /** - * Get the version form a bundle file - * @param bundleFile - * @return + * Get the version form a bundle file by looking at the manifest + * @param bundleFile bundle file + * @return the version * @throws IOException */ public static String getBundleVersionFromFile(File bundleFile) throws IOException { @@ -578,32 +709,24 @@ public class OsgiConsoleClient extends S } - class ConfigurationPoller extends AbstractPoller { + class ConfigurationPoller extends Polling { private final String pid; - int[] expectedStatus; - public Map<String, Object> config; + private final int[] expectedStatus; + private Map<String, Object> config; + + public ConfigurationPoller(String pid, int... expectedStatus) { + super(); - public ConfigurationPoller(long waitInterval, long waitCount, String pid, int... expectedStatus) { - super(waitInterval, waitCount); this.pid = pid; - this.config = null; this.expectedStatus = expectedStatus; + this.config = null; } @Override - public boolean call() { - try { - config = getConfiguration(pid, expectedStatus); - } catch (ClientException e) { - LOG.warn("Couldn't get config " + pid, e); - } - return true; - } - - @Override - public boolean condition() { - return null != config; + public Boolean call() throws Exception { + config = getConfiguration(pid, expectedStatus); + return config != null; } public Map<String, Object> getConfig() { Modified: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/OsgiInstanceConfig.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/OsgiInstanceConfig.java?rev=1795303&r1=1795302&r2=1795303&view=diff ============================================================================== --- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/OsgiInstanceConfig.java (original) +++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/OsgiInstanceConfig.java Tue May 16 12:34:17 2017 @@ -24,23 +24,26 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Map; +import java.util.concurrent.TimeoutException; /** * <p>Allows saving and restoring the OSGiConfig to be used before and after altering OSGi configurations for tests</p> * <p>See {@link InstanceConfig}</p> */ public class OsgiInstanceConfig implements InstanceConfig { + private static final Logger LOG = LoggerFactory.getLogger(OsgiInstanceConfig.class); /** - * Number of retries for retrieving the current osgi config for save() and restore() + * Time im ms to wait for retrieving the current osgi config for save() and restore() */ - protected int waitCount = 20; + private static final long WAIT_TIMEOUT = 20000; // in ms - private static final Logger LOG = LoggerFactory.getLogger(OsgiInstanceConfig.class); private final OsgiConsoleClient osgiClient; private final String configPID; private Map<String, Object> config; + @Deprecated + protected int waitCount = 20; /** * @@ -50,7 +53,8 @@ public class OsgiInstanceConfig implemen * @throws ClientException if the client cannot be initialized * @throws InstanceConfigException if the config cannot be saved */ - public <T extends SlingClient> OsgiInstanceConfig(T client, String configPID) throws ClientException, InstanceConfigException { + public <T extends SlingClient> OsgiInstanceConfig(T client, String configPID) + throws ClientException, InstanceConfigException, InterruptedException { this.osgiClient = client.adaptTo(OsgiConsoleClient.class); this.configPID = configPID; @@ -63,14 +67,14 @@ public class OsgiInstanceConfig implemen * * @throws InstanceConfigException if the config cannot be saved */ - public InstanceConfig save() throws InstanceConfigException { + public InstanceConfig save() throws InstanceConfigException, InterruptedException { try { - this.config = osgiClient.getConfigurationWithWait(waitCount, this.configPID); + this.config = osgiClient.waitGetConfiguration(WAIT_TIMEOUT, this.configPID); LOG.info("Saved OSGi config for {}. It is currently this: {}", this.configPID, this.config); } catch (ClientException e) { throw new InstanceConfigException("Error getting config", e); - } catch (InterruptedException e) { - throw new InstanceConfigException("Saving configuration was interrupted ", e); + } catch (TimeoutException e) { + throw new InstanceConfigException("Timeout of " + WAIT_TIMEOUT + " ms was reached while waiting for the configuration", e); } return this; } @@ -80,14 +84,14 @@ public class OsgiInstanceConfig implemen * * @throws InstanceConfigException if the config cannot be restored */ - public InstanceConfig restore() throws InstanceConfigException { + public InstanceConfig restore() throws InstanceConfigException, InterruptedException { try { - osgiClient.editConfigurationWithWait(waitCount, this.configPID, null, config); + osgiClient.waitEditConfiguration(WAIT_TIMEOUT, this.configPID, null, config); LOG.info("restored OSGi config for {}. It is now this: {}", this.configPID, this.config); } catch (ClientException e) { throw new InstanceConfigException("Could not edit OSGi configuration", e); - } catch (InterruptedException e) { - throw new InstanceConfigException("Restoring configuration was interrupted", e); + } catch (TimeoutException e) { + throw new InstanceConfigException("Timeout of " + WAIT_TIMEOUT + " ms was reached while waiting for the configuration", e); } return this; } Modified: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/package-info.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/package-info.java?rev=1795303&r1=1795302&r2=1795303&view=diff ============================================================================== --- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/package-info.java (original) +++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/package-info.java Tue May 16 12:34:17 2017 @@ -19,7 +19,7 @@ /** * OSGI testing tools. */ -@Version("1.1.0") +@Version("1.2.0") package org.apache.sling.testing.clients.osgi; import org.osgi.annotation.versioning.Version; Modified: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/package-info.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/package-info.java?rev=1795303&r1=1795302&r2=1795303&view=diff ============================================================================== --- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/package-info.java (original) +++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/package-info.java Tue May 16 12:34:17 2017 @@ -17,7 +17,7 @@ * under the License. */ -@Version("1.2.0") +@Version("1.3.0") package org.apache.sling.testing.clients; import org.osgi.annotation.versioning.Version; Modified: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/TimeoutsProvider.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/TimeoutsProvider.java?rev=1795303&r1=1795302&r2=1795303&view=diff ============================================================================== --- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/TimeoutsProvider.java (original) +++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/TimeoutsProvider.java Tue May 16 12:34:17 2017 @@ -23,7 +23,11 @@ import org.slf4j.LoggerFactory; * factor. Useful to cope with slower integration testing systems: * use timeout constants in your code that work for usual development * systems, and set a multiplier when running on a slower system. + * + * @deprecated duplicate of {@link org.apache.sling.testing.timeouts.TimeoutsProvider}. This will be removed in the future, so switch to + * the other one instead */ +@Deprecated public class TimeoutsProvider { private static final Logger log = LoggerFactory.getLogger(TimeoutsProvider.class); public static final String PROP_TIMEOUT_MULTIPLIER = "sling.testing.timeout.multiplier"; Modified: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/config/InstanceConfig.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/config/InstanceConfig.java?rev=1795303&r1=1795302&r2=1795303&view=diff ============================================================================== --- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/config/InstanceConfig.java (original) +++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/config/InstanceConfig.java Tue May 16 12:34:17 2017 @@ -28,7 +28,7 @@ public interface InstanceConfig { * @return this * @throws InstanceConfigException if saving the configuration fails */ - public InstanceConfig save() throws InstanceConfigException; + public InstanceConfig save() throws InstanceConfigException, InterruptedException; /** * Restores the saved status of the configuration @@ -36,5 +36,5 @@ public interface InstanceConfig { * @return this * @throws InstanceConfigException if restoring the configuration fails */ - public InstanceConfig restore() throws InstanceConfigException; + public InstanceConfig restore() throws InstanceConfigException, InterruptedException; } Modified: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/config/impl/InstanceConfigCacheImpl.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/config/impl/InstanceConfigCacheImpl.java?rev=1795303&r1=1795302&r2=1795303&view=diff ============================================================================== --- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/config/impl/InstanceConfigCacheImpl.java (original) +++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/config/impl/InstanceConfigCacheImpl.java Tue May 16 12:34:17 2017 @@ -103,7 +103,7 @@ public class InstanceConfigCacheImpl imp @Override - public InstanceConfig save() throws InstanceConfigException { + public InstanceConfig save() throws InstanceConfigException, InterruptedException { for (InstanceConfig ic : configs) { ic.save(); } @@ -111,7 +111,7 @@ public class InstanceConfigCacheImpl imp } @Override - public InstanceConfig restore() throws InstanceConfigException { + public InstanceConfig restore() throws InstanceConfigException, InterruptedException { for (InstanceConfig ic : configs) { ic.restore(); } Modified: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/AbstractPoller.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/AbstractPoller.java?rev=1795303&r1=1795302&r2=1795303&view=diff ============================================================================== --- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/AbstractPoller.java (original) +++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/AbstractPoller.java Tue May 16 12:34:17 2017 @@ -16,6 +16,11 @@ */ package org.apache.sling.testing.clients.util.poller; +/** + * @deprecated use {@link Polling} instead. + * @see Polling for a better way to implement polling + */ +@Deprecated public abstract class AbstractPoller implements Poller { private final long waitInterval; Modified: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/PathPoller.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/PathPoller.java?rev=1795303&r1=1795302&r2=1795303&view=diff ============================================================================== --- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/PathPoller.java (original) +++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/PathPoller.java Tue May 16 12:34:17 2017 @@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory; /** * Allows polling for a resource */ +@Deprecated public class PathPoller extends AbstractPoller { private static final Logger LOG = LoggerFactory.getLogger(PathPoller.class); private final AbstractSlingClient client; Modified: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/Poller.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/Poller.java?rev=1795303&r1=1795302&r2=1795303&view=diff ============================================================================== --- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/Poller.java (original) +++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/Poller.java Tue May 16 12:34:17 2017 @@ -19,7 +19,11 @@ package org.apache.sling.testing.clients /** * Abstract Poller interface. * Provides simple methods to implement custom pollers + * + * @deprecated use {@link Polling} instead. + * @see Polling for a better way to implement polling */ +@Deprecated public interface Poller { boolean call(); boolean condition(); Modified: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/package-info.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/package-info.java?rev=1795303&r1=1795302&r2=1795303&view=diff ============================================================================== --- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/package-info.java (original) +++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/package-info.java Tue May 16 12:34:17 2017 @@ -17,7 +17,7 @@ * under the License. */ -@Version("1.0.0") +@Version("1.1.0") package org.apache.sling.testing.clients.util.poller; import org.osgi.annotation.versioning.Version;