You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2016/04/29 16:03:33 UTC
svn commit: r1741632 [3/4] - in /sling/trunk/testing/http: ./ clients/
clients/src/ clients/src/main/ clients/src/main/java/
clients/src/main/java/org/ clients/src/main/java/org/apache/
clients/src/main/java/org/apache/sling/ clients/src/main/java/org/...
Added: 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=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/BundlesInstaller.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/BundlesInstaller.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,186 @@
+/*
+ * 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 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.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 */
+public class BundlesInstaller {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ private final WebconsoleClient webconsoleClient;
+ public static final String ACTIVE_STATE = "active";
+
+ public BundlesInstaller(WebconsoleClient wcc) {
+ webconsoleClient = wcc;
+ }
+
+ 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);
+ return true;
+ } catch(AssertionError e) {
+ 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);
+ if (versionOnServer.equals(versionInBundle)) {
+ return true;
+ } else {
+ log.info("Bundle installed doesn't match: "+bundleSymbolicName+
+ ", versionOnServer="+versionOnServer+", versionInBundle="+versionInBundle);
+ return false;
+ }
+ }
+
+ /** Install a list of bundles supplied as Files */
+ public void installBundles(List<File> toInstall, boolean startBundles) throws Exception {
+ for(File f : toInstall) {
+ final String bundleSymbolicName = getBundleSymbolicName(f);
+ if (isInstalled(f)) {
+ if (f.getName().contains("SNAPSHOT")) {
+ log.info("Reinstalling (due to SNAPSHOT version): {}", bundleSymbolicName);
+ webconsoleClient.uninstallBundle(bundleSymbolicName, f);
+ } else if (!isInstalledWithSameVersion(f)) {
+ log.info("Reinstalling (due to version mismatch): {}", bundleSymbolicName);
+ webconsoleClient.uninstallBundle(bundleSymbolicName, f);
+ } else {
+ log.info("Not reinstalling: {}", bundleSymbolicName);
+ continue;
+ }
+ }
+ webconsoleClient.installBundle(f, startBundles);
+ log.info("Installed: {}", bundleSymbolicName);
+ }
+
+ // ensure that bundles are re-wired esp. if an existing bundle was updated
+ webconsoleClient.refreshPackages();
+
+ log.info("{} additional bundles installed", toInstall.size());
+ }
+
+ /** Uninstall a list of bundles supplied as Files */
+ public void uninstallBundles(List<File> toUninstall) throws Exception {
+ for(File f : toUninstall) {
+ final String bundleSymbolicName = getBundleSymbolicName(f);
+ if (isInstalled(f)) {
+ log.info("Uninstalling bundle: {}", bundleSymbolicName);
+ webconsoleClient.uninstallBundle(bundleSymbolicName, f);
+ } 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();
+
+ log.info("{} additional bundles uninstalled", toUninstall.size());
+ }
+
+ /** Wait for all bundles specified in symbolicNames list to be installed in the
+ * remote web console.
+ */
+ 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 void startAllBundles(List<String> symbolicNames, int timeoutSeconds) throws Exception {
+ 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;
+ }
+ }
+
+ if(toStart.isEmpty()) {
+ log.info("Ok - all bundles are in the {} state", ACTIVE_STATE);
+ break;
+ }
+
+ for(String name : toStart) {
+ webconsoleClient.startBundle(name);
+ }
+
+ 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());
+ }
+ 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();
+ }
+ return version;
+ }
+}
\ No newline at end of file
Added: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/Component.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/Component.java?rev=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/Component.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/Component.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+public class Component {
+
+ public enum Status {
+
+ ACTIVE("active"),
+
+ REGISTERED("registered"),
+
+ UNSATISFIED("unsatisfied");
+
+ String value;
+
+ Status(String value) {
+ this.value = value;
+ }
+
+ public static Status value(String o) {
+ for(Status s : values()) {
+ if(s.value.equalsIgnoreCase(o)) {
+ return s;
+ }
+ }
+ return null;
+ }
+
+ public String toString() {
+ return value;
+ }
+
+ }
+
+}
Added: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/ComponentInfo.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/ComponentInfo.java?rev=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/ComponentInfo.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/ComponentInfo.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,69 @@
+/*
+ * 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 org.apache.sling.testing.clients.ClientException;
+import org.codehaus.jackson.JsonNode;
+
+public class ComponentInfo {
+
+ private JsonNode component;
+
+ public ComponentInfo(JsonNode root) throws ClientException {
+ if(root.get("id") != null) {
+ if(root.get("id") == null) {
+ throw new ClientException("No Component Info returned");
+ }
+ component = root;
+ } else {
+ if(root.get("data") == null && root.get("data").size() < 1) {
+ throw new ClientException("No Component Info returned");
+ }
+ component = root.get("data").get(0);
+ }
+ }
+
+ /**
+ * @return the component identifier
+ */
+ public int getId() {
+ return component.get("id").getIntValue();
+ }
+
+ /**
+ * @return the component name
+ */
+ public String getName() {
+ return component.get("name").getTextValue();
+ }
+
+ /**
+ * @return the component status
+ */
+ public Component.Status getStatus() {
+ return Component.Status.value(component.get("state").getTextValue());
+ }
+
+ /**
+ * @return the component persistent identifier
+ */
+ public String getPid() {
+ return component.get("pid").getTextValue();
+ }
+
+}
Added: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/ComponentsInfo.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/ComponentsInfo.java?rev=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/ComponentsInfo.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/ComponentsInfo.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,95 @@
+/*
+ * 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 org.apache.sling.testing.clients.ClientException;
+import org.codehaus.jackson.JsonNode;
+
+import java.util.Iterator;
+
+/**
+ * Thin wrapper around the list of components
+ */
+public class ComponentsInfo {
+
+ private JsonNode root = null;
+
+ /**
+ * The only constructor.
+ *
+ * @param rootNode the root JSON node of the components info.
+ * @throws ClientException if the info cannot be retrieved
+ */
+ public ComponentsInfo(JsonNode rootNode) throws ClientException {
+ this.root = rootNode;
+ }
+
+ /**
+ * @return the number of installed components
+ * @throws ClientException if the info cannot be retrieved
+ */
+ public int getNumberOfInstalledComponents() throws ClientException {
+ if(root.get("status") == null)
+ throw new ClientException("Number of installed Components not defined!");
+ return Integer.parseInt(root.get("status").getValueAsText());
+ }
+
+ /**
+ * @param id the id of the component
+ * @return the ComponentInfo for a component with the identifier {@code id}
+ * @throws ClientException if the info cannot be retrieved
+ */
+ public ComponentInfo forId(String id) throws ClientException {
+ JsonNode component = findBy("id", id);
+ return (component != null) ? new ComponentInfo(component) : null;
+ }
+
+ /**
+ * @param name the name of the component
+ * @return the ComponentInfo for a component with the name {@code name}
+ * @throws ClientException if the info cannot be retrieved
+ */
+ public ComponentInfo forName(String name) throws ClientException {
+ JsonNode component = findBy("name", name);
+ return (component != null) ? new ComponentInfo(component) : null;
+ }
+
+ /**
+ * @param pid the pid of the component
+ * @return the ComponentInfo for a component with the pid {@code pid}
+ * @throws ClientException if the info cannot be retrieved
+ */
+ public ComponentInfo forPid(String pid) throws ClientException {
+ JsonNode component = findBy("pid", pid);
+ return (component != null) ? new ComponentInfo(component) : null;
+ }
+
+ private JsonNode findBy(String key, String value) {
+ Iterator<JsonNode> nodes = root.get("data").getElements();
+ while(nodes.hasNext()) {
+ JsonNode node = nodes.next();
+ if(node.get(key) != null) {
+ if(node.get(key).isValueNode()) {
+ return node;
+ }
+ }
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
Added: 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=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/OsgiConsoleClient.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/OsgiConsoleClient.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,324 @@
+/*
+ * 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 org.apache.http.Header;
+import org.apache.http.impl.client.CloseableHttpClient;
+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.util.FormEntityBuilder;
+import org.apache.sling.testing.clients.util.HttpUtils;
+import org.codehaus.jackson.JsonNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.URI;
+import java.util.*;
+
+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.
+ */
+public class OsgiConsoleClient extends SlingClient {
+
+ private static final Logger LOG = LoggerFactory.getLogger(OsgiConsoleClient.class);
+ /**
+ * All System Console REST API calls go to /system/console and below
+ */
+ private final String CONSOLE_ROOT_URL = "/system/console";
+
+ /**
+ * The URL for configuration requests
+ */
+ private final String URL_CONFIGURATION = CONSOLE_ROOT_URL + "/configMgr";
+
+ /**
+ * The URL for bundle requests
+ */
+ private final String URL_BUNDLES = CONSOLE_ROOT_URL + "/bundles";
+
+ /**
+ * The URL for components requests
+ */
+ private final String URL_COMPONENTS = CONSOLE_ROOT_URL + "/components";
+
+ /**
+ * Default constructor. Simply calls {@link SlingClient#SlingClient(URI, String, String)}
+ *
+ * @param serverUrl the URL to the server under test
+ * @param userName the user name used for authentication
+ * @param password the password for this user
+ * @throws ClientException if the client cannot be instantiated
+ */
+ public OsgiConsoleClient(URI serverUrl, String userName, String password) throws ClientException {
+ super(serverUrl, userName, password);
+ }
+
+ /**
+ * Constructor used by adaptTo() and InternalBuilder classes. Should not be called directly in the code
+ *
+ * @param http http client to be used for requests
+ * @param config sling specific configs
+ * @throws ClientException if the client cannot be instantiated
+ */
+ public OsgiConsoleClient(CloseableHttpClient http, SlingClientConfig config) throws ClientException {
+ super(http, config);
+ }
+
+ /**
+ * Returns the wrapper for the bundles info json
+ *
+ * @param expectedStatus list of accepted statuses of the response
+ * @return all the bundles info
+ * @throws ClientException if the response status does not match any of the expectedStatus
+ */
+ public BundlesInfo getBundlesInfo(int... expectedStatus) throws ClientException {
+ // request the bundles information
+ SlingHttpResponse resp = this.doGet(URL_BUNDLES + ".json", HttpUtils.getExpectedStatus(SC_OK, expectedStatus));
+ // return the wrapper
+ return new BundlesInfo(JsonUtils.getJsonNodeFromString(resp.getContent()));
+ }
+
+ /**
+ * Returns the wrapper for the bundle info json
+ *
+ * @param id the id of the bundle
+ * @param expectedStatus list of accepted statuses of the response
+ * @return the bundle info
+ * @throws ClientException if the response status does not match any of the expectedStatus
+ */
+ public BundleInfo getBundleInfo(String id, int... expectedStatus) throws ClientException {
+ SlingHttpResponse resp = this.doGet(URL_BUNDLES + "/" + id + ".json");
+ HttpUtils.verifyHttpStatus(resp, HttpUtils.getExpectedStatus(SC_OK, expectedStatus));
+ return new BundleInfo(JsonUtils.getJsonNodeFromString(resp.getContent()));
+ }
+
+ /**
+ * Returns the wrapper for the components info json
+ *
+ * @param expectedStatus list of accepted statuses of the response
+ * @return the components info
+ * @throws ClientException if the response status does not match any of the expectedStatus
+ */
+ public ComponentsInfo getComponentsInfo(int... expectedStatus) throws ClientException {
+ SlingHttpResponse resp = this.doGet(URL_COMPONENTS + ".json");
+ HttpUtils.verifyHttpStatus(resp, HttpUtils.getExpectedStatus(SC_OK, expectedStatus));
+ return new ComponentsInfo(JsonUtils.getJsonNodeFromString(resp.getContent()));
+ }
+
+ /**
+ * Returns the wrapper for the component info json
+ *
+ * @param id the id of the component
+ * @param expectedStatus list of accepted statuses of the response
+ * @return the component info
+ * @throws ClientException if the response status does not match any of the expectedStatus
+ */
+ public ComponentInfo getComponentInfo(String id, int expectedStatus) throws ClientException {
+ SlingHttpResponse resp = this.doGet(URL_COMPONENTS + "/" + id + ".json");
+ HttpUtils.verifyHttpStatus(resp, HttpUtils.getExpectedStatus(SC_OK, expectedStatus));
+ return new ComponentInfo(JsonUtils.getJsonNodeFromString(resp.getContent()));
+ }
+
+ /**
+ * Returns a map of all properties set for the config referenced by the PID, where the map keys
+ * are the property names.
+ *
+ * @param pid the pid of the configuration
+ * @param expectedStatus list of accepted statuses of the response
+ * @return the properties as a map
+ * @throws ClientException if the response status does not match any of the expectedStatus
+ */
+ public Map<String, Object> getConfiguration(String pid, int... expectedStatus) throws ClientException {
+ // make the request
+ SlingHttpResponse resp = this.doPost(URL_CONFIGURATION + "/" + pid, null);
+ // check the returned status
+ HttpUtils.verifyHttpStatus(resp, HttpUtils.getExpectedStatus(SC_OK, expectedStatus));
+ // get the JSON node
+ JsonNode rootNode = JsonUtils.getJsonNodeFromString(resp.getContent());
+ // go through the params
+ Map<String, Object> props = new HashMap<String, Object>();
+ if(rootNode.get("properties") == null)
+ return props;
+ JsonNode properties = rootNode.get("properties");
+ for(Iterator<String> it = properties.getFieldNames(); it.hasNext();) {
+ String propName = it.next();
+ JsonNode value = properties.get(propName).get("value");
+ if(value != null) {
+ props.put(propName, value.getValueAsText());
+ continue;
+ }
+ value = properties.get(propName).get("values");
+ if(value != null) {
+ Iterator<JsonNode> iter = value.getElements();
+ List<String> list = new ArrayList<String>();
+ while(iter.hasNext()) {
+ list.add(iter.next().getValueAsText());
+ }
+ props.put(propName, list.toArray(new String[list.size()]));
+ }
+ }
+ 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.
+ *
+ * @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
+ * @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"
+ */
+ 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);
+ return poller.getConfig();
+ }
+
+ /**
+ * Sets properties of a config referenced by its PID. the properties to be edited are passed as
+ * a map of property name,value pairs.
+ *
+ * @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 location of the config
+ * @throws ClientException if the response status does not match any of the expectedStatus
+ */
+ public String editConfiguration(String PID, String factoryPID, Map<String, Object> configProperties, int... expectedStatus)
+ throws ClientException {
+ FormEntityBuilder builder = FormEntityBuilder.create();
+ builder.addParameter("apply", "true");
+ builder.addParameter("action", "ajaxConfigManager");
+ // send factory PID if set
+ if (factoryPID != null) {
+ builder.addParameter("factoryPid", factoryPID);
+ }
+ // add properties to edit
+ StringBuilder propertyList = new StringBuilder("");
+ for (String propName : configProperties.keySet()) {
+ Object o = configProperties.get(propName);
+ if (o instanceof String) {
+ builder.addParameter(propName, (String)o);
+ } else if (o instanceof String[]) {
+ for (String s : (String[])o) {
+ builder.addParameter(propName, s);
+ }
+ }
+ propertyList.append(propName).append(",");
+ }
+ // cut off the last comma
+ builder.addParameter("propertylist", propertyList.substring(0, propertyList.length() - 1));
+ // make the request
+ SlingHttpResponse resp = this.doPost(URL_CONFIGURATION + "/" + PID, builder.build());
+ // check the returned status
+ HttpUtils.verifyHttpStatus(resp, HttpUtils.getExpectedStatus(SC_MOVED_TEMPORARILY, expectedStatus));
+
+ Header[] locationHeader = resp.getHeaders("Location");
+ if (locationHeader!=null && locationHeader.length==1) {
+ return locationHeader[0].getValue().substring(URL_CONFIGURATION.length()+1);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * 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 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
+ * @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"
+ */
+ 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);
+ getConfigurationWithWait(waitCount, pid);
+ return pid;
+ }
+
+ /**
+ * Delete the config referenced by the PID
+ *
+ * @param pid pid
+ * @param expectedStatus expected response status
+ * @throws ClientException if the response status does not match any of the expectedStatus
+ */
+ public void 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
+ HttpUtils.verifyHttpStatus(resp, HttpUtils.getExpectedStatus(200, expectedStatus));
+ }
+
+
+ class ConfigurationPoller extends AbstractPoller {
+
+ private final String pid;
+ int[] expectedStatus;
+ public Map<String, Object> config;
+
+ public ConfigurationPoller(long waitInterval, long waitCount, String pid, int... expectedStatus) {
+ super(waitInterval, waitCount);
+ this.pid = pid;
+ this.config = null;
+ this.expectedStatus = expectedStatus;
+ }
+
+ @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 Map<String, Object> getConfig() {
+ return config;
+ }
+ }
+}
\ No newline at end of file
Added: 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=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/OsgiInstanceConfig.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/OsgiInstanceConfig.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,94 @@
+/*
+ * 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 org.apache.sling.testing.clients.ClientException;
+import org.apache.sling.testing.clients.util.config.InstanceConfig;
+import org.apache.sling.testing.clients.util.config.InstanceConfigException;
+import org.apache.sling.testing.clients.SlingClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+
+/**
+ * <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 {
+
+ /**
+ * Number of retries for retrieving the current osgi config for save() and restore()
+ */
+ protected int waitCount = 20;
+
+ private static final Logger LOG = LoggerFactory.getLogger(OsgiInstanceConfig.class);
+ private final OsgiConsoleClient osgiClient;
+ private final String configPID;
+ private Map<String, Object> config;
+
+
+ /**
+ *
+ * @param client The Granite Client to be used internally
+ * @param configPID The PID for the OSGi configuration
+ * @param <T> The type of the Granite Client
+ * @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 {
+ this.osgiClient = client.adaptTo(OsgiConsoleClient.class);
+ this.configPID = configPID;
+
+ // Save the configuration
+ save();
+ }
+
+ /**
+ * Save the current OSGi configuration for the PID defined in the constructor
+ *
+ * @throws InstanceConfigException if the config cannot be saved
+ */
+ public InstanceConfig save() throws InstanceConfigException {
+ try {
+ this.config = osgiClient.getConfigurationWithWait(waitCount, 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);
+ }
+ return this;
+ }
+
+ /**
+ * Restore the current OSGi configuration for the PID defined in the constructor
+ *
+ * @throws InstanceConfigException if the config cannot be restored
+ */
+ public InstanceConfig restore() throws InstanceConfigException {
+ try {
+ osgiClient.editConfigurationWithWait(waitCount, 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);
+ }
+ return this;
+ }
+}
Added: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/WebconsoleClient.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/WebconsoleClient.java?rev=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/WebconsoleClient.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/WebconsoleClient.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,190 @@
+/*
+ * 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);
+ }
+
+}
Added: 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=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/package-info.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/osgi/package-info.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+/**
+ * OSGI testing tools.
+ */
+@aQute.bnd.annotation.Version("1.0.0")
+package org.apache.sling.testing.clients.osgi;
Added: 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=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/package-info.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/package-info.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+@Version("1.0.0")
+package org.apache.sling.testing.clients;
+
+import aQute.bnd.annotation.Version;
+
Added: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/FormEntityBuilder.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/FormEntityBuilder.java?rev=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/FormEntityBuilder.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/FormEntityBuilder.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,81 @@
+/*
+ * 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.util;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.message.BasicNameValuePair;
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper for creating Entity objects for POST requests.
+ */
+public class FormEntityBuilder {
+ public final static String DEFAULT_ENCODING = "UTF-8";
+
+ private final List<NameValuePair> params;
+ private String encoding;
+
+ public static FormEntityBuilder create() {
+ return new FormEntityBuilder();
+ }
+
+ FormEntityBuilder() {
+ params = new ArrayList<NameValuePair>();
+ encoding = DEFAULT_ENCODING;
+ }
+
+ public FormEntityBuilder addAllParameters(Map<String, String> parameters) {
+ if (parameters != null) {
+ for (String key : parameters.keySet()) {
+ addParameter(key, parameters.get(key));
+ }
+ }
+
+ return this;
+ }
+
+ public FormEntityBuilder addAllParameters(List<NameValuePair> parameters) {
+ if (parameters != null) {
+ params.addAll(parameters);
+ }
+
+ return this;
+ }
+
+ public FormEntityBuilder addParameter(String name, String value) {
+ params.add(new BasicNameValuePair(name, value));
+ return this;
+ }
+
+ public FormEntityBuilder setEncoding(String encoding) {
+ this.encoding = encoding;
+ return this;
+ }
+
+ public UrlEncodedFormEntity build() {
+ try {
+ return new UrlEncodedFormEntity(params, encoding);
+ } catch (UnsupportedEncodingException ue) {
+ throw new Error("Unexpected UnsupportedEncodingException", ue);
+ }
+ }
+}
Added: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/HttpUtils.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/HttpUtils.java?rev=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/HttpUtils.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/HttpUtils.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,183 @@
+/*
+ * 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.util;
+
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.sling.testing.clients.ClientException;
+import org.apache.sling.testing.clients.SlingHttpResponse;
+
+
+import java.net.URI;
+
+
+public class HttpUtils {
+
+ /**
+ * Verify expected status and dump response in case expected status is not returned.
+ * Warning! It will try to consume the entity in case of error
+ *
+ * @param response The Sling HTTP response
+ * @param expectedStatus List of acceptable HTTP Statuses
+ * @throws ClientException if status is not expected
+ */
+ public static void verifyHttpStatus(SlingHttpResponse response, int... expectedStatus) throws ClientException {
+ if (!checkStatus(response, expectedStatus)) {
+ throwError(response, buildDefaultErrorMessage(response), expectedStatus);
+ }
+ }
+
+ /**
+ * Verify expected status and show error message in case expected status is not returned.
+ *
+ * @param response The SlingHttpResponse of an executed request.
+ * @param errorMessage error message; if {@code null}, errorMessage is extracted from response
+ * @param expectedStatus List of acceptable HTTP Statuses
+ * @throws ClientException if status is not expected
+ */
+ public static void verifyHttpStatus(HttpResponse response, String errorMessage, int... expectedStatus)
+ throws ClientException {
+ if (!checkStatus(response, expectedStatus)) {
+ throwError(response, errorMessage, expectedStatus);
+ }
+ }
+
+ private static boolean checkStatus(HttpResponse response, int... expectedStatus)
+ throws ClientException {
+
+ // if no HttpResponse was given
+ if (response == null) {
+ throw new NullPointerException("The response is null!");
+ }
+
+ // if no expected statuses are given
+ if (expectedStatus == null || expectedStatus.length == 0) {
+ throw new IllegalArgumentException("At least one expected HTTP Status must be set!");
+ }
+
+ // get the returned HTTP Status
+ int givenStatus = getHttpStatus(response);
+
+ // check if it matches with an expected one
+ for (int expected : expectedStatus) {
+ if (givenStatus == expected) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean throwError(HttpResponse response, String errorMessage, int... expectedStatus)
+ throws ClientException {
+ // build error message
+ String errorMsg = "Expected HTTP Status: ";
+ for (int expected : expectedStatus) {
+ errorMsg += expected + " ";
+ }
+
+ // get the returned HTTP Status
+ int givenStatus = getHttpStatus(response);
+
+ errorMsg += ". Instead " + givenStatus + " was returned!\n";
+ if (errorMessage != null) {
+ errorMsg += errorMessage;
+ }
+
+ // throw the exception
+ throw new ClientException(errorMsg);
+ }
+
+
+ /**
+ * Build default error message
+ *
+ * @param resp The response of a sling request
+ * @return default error message
+ */
+ public static String buildDefaultErrorMessage(SlingHttpResponse resp) {
+
+ String content = resp.getContent();
+
+ // if no response content is available
+ if (content == null) return "";
+ String errorMsg = resp.getSlingMessage();
+
+ errorMsg = (errorMsg == null || errorMsg.length() == 0)
+ // any other returned content
+ ? " Response Content:\n" + content
+ // response message from sling response
+ : "Error Message: \n" + errorMsg;
+
+ return errorMsg;
+ }
+
+ /**
+ * Get HTTP Status of the response.
+ *
+ * @param response The RequestExecutor of an executed request.
+ * @return The HTTP Status of the response
+ * @throws ClientException never (kept for uniformity)
+ */
+ public static int getHttpStatus(HttpResponse response) throws ClientException {
+ return response.getStatusLine().getStatusCode();
+ }
+
+ /**
+ * Get the first 'Location' header and verify it's a valid URI.
+ *
+ * @param response HttpResponse the http response
+ * @return the location path
+ * @throws ClientException never (kept for uniformity)
+ */
+ public static String getLocationHeader(HttpResponse response) throws ClientException {
+ if (response == null) throw new ClientException("Response must not be null!");
+
+ String locationPath = null;
+ Header locationHeader = response.getFirstHeader("Location");
+ if (locationHeader != null) {
+ String location = locationHeader.getValue();
+ URI locationURI = URI.create(location);
+ locationPath = locationURI.getPath();
+ }
+
+ if (locationPath == null) {
+ throw new ClientException("not able to determine location path");
+ }
+ return locationPath;
+ }
+
+ /**
+ * Check if expected status is in range
+ *
+ * @param response the http response
+ * @param range the http status range
+ * @return true if response is in range
+ */
+ public static boolean isInHttpStatusRange(HttpResponse response, int range) {
+ return range == response.getStatusLine().getStatusCode() / 100 * 100;
+ }
+
+ public static int[] getExpectedStatus(int defaultStatus, int... expectedStatus) {
+ if (expectedStatus == null || expectedStatus.length == 0) {
+ expectedStatus = new int[]{defaultStatus};
+ }
+ return expectedStatus;
+ }
+
+
+}
\ No newline at end of file
Added: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/InputStreamBodyWithLength.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/InputStreamBodyWithLength.java?rev=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/InputStreamBodyWithLength.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/InputStreamBodyWithLength.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,69 @@
+/*
+ * 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.util;
+
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.mime.content.InputStreamBody;
+import org.apache.sling.testing.clients.ClientException;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * If we want to upload a file that is a resource in a jar file, the http client expects a content length.
+ */
+public class InputStreamBodyWithLength extends InputStreamBody {
+ private long streamLength;
+
+ public InputStreamBodyWithLength(String resourcePath, String contentType, String fileName) throws ClientException {
+ super(ResourceUtil.getResourceAsStream(resourcePath), ContentType.create(contentType), fileName);
+ this.streamLength = getResourceStreamLength(resourcePath);
+ }
+
+ @Override
+ public long getContentLength() {
+ return streamLength;
+ }
+
+ /**
+ * Returns the length of a resource (which is needed for the InputStreamBody
+ * to work. Can't currently think of a better solution than going through
+ * the resource stream and count.
+ *
+ * @param resourcePath path to the file
+ * @return the size of the resource
+ */
+ private static long getResourceStreamLength(String resourcePath) throws ClientException {
+ int streamLength = 0;
+ InputStream stream = ResourceUtil.getResourceAsStream(resourcePath);
+ try {
+ for (int avail = stream.available(); avail > 0; avail = stream.available()) {
+ streamLength += avail;
+ stream.skip(avail);
+ }
+ } catch (IOException e) {
+ throw new ClientException("Could not read " + resourcePath + "!", e);
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ throw new ClientException("Could not close Inputstream for " + resourcePath + "!", e);
+ }
+ }
+ return streamLength;
+ }
+}
\ No newline at end of file
Added: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/JsonUtils.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/JsonUtils.java?rev=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/JsonUtils.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/JsonUtils.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,44 @@
+/*
+ * 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.util;
+
+import org.apache.sling.testing.clients.ClientException;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.map.ObjectMapper;
+
+import java.io.IOException;
+
+public class JsonUtils {
+ /**
+ * Get {@link JsonNode} from a a String containing JSON.
+ *
+ * @param jsonString A string containing JSON
+ * @return A {@link JsonNode} that is the root node of the JSON structure.
+ * @throws ClientException if error occurs while reading json string
+ */
+ public static JsonNode getJsonNodeFromString(String jsonString) throws ClientException {
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ return mapper.readTree(jsonString);
+ } catch (JsonProcessingException e) {
+ throw new ClientException("Could not read json file.", e);
+ } catch (IOException e) {
+ throw new ClientException("Could not read json node.", e);
+ }
+ }
+}
\ No newline at end of file
Added: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/PortAllocator.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/PortAllocator.java?rev=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/PortAllocator.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/PortAllocator.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,58 @@
+/*
+ * 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.util;
+
+import java.net.ServerSocket;
+import java.util.HashSet;
+import java.util.Set;
+
+public class PortAllocator {
+
+ private static Set<Integer> allocatedPorts;
+
+ static {
+ allocatedPorts = new HashSet<Integer>();
+ }
+
+ public Integer allocatePort() {
+ while (true) {
+ int port = tryAllocation();
+
+ boolean portAdded = checkAndAddPort(port);
+
+ if (portAdded) {
+ return port;
+ }
+ }
+ }
+
+ private int tryAllocation() {
+ try {
+ ServerSocket serverSocket = new ServerSocket(0);
+ int port = serverSocket.getLocalPort();
+ serverSocket.close();
+ return port;
+ } catch (Exception e) {
+ throw new RuntimeException("Can't allocate a port");
+ }
+ }
+
+ private synchronized boolean checkAndAddPort(int port) {
+ return allocatedPorts.add(port);
+ }
+
+}
Added: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/ResourceUtil.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/ResourceUtil.java?rev=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/ResourceUtil.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/ResourceUtil.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,66 @@
+/*
+ * 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.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+public class ResourceUtil {
+
+ /**
+ * We must get the Resource as a stream from the ContextClassLoader and not from the normal classLoader
+ * acquired by using getClass.getClassLoader, since we must be able to load resources from different threads
+ * e.g. running in ant.
+ *
+ * @param resourcePath path to the resource
+ * @return resource as InputStream
+ */
+ public static InputStream getResourceAsStream(String resourcePath) {
+ return Thread.currentThread().getContextClassLoader().getClass().getResourceAsStream(resourcePath);
+ }
+
+ /**
+ * Helper method to read a resource from class using {@link Class#getResourceAsStream(String)}
+ * and convert into a String.
+ *
+ * @param resource The resource to read.
+ * @return The requested resource as String, resolved using
+ * {@link Class#getResourceAsStream(String)}, or {@code null}
+ * if the requested resource cannot be resolved for some reason
+ * @throws IOException if the Resource Stream cannot be read
+ */
+ public static String readResourceAsString(String resource) throws IOException {
+ InputStream resourceAsStream = ResourceUtil.getResourceAsStream(resource);
+ if (resourceAsStream != null) {
+ StringBuilder sb = new StringBuilder();
+ String line;
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(resourceAsStream, "UTF-8"));
+ while ((line = reader.readLine()) != null) {
+ sb.append(line).append("\n");
+ }
+ } finally {
+ resourceAsStream.close();
+ }
+ return sb.toString();
+ }
+ return null;
+ }
+
+}
Added: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/SlingParameter.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/SlingParameter.java?rev=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/SlingParameter.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/SlingParameter.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,108 @@
+/*
+ * 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.util;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.message.BasicNameValuePair;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ */
+public class SlingParameter {
+
+ private String typeHint = null;
+ private boolean delete = false;
+
+ String parameterName;
+ private String[] values = null;
+ private boolean multiple = false;
+
+ public SlingParameter(String parameterName) {
+ if (parameterName == null || parameterName.length() == 0) {
+ throw new IllegalArgumentException("parameterName must not be null or empty");
+ }
+ this.parameterName = parameterName;
+ }
+
+ public SlingParameter value(String value) {
+ if (value != null) {
+ this.values(new String[]{value});
+ } else {
+ this.values(new String[]{});
+ }
+ return this;
+ }
+
+ public SlingParameter values(String[] values) {
+ if (values == null) {
+ this.values = new String[]{};
+ } else {
+ this.values = values;
+ }
+ return this;
+ }
+
+ public SlingParameter typeHint(String typeHint) {
+ this.typeHint = typeHint;
+ return this;
+ }
+
+ public SlingParameter delete() {
+ this.delete = true;
+ return this;
+ }
+
+ public SlingParameter multiple() {
+ this.multiple = true;
+ return this;
+ }
+
+ public List<NameValuePair> toNameValuePairs() {
+ List<NameValuePair> parameters = new ArrayList<NameValuePair>();
+
+ if (multiple) {
+ for (String value : values) {
+ parameters.add(new BasicNameValuePair(parameterName, value));
+ }
+ } else if (values != null && values.length == 1) {
+ parameters.add(new BasicNameValuePair(parameterName, values[0]));
+ } else if (values != null && values.length > 1) {
+ // TODO not sure about the proper format of the values in this case?
+ // For now, only take the first one.
+ parameters.add(new BasicNameValuePair(parameterName, values[0]));
+ } else {
+ parameters.add(new BasicNameValuePair(parameterName, null));
+ }
+
+ // add @TypeHint suffix
+ if (typeHint != null) {
+ String parameter = parameterName + "@TypeHint";
+ parameters.add(new BasicNameValuePair(parameter, typeHint));
+ }
+
+ // add @Delete suffix
+ if (delete) {
+ String parameter = parameterName + "@Delete";
+ parameters.add(new BasicNameValuePair(parameter, "true"));
+ }
+
+ return parameters;
+ }
+}
Added: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/URLParameterBuilder.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/URLParameterBuilder.java?rev=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/URLParameterBuilder.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/URLParameterBuilder.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,79 @@
+/*
+ * 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.util;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.client.utils.URLEncodedUtils;
+import org.apache.http.message.BasicNameValuePair;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class URLParameterBuilder {
+
+ public final static String DEFAULT_ENCODING = "UTF-8";
+
+ private List<NameValuePair> params;
+ private String encoding;
+
+ public static URLParameterBuilder create() {
+ return new URLParameterBuilder();
+ }
+
+ URLParameterBuilder() {
+ params = new ArrayList<NameValuePair>();
+ encoding = DEFAULT_ENCODING;
+ }
+
+ public URLParameterBuilder add(String name, String value) {
+ params.add(new BasicNameValuePair(name, value));
+ return this;
+ }
+
+ public URLParameterBuilder add(NameValuePair pair) {
+ params.add(pair);
+ return this;
+ }
+
+ public URLParameterBuilder add(List<NameValuePair> list) {
+ params.addAll(list);
+ return this;
+ }
+
+ public URLParameterBuilder add(String name, String[] values) {
+ for (String value : values) this.add(name, value);
+ return this;
+ }
+
+ public URLParameterBuilder setEncoding(String encoding) {
+ this.encoding = encoding;
+ return this;
+ }
+
+ /**
+ * Build the URL parameters
+ *
+ * @return The URL parameters string without the leading question mark.
+ */
+ public String getURLParameters() {
+ return URLEncodedUtils.format(params, encoding);
+ }
+
+ public List<NameValuePair> getList() {
+ return params;
+ }
+}
Added: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/UniquePaths.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/UniquePaths.java?rev=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/UniquePaths.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/UniquePaths.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,67 @@
+/*
+ * 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.util;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/** Generate unique paths, for tests isolation */
+public class UniquePaths {
+
+ private static long startTime = System.currentTimeMillis();
+ private static AtomicLong counter = new AtomicLong();
+ public final static String SEP = "_";
+ public final static String U_PATTERN = "_UNIQ_";
+
+ /**
+ * Return a unique path based on basePath
+ * @param nameReference The simple class name of that object is used as part of the
+ * generated unique ID
+ * @param basePath All occurrences of {@link UniquePaths#U_PATTERN} in basePath are replaced by the generated
+ * unique ID. If $U$ is not found in basePath, unique ID is added at its end.
+ * @return path with a unique value for each call.
+ */
+ public static String get(Object nameReference, String basePath) {
+ if(basePath == null) {
+ basePath = "";
+ }
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append(nameReference.getClass().getSimpleName());
+ sb.append(SEP);
+ sb.append(startTime);
+ sb.append(SEP);
+ sb.append(counter.incrementAndGet());
+
+ if(basePath.contains(U_PATTERN)) {
+ return basePath.replaceAll(U_PATTERN, sb.toString());
+ } else {
+ return basePath + sb.toString();
+ }
+ }
+
+ /**
+ * Get a unique ID with no base path
+ *
+ * @param nameReference The simple class name of that object is used as part of the
+ * generated unique ID
+ * @return path with a unique value for each call
+ */
+ public static String get(Object nameReference) {
+ return get(nameReference, null);
+ }
+}
Added: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/XSSUtils.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/XSSUtils.java?rev=1741632&view=auto
==============================================================================
--- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/XSSUtils.java (added)
+++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/XSSUtils.java Fri Apr 29 14:03:32 2016
@@ -0,0 +1,137 @@
+/*
+ * 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.util;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.sling.xss.XSSAPI;
+import org.apache.sling.xss.impl.XSSAPIImpl;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+/**
+ * Basic class for XSS Testing
+ * The reliability of these methods are not critical
+ */
+public class XSSUtils {
+
+ /**
+ * Use to ensure that HTTP query strings are in proper form, by escaping
+ * special characters such as spaces.
+ *
+ * @param urlString the string to be encoded
+ * @return the encoded string
+ */
+ public static String encodeUrl(String urlString) {
+ try {
+ return URLEncoder.encode(urlString, "UTF-8");
+ } catch (UnsupportedEncodingException ex) {
+ throw new RuntimeException("UTF-8 not supported", ex);
+ }
+ }
+
+ /**
+ * Use to encapsulate old-style escaping of HTML (using StringEscapeUtils).
+ * NB: newer code uses XSSAPI (based on OWASP's ESAPI).
+ *
+ * @param htmlString the string to be escaped
+ * @return the escaped string
+ */
+ public static String escapeHtml(String htmlString) {
+ return StringEscapeUtils.escapeHtml4(htmlString);
+ }
+
+ /**
+ * Use to encapsulate old-style escaping of XML (with JSTL encoding rules).
+ * NB: newer code uses XSSAPI (based on OWASP's ESAPI).
+ *
+ * @param xmlString the string to be escaped
+ * @return the escaped string
+ */
+ public static String escapeXml(String xmlString) {
+ String xssString = xmlString;
+ if (xmlString != null) {
+ xssString = xssString.replace(";", ";");
+ xssString = xssString.replace(" ", " ");
+ xssString = xssString.replace("'", "'");
+ xssString = xssString.replace("\"", """);
+ xssString = xssString.replace(">", ">");
+ xssString = xssString.replace("<", "<");
+ xssString = xssString.replace("/", "/");
+ xssString = xssString.replace("(", "(");
+ xssString = xssString.replace(")", ")");
+ xssString = xssString.replace(":", ":");
+ }
+ return xssString;
+ }
+
+ /**
+ * Use to encapsulate new-style (XSSAPI-based) encoding for HTML element content.
+ *
+ * @param source the string to be encoded
+ * @return the encoded string
+ */
+ public static String encodeForHTML(String source) {
+ XSSAPI xssAPI = new XSSAPIImpl();
+ return xssAPI.encodeForHTML(source);
+ }
+
+ /**
+ * Use to encapsulate new-style (XSSAPI-based) encoding for HTML attribute values.
+ *
+ * @param source the string to be encoded
+ * @return the encoded string
+ */
+ public static String encodeForHTMLAttr(String source) {
+ XSSAPI xssAPI = new XSSAPIImpl();
+ return xssAPI.encodeForHTMLAttr(source);
+ }
+
+ /**
+ * Use to encapsulate new-style (XSSAPI-based) encoding for XML element content.
+ *
+ * @param source the string to be encoded
+ * @return the encoded string
+ */
+ public static String encodeForXML(String source) {
+ XSSAPI xssAPI = new XSSAPIImpl();
+ return xssAPI.encodeForXML(source);
+ }
+
+ /**
+ * Use to encapsulate new-style (XSSAPI-based) encoding for XML attribute values.
+ *
+ * @param source the string to be encoded
+ * @return the encoded string
+ */
+ public static String encodeForXMLAttr(String source) {
+ XSSAPI xssAPI = new XSSAPIImpl();
+ return xssAPI.encodeForXMLAttr(source);
+ }
+
+ /**
+ * Use to encapsulate new-style (XSSAPI-based) encoding for JavaScript strings.
+ *
+ * @param source the string to be encoded
+ * @return the encoded string
+ */
+ public static String encodeForJSString(String source) {
+ XSSAPI xssAPI = new XSSAPIImpl();
+ return xssAPI.encodeForJSString(source);
+ }
+
+}