This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.testing.clients-1.0.0 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-clients.git
commit 6f3b69ef472e49a831df6a0f0d60504727192a7f Author: Bertrand Delacretaz <[email protected]> AuthorDate: Fri May 13 14:02:03 2016 +0000 SLING-5725 - Remove o.a.s.testing.tools dependency in o.a.s.testing.clients and merge OSGi console clients - contribued by Andrei Dulvac, thanks! git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/testing/http/clients@1743676 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 24 +- .../testing/clients/osgi/BundlesInstaller.java | 243 +++++++++-------- .../testing/clients/osgi/OsgiConsoleClient.java | 293 ++++++++++++++++++++- .../testing/clients/osgi/WebconsoleClient.java | 190 ------------- .../testing/clients/util/poller/PathPoller.java | 64 +++++ 5 files changed, 505 insertions(+), 309 deletions(-) diff --git a/pom.xml b/pom.xml index f0da527..b67851d 100644 --- a/pom.xml +++ b/pom.xml @@ -69,17 +69,6 @@ <dependencies> <dependency> - <groupId>org.apache.sling</groupId> - <artifactId>org.apache.sling.testing.tools</artifactId> - <version>1.0.12</version> - <exclusions> - <exclusion> - <groupId>org.apache.httpcomponents</groupId> - <artifactId>httpcore</artifactId> - </exclusion> - </exclusions> - </dependency> - <dependency> <groupId>org.osgi</groupId> <artifactId>org.osgi.core</artifactId> </dependency> @@ -144,5 +133,18 @@ <artifactId>org.apache.sling.xss</artifactId> <version>1.0.4</version> </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.commons.json</artifactId> + <version>2.0.16</version> + </dependency> + + <!-- For tests --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + <scope>test</scope> + </dependency> </dependencies> </project> diff --git a/src/main/java/org/apache/sling/testing/clients/osgi/BundlesInstaller.java b/src/main/java/org/apache/sling/testing/clients/osgi/BundlesInstaller.java index 1c737a4..e99c454 100644 --- a/src/main/java/org/apache/sling/testing/clients/osgi/BundlesInstaller.java +++ b/src/main/java/org/apache/sling/testing/clients/osgi/BundlesInstaller.java @@ -16,171 +16,202 @@ */ package org.apache.sling.testing.clients.osgi; -import org.osgi.framework.Constants; +import org.apache.sling.testing.clients.ClientException; +import org.apache.sling.testing.clients.util.poller.AbstractPoller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.util.LinkedList; import java.util.List; -import java.util.jar.JarInputStream; -import java.util.jar.Manifest; -/** Utility that installs and starts additional bundles for testing */ +/** + * Utility for installing and starting additional bundles for testing + */ public class BundlesInstaller { private final Logger log = LoggerFactory.getLogger(getClass()); - private final WebconsoleClient webconsoleClient; + private final OsgiConsoleClient osgiConsoleClient; public static final String ACTIVE_STATE = "active"; - - public BundlesInstaller(WebconsoleClient wcc) { - webconsoleClient = wcc; + + public BundlesInstaller(OsgiConsoleClient cc) { + osgiConsoleClient = cc; } - - public boolean isInstalled(File bundleFile) throws Exception { - final String bundleSymbolicName = getBundleSymbolicName(bundleFile); - try{ - log.debug("Checking if installed: "+bundleSymbolicName); - webconsoleClient.checkBundleInstalled(bundleSymbolicName, 1); - // if this succeeds, then there's no need to install again - log.debug("Already installed: "+bundleSymbolicName); + + /** + * 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 ClientException, 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) { + log.debug("Already installed: " + bundleSymbolicName); return true; - } catch(AssertionError e) { - log.debug("Not yet installed: "+bundleSymbolicName); + } else { + log.debug("Not yet installed: " + bundleSymbolicName); return false; } - } - - /** Check if the installed version matches the one of the bundle (file) **/ - public boolean isInstalledWithSameVersion(File bundleFile) throws Exception { - final String bundleSymbolicName = getBundleSymbolicName(bundleFile); - final String versionOnServer = webconsoleClient.getBundleVersion(bundleSymbolicName); - final String versionInBundle = getBundleVersion(bundleFile); + + /** + * Check if the installed version matches the one of the bundle (file) + * @param bundleFile + * @return + * @throws Exception + */ + public boolean isInstalledWithSameVersion(File bundleFile) throws ClientException, IOException { + final String bundleSymbolicName = OsgiConsoleClient.getBundleSymbolicName(bundleFile); + final String versionOnServer = osgiConsoleClient.getBundleVersion(bundleSymbolicName); + final String versionInBundle = OsgiConsoleClient.getBundleVersionFromFile(bundleFile); if (versionOnServer.equals(versionInBundle)) { return true; } else { - log.info("Bundle installed doesn't match: "+bundleSymbolicName+ - ", versionOnServer="+versionOnServer+", versionInBundle="+versionInBundle); + log.warn("Installed bundle doesn't match: {}, versionOnServer={}, versionInBundle={}", + bundleSymbolicName, versionOnServer, versionInBundle); return false; } } - - /** Install a list of bundles supplied as Files */ - public void installBundles(List<File> toInstall, boolean startBundles) throws Exception { + + /** + * Install a list of bundles supplied as Files + * @param toInstall + * @param startBundles + * @throws Exception + */ + public void installBundles(List<File> toInstall, boolean startBundles) throws ClientException, IOException, InterruptedException { for(File f : toInstall) { - final String bundleSymbolicName = getBundleSymbolicName(f); + final String bundleSymbolicName = OsgiConsoleClient.getBundleSymbolicName(f); if (isInstalled(f)) { if (f.getName().contains("SNAPSHOT")) { log.info("Reinstalling (due to SNAPSHOT version): {}", bundleSymbolicName); - webconsoleClient.uninstallBundle(bundleSymbolicName, f); + osgiConsoleClient.uninstallBundle(bundleSymbolicName); } else if (!isInstalledWithSameVersion(f)) { log.info("Reinstalling (due to version mismatch): {}", bundleSymbolicName); - webconsoleClient.uninstallBundle(bundleSymbolicName, f); + osgiConsoleClient.uninstallBundle(bundleSymbolicName); } else { log.info("Not reinstalling: {}", bundleSymbolicName); continue; } } - webconsoleClient.installBundle(f, startBundles); + osgiConsoleClient.installBundle(f, startBundles); log.info("Installed: {}", bundleSymbolicName); } - + // ensure that bundles are re-wired esp. if an existing bundle was updated - webconsoleClient.refreshPackages(); + osgiConsoleClient.refreshPackages(); log.info("{} additional bundles installed", toInstall.size()); } - - /** Uninstall a list of bundles supplied as Files */ - public void uninstallBundles(List<File> toUninstall) throws Exception { + + /** + * Uninstall a list of bundles supplied as Files + * @param toUninstall + * @throws ClientException + * @throws IOException + * @throws InterruptedException + */ + public void uninstallBundles(List<File> toUninstall) throws ClientException, IOException, InterruptedException { for(File f : toUninstall) { - final String bundleSymbolicName = getBundleSymbolicName(f); + final String bundleSymbolicName = OsgiConsoleClient.getBundleSymbolicName(f); if (isInstalled(f)) { log.info("Uninstalling bundle: {}", bundleSymbolicName); - webconsoleClient.uninstallBundle(bundleSymbolicName, f); + osgiConsoleClient.uninstallBundle(bundleSymbolicName); } else { log.info("Could not uninstall: {} as it never was installed", bundleSymbolicName); } } - + // ensure that bundles are re-wired esp. if an existing bundle was updated - webconsoleClient.refreshPackages(); + osgiConsoleClient.refreshPackages(); log.info("{} additional bundles uninstalled", toUninstall.size()); } - - /** Wait for all bundles specified in symbolicNames list to be installed in the - * remote web console. + + + /** + * Wait for all bundles specified in symbolicNames list to be installed in the OSGi web console. + * @param symbolicNames the list of names for the bundles + * @param timeoutSeconds how many seconds to wait + * @return + * @throws Exception */ - public void waitForBundlesInstalled(List<String> symbolicNames, int timeoutSeconds) throws Exception { - log.info("Checking that bundles are installed (timeout {} seconds): {}", timeoutSeconds, symbolicNames); - for(String symbolicName : symbolicNames) { - webconsoleClient.checkBundleInstalled(symbolicName, timeoutSeconds); + 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) { + boolean started = osgiConsoleClient.checkBundleInstalled(symbolicName, 500, 2 * timeoutSeconds); + if (!started) return false; } + return true; } - - public void startAllBundles(List<String> symbolicNames, int timeoutSeconds) throws Exception { + + /** + * 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); - - final long timeout = System.currentTimeMillis() + timeoutSeconds * 1000L; - final List<String> toStart = new LinkedList<String>(); - while(System.currentTimeMillis() < timeout) { - toStart.clear(); - for(String name : symbolicNames) { - final String state = webconsoleClient.getBundleState(name); - if(!state.equalsIgnoreCase(ACTIVE_STATE)) { - toStart.add(name); - break; - } + class StartAllBundlesPoller extends AbstractPoller { + private ClientException exception; + public StartAllBundlesPoller(List<String> symbolicNames, long waitInterval, long waitCount) { + super(waitInterval, waitCount); } - - if(toStart.isEmpty()) { - log.info("Ok - all bundles are in the {} state", ACTIVE_STATE); - break; + + @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; } - - for(String name : toStart) { - webconsoleClient.startBundle(name); + + @Override + public boolean condition() { + 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; + } + } + return true; } - - Thread.sleep(500L); - } - - if(!toStart.isEmpty()) { - throw new Exception("Some bundles did not start: " + toStart); - } - } - - public String getBundleSymbolicName(File bundleFile) throws IOException { - String name = null; - final JarInputStream jis = new JarInputStream(new FileInputStream(bundleFile)); - try { - final Manifest m = jis.getManifest(); - if (m == null) { - throw new IOException("Manifest is null in " + bundleFile.getAbsolutePath()); + + public ClientException getException() { + return exception; } - name = m.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME); - } finally { - jis.close(); } - return name; - } - - public String getBundleVersion(File bundleFile) throws IOException { - String version = null; - final JarInputStream jis = new JarInputStream(new FileInputStream(bundleFile)); - try { - final Manifest m = jis.getManifest(); - if(m == null) { - throw new IOException("Manifest is null in " + bundleFile.getAbsolutePath()); - } - version = m.getMainAttributes().getValue(Constants.BUNDLE_VERSION); - } finally { - jis.close(); + StartAllBundlesPoller poller = new StartAllBundlesPoller(symbolicNames, 1000, timeoutSeconds); + if (!poller.callUntilCondition()) { + throw new ClientException("Some bundles did not start or timed out", poller.getException()); } - return version; + } + + + + } \ No newline at end of file diff --git a/src/main/java/org/apache/sling/testing/clients/osgi/OsgiConsoleClient.java b/src/main/java/org/apache/sling/testing/clients/osgi/OsgiConsoleClient.java index 9c5a928..08cd742 100644 --- a/src/main/java/org/apache/sling/testing/clients/osgi/OsgiConsoleClient.java +++ b/src/main/java/org/apache/sling/testing/clients/osgi/OsgiConsoleClient.java @@ -18,7 +18,11 @@ package org.apache.sling.testing.clients.osgi; import org.apache.http.Header; +import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.sling.commons.json.JSONArray; +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; @@ -27,18 +31,27 @@ import org.apache.sling.testing.clients.SlingClient; import org.apache.sling.testing.clients.SlingClientConfig; import org.apache.sling.testing.clients.util.FormEntityBuilder; import org.apache.sling.testing.clients.util.HttpUtils; +import org.apache.sling.testing.clients.util.poller.PathPoller; import org.codehaus.jackson.JsonNode; +import org.osgi.framework.Constants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; import java.net.URI; import java.util.*; +import java.util.jar.JarInputStream; +import java.util.jar.Manifest; import static org.apache.http.HttpStatus.SC_MOVED_TEMPORARILY; import static org.apache.http.HttpStatus.SC_OK; /** * A client that wraps the Felix OSGi Web Console REST API calls. + * @see <a href=http://felix.apache.org/documentation/subprojects/apache-felix-web-console/web-console-restful-api.html> + * Web Console RESTful API</a> */ public class OsgiConsoleClient extends SlingClient { @@ -63,6 +76,12 @@ public class OsgiConsoleClient extends SlingClient { */ private final String URL_COMPONENTS = CONSOLE_ROOT_URL + "/components"; + + public static final String JSON_KEY_ID = "id"; + public static final String JSON_KEY_VERSION = "version"; + public static final String JSON_KEY_DATA = "data"; + public static final String JSON_KEY_STATE = "state"; + /** * Default constructor. Simply calls {@link SlingClient#SlingClient(URI, String, String)} * @@ -141,6 +160,10 @@ public class OsgiConsoleClient extends SlingClient { return new ComponentInfo(JsonUtils.getJsonNodeFromString(resp.getContent())); } + // + // OSGi configurations + // + /** * Returns a map of all properties set for the config referenced by the PID, where the map keys * are the property names. @@ -276,16 +299,282 @@ public class OsgiConsoleClient extends SlingClient { * * @param pid pid * @param expectedStatus expected response status + * @return the sling response * @throws ClientException if the response status does not match any of the expectedStatus */ - public void deleteConfiguration(String pid, int... expectedStatus) throws ClientException { + public SlingHttpResponse deleteConfiguration(String pid, int... expectedStatus) throws ClientException { FormEntityBuilder builder = FormEntityBuilder.create(); builder.addParameter("apply", "1"); builder.addParameter("delete", "1"); // make the request SlingHttpResponse resp = this.doPost(URL_CONFIGURATION + "/" + pid, builder.build()); - // check the returned status + // check the returned status HttpUtils.verifyHttpStatus(resp, HttpUtils.getExpectedStatus(200, expectedStatus)); + return resp; + } + + // + // Bundles + // + + /** + * Uninstall a bundle + * @param symbolicName + * @return the sling response + * @throws ClientException + */ + public SlingHttpResponse uninstallBundle(String symbolicName) throws ClientException { + final long bundleId = getBundleId(symbolicName); + LOG.info("Uninstalling bundle {} with bundleId {}", symbolicName, bundleId); + FormEntityBuilder builder = FormEntityBuilder.create(); + builder.addParameter("action", "uninstall"); + return this.doPost(getBundlePath(symbolicName), builder.build(), 200); + } + + /** + * Install a bundle using the Felix webconsole HTTP interface + * @param f the bundle file + * @param startBundle whether to start the bundle or not + * @return the sling response + * @throws ClientException + */ + public SlingHttpResponse installBundle(File f, boolean startBundle) throws ClientException { + return installBundle(f, startBundle, 0); + } + + /** + * Install a bundle using the Felix webconsole HTTP interface, with a specific start level + * @param f + * @param startBundle + * @param startLevel + * @return the sling response + * @throws ClientException + */ + public SlingHttpResponse installBundle(File f, boolean startBundle, int startLevel) throws ClientException { + // Setup request for Felix Webconsole bundle install + MultipartEntityBuilder builder = MultipartEntityBuilder.create() + .addTextBody("action", "install") + .addBinaryBody("bundlefile", f); + if (startBundle) { + builder.addTextBody("bundlestart", "true"); + } + if (startLevel > 0) { + builder.addTextBody("bundlestartlevel", String.valueOf(startLevel)); + LOG.info("Installing bundle {} at start level {}", f.getName(), startLevel); + } else { + LOG.info("Installing bundle {} at default start level", f.getName()); + } + + return this.doPost(URL_BUNDLES, builder.build(), 302); + + } + + /** + * 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 waitTime how long to wait between retries of checking the bundle + * @param retries how many times to check for the bundle to be installed, until giving up + * @return true if the bundle was successfully installed, false otherwise + * @throws ClientException + */ + public boolean installBundleWithRetry(File f, boolean startBundle, int startLevel, int waitTime, int retries) + throws ClientException, InterruptedException { + installBundle(f, startBundle, startLevel); + try { + return this.checkBundleInstalled(OsgiConsoleClient.getBundleSymbolicName(f), waitTime, retries); + } catch (IOException e) { + throw new ClientException("Cannot get bundle symbolic name", e); + } + } + + /** + * 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 + * @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(); + } + + /** + * Get the id of the bundle + * @param symbolicName + * @return + * @throws Exception + */ + public long getBundleId(String symbolicName) throws ClientException { + final JSONObject bundle = getBundleData(symbolicName); + try { + return bundle.getLong(JSON_KEY_ID); + } catch (JSONException e) { + throw new ClientException("Cannot get id from json", e); + } + } + + /** + * Get the version of the bundle + * @param symbolicName + * @return + * @throws ClientException + */ + public String getBundleVersion(String symbolicName) throws ClientException { + final JSONObject bundle = getBundleData(symbolicName); + try { + return bundle.getString(JSON_KEY_VERSION); + } catch (JSONException e) { + throw new ClientException("Cannot get version from json", e); + } + } + + /** + * Get the state of the bundle + * @param symbolicName + * @return + * @throws Exception + */ + public String getBundleState(String symbolicName) throws ClientException { + final JSONObject bundle = getBundleData(symbolicName); + try { + return bundle.getString(JSON_KEY_STATE); + } catch (JSONException e) { + throw new ClientException("Cannot get state from json", e); + } + } + + /** + * Starts a bundle + * @param symbolicName the name of the bundle + * @throws ClientException + */ + public void startBundle(String symbolicName) throws ClientException { + // To start the bundle we POST action=start to its URL + final String path = getBundlePath(symbolicName); + LOG.info("Starting bundle {} via {}", symbolicName, path); + this.doPost(path, FormEntityBuilder.create().addParameter("action", "start").build(), SC_OK); + } + + /** + * Starts a bundle and waits for it to be started + * @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 + */ + public void startBundlewithWait(String symbolicName, int waitTime, int retries) + throws ClientException, InterruptedException { + // start a bundle + startBundle(symbolicName); + // wait for it to be in the started state + checkBundleInstalled(symbolicName, waitTime, retries); + } + + /** + * Calls PackageAdmin.refreshPackages to force re-wiring of all the bundles. + * @throws ClientException + */ + public void refreshPackages() throws ClientException { + LOG.info("Refreshing packages."); + FormEntityBuilder builder = FormEntityBuilder.create(); + builder.addParameter("action", "refreshPackages"); + this.doPost(URL_BUNDLES, builder.build(), 200); + } + + + // + // private methods + // + + private String getBundlePath(String symbolicName, String extension) { + return getBundlePath(symbolicName) + extension; + } + + private String getBundlePath(String symbolicName) { + return URL_BUNDLES + "/" + symbolicName; + } + + 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(); + + try { + final JSONObject root = new JSONObject(content); + + if (!root.has(JSON_KEY_DATA)) { + throw new ClientException(path + " does not provide '" + JSON_KEY_DATA + "' element, JSON content=" + content); + } + + final JSONArray data = root.getJSONArray(JSON_KEY_DATA); + if (data.length() < 1) { + throw new ClientException(path + "." + JSON_KEY_DATA + " is empty, JSON content=" + content); + } + + final JSONObject bundle = data.getJSONObject(0); + if (!bundle.has(JSON_KEY_STATE)) { + throw new ClientException(path + ".data[0].state missing, JSON content=" + content); + } + + return bundle; + } catch (JSONException e) { + throw new ClientException("Cannot get json", e); + } + } + + // + // static methods + // + + /** + * Get the symbolic name from a bundle file + * @param bundleFile + * @return + * @throws IOException + */ + public static String getBundleSymbolicName(File bundleFile) throws IOException { + String name = null; + final JarInputStream jis = new JarInputStream(new FileInputStream(bundleFile)); + try { + final Manifest m = jis.getManifest(); + if (m == null) { + throw new IOException("Manifest is null in " + bundleFile.getAbsolutePath()); + } + name = m.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME); + } finally { + jis.close(); + } + return name; + } + + /** + * Get the version form a bundle file + * @param bundleFile + * @return + * @throws IOException + */ + public static String getBundleVersionFromFile(File bundleFile) throws IOException { + String version = null; + final JarInputStream jis = new JarInputStream(new FileInputStream(bundleFile)); + try { + final Manifest m = jis.getManifest(); + if(m == null) { + throw new IOException("Manifest is null in " + bundleFile.getAbsolutePath()); + } + version = m.getMainAttributes().getValue(Constants.BUNDLE_VERSION); + } finally { + jis.close(); + } + return version; } diff --git a/src/main/java/org/apache/sling/testing/clients/osgi/WebconsoleClient.java b/src/main/java/org/apache/sling/testing/clients/osgi/WebconsoleClient.java deleted file mode 100644 index bbda9bc..0000000 --- a/src/main/java/org/apache/sling/testing/clients/osgi/WebconsoleClient.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.sling.testing.clients.osgi; - - -import java.io.File; - -import org.apache.http.entity.mime.MultipartEntity; -import org.apache.http.entity.mime.content.FileBody; -import org.apache.http.entity.mime.content.StringBody; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.sling.commons.json.JSONArray; -import org.apache.sling.commons.json.JSONObject; -import org.apache.sling.testing.tools.http.RequestBuilder; -import org.apache.sling.testing.tools.http.RequestExecutor; -import org.apache.sling.testing.tools.http.RetryingContentChecker; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** HTTP Client for the Felix webconsole - simplistic for now */ -public class WebconsoleClient { - private final Logger log = LoggerFactory.getLogger(getClass()); - private final RequestExecutor executor; - private final RequestBuilder builder; - private final String username; - private final String password; - - public static final String JSON_KEY_ID = "id"; - public static final String JSON_KEY_VERSION = "version"; - public static final String JSON_KEY_DATA = "data"; - public static final String JSON_KEY_STATE = "state"; - public static final String CONSOLE_BUNDLES_PATH = "/system/console/bundles"; - - public WebconsoleClient(String slingServerUrl, String username, String password) { - this.builder = new RequestBuilder(slingServerUrl); - this.executor = new RequestExecutor(new DefaultHttpClient()); - this.username = username; - this.password = password; - } - - public void uninstallBundle(String symbolicName, File f) throws Exception { - final long bundleId = getBundleId(symbolicName); - - log.info("Uninstalling bundle {} with bundleId {}", symbolicName, bundleId); - - final MultipartEntity entity = new MultipartEntity(); - entity.addPart("action",new StringBody("uninstall")); - executor.execute( - builder.buildPostRequest(CONSOLE_BUNDLES_PATH+"/"+bundleId) - .withCredentials(username, password) - .withEntity(entity) - ).assertStatus(200); - } - - /** Install a bundle using the Felix webconsole HTTP interface, with a specific start level */ - public void installBundle(File f, boolean startBundle) throws Exception { - installBundle(f, startBundle, 0); - } - - /** Install a bundle using the Felix webconsole HTTP interface, with a specific start level */ - public void installBundle(File f, boolean startBundle, int startLevel) throws Exception { - - // Setup request for Felix Webconsole bundle install - final MultipartEntity entity = new MultipartEntity(); - entity.addPart("action",new StringBody("install")); - if(startBundle) { - entity.addPart("bundlestart", new StringBody("true")); - } - entity.addPart("bundlefile", new FileBody(f)); - - if(startLevel > 0) { - entity.addPart("bundlestartlevel", new StringBody(String.valueOf(startLevel))); - log.info("Installing bundle {} at start level {}", f.getName(), startLevel); - } else { - log.info("Installing bundle {} at default start level", f.getName()); - } - - // Console returns a 302 on success (and in a POST this - // is not handled automatically as per HTTP spec) - executor.execute( - builder.buildPostRequest(CONSOLE_BUNDLES_PATH) - .withCredentials(username, password) - .withEntity(entity) - ).assertStatus(302); - } - - /** Check that specified bundle is installed - must be called - * before other methods that take a symbolicName parameter, - * in case installBundle was just called and the actual - * installation hasn't happened yet. */ - public void checkBundleInstalled(String symbolicName, int timeoutSeconds) { - final String path = getBundlePath(symbolicName, ".json"); - new RetryingContentChecker(executor, builder, username, password).check(path, 200, timeoutSeconds, 500); - } - - private JSONObject getBundleData(String symbolicName) throws Exception { - // 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 = executor.execute( - builder.buildGetRequest(path) - .withCredentials(username, password) - ).assertStatus(200) - .getContent(); - - final JSONObject root = new JSONObject(content); - if(!root.has(JSON_KEY_DATA)) { - throw new Exception(path + " does not provide '" + JSON_KEY_DATA + "' element, JSON content=" + content); - } - final JSONArray data = root.getJSONArray(JSON_KEY_DATA); - if(data.length() < 1) { - throw new Exception(path + "." + JSON_KEY_DATA + " is empty, JSON content=" + content); - } - final JSONObject bundle = data.getJSONObject(0); - if(!bundle.has(JSON_KEY_STATE)) { - throw new Exception(path + ".data[0].state missing, JSON content=" + content); - } - return bundle; - } - - /** Get bundle id */ - public long getBundleId(String symbolicName) throws Exception { - final JSONObject bundle = getBundleData(symbolicName); - return bundle.getLong(JSON_KEY_ID); - } - - /** Get bundle version **/ - public String getBundleVersion(String symbolicName) throws Exception { - final JSONObject bundle = getBundleData(symbolicName); - return bundle.getString(JSON_KEY_VERSION); - } - - /** Get specified bundle state */ - public String getBundleState(String symbolicName) throws Exception { - final JSONObject bundle = getBundleData(symbolicName); - return bundle.getString(JSON_KEY_STATE); - } - - /** Start specified bundle */ - public void startBundle(String symbolicName) throws Exception { - // To start the bundle we POST action=start to its URL - final String path = getBundlePath(symbolicName, null); - log.info("Starting bundle {} via {}", symbolicName, path); - - final MultipartEntity entity = new MultipartEntity(); - entity.addPart("action",new StringBody("start")); - executor.execute( - builder.buildPostRequest(path) - .withCredentials(username, password) - .withEntity(entity) - ).assertStatus(200); - } - - private String getBundlePath(String symbolicName, String extension) { - return CONSOLE_BUNDLES_PATH + "/" + symbolicName - + (extension == null ? "" : extension); - } - - /** Calls PackageAdmin.refreshPackages to enforce re-wiring of all bundles. */ - public void refreshPackages() throws Exception { - log.info("Refresh packages."); - - final MultipartEntity entity = new MultipartEntity(); - entity.addPart("action", new StringBody("refreshPackages")); - - executor.execute( - builder.buildPostRequest(CONSOLE_BUNDLES_PATH) - .withCredentials(username, password) - .withEntity(entity) - ).assertStatus(200); - } - -} diff --git a/src/main/java/org/apache/sling/testing/clients/util/poller/PathPoller.java b/src/main/java/org/apache/sling/testing/clients/util/poller/PathPoller.java new file mode 100644 index 0000000..c7b50cb --- /dev/null +++ b/src/main/java/org/apache/sling/testing/clients/util/poller/PathPoller.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p/> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p/> + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ******************************************************************************/ +package org.apache.sling.testing.clients.util.poller; + +import org.apache.sling.testing.clients.AbstractSlingClient; +import org.apache.sling.testing.clients.ClientException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Allows polling for a resource + */ +public class PathPoller extends AbstractPoller { + private static final Logger LOG = LoggerFactory.getLogger(PathPoller.class); + private final AbstractSlingClient client; + private final String path; + private final int[] expectedStatus; + private Exception exception; + + public PathPoller(AbstractSlingClient client, String path, long waitInterval, long waitCount, int... expectedStatus) { + super(waitInterval, waitCount); + this.client = client; + this.path = path; + this.expectedStatus = expectedStatus; + } + + + @Override + public boolean call() { + return true; + } + + @Override + public boolean condition() { + try { + client.doGet(path, expectedStatus); + } catch (ClientException e) { + LOG.warn("Get on {} failed: {}", path, e); + this.exception = e; + return false; + } + return true; + } + + public Exception getException() { + return exception; + } +} -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
