| /* |
| * 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); |
| } |
| |
| } |