You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by jo...@apache.org on 2022/06/02 15:00:57 UTC

[sling-org-apache-sling-testing-clients] 01/01: SLING-11364 use specialized exceptions

This is an automated email from the ASF dual-hosted git repository.

joerghoh pushed a commit to branch SLING-11364-more-precise-exceptions
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-clients.git

commit da17429b23e580a5bfcbcb81142bc3d191b367d2
Author: Joerg Hoh <jh...@adobe.com>
AuthorDate: Thu Jun 2 16:58:55 2022 +0200

    SLING-11364 use specialized exceptions
---
 .../sling/testing/clients/AbstractSlingClient.java |  7 +--
 .../sling/testing/clients/ClientException.java     | 15 +++++-
 .../apache/sling/testing/clients/SlingClient.java  |  3 +-
 .../sling/testing/clients/SlingClientConfig.java   |  5 +-
 .../sling/testing/clients/SlingHttpResponse.java   |  9 ++--
 .../testing/clients/email/SlingEmailClient.java    |  6 ++-
 .../exceptions/TestValidationException.java        | 55 ++++++++++++++++++++++
 .../clients/exceptions/TestingIOException.java     | 48 +++++++++++++++++++
 .../testing/clients/indexing/IndexingClient.java   |  3 +-
 .../sling/testing/clients/osgi/BundleInfo.java     |  8 ++--
 .../sling/testing/clients/osgi/BundlesInfo.java    | 18 +++----
 .../testing/clients/osgi/BundlesInstaller.java     |  3 +-
 .../sling/testing/clients/osgi/ComponentInfo.java  |  8 ++--
 .../sling/testing/clients/osgi/ComponentsInfo.java | 14 +++---
 .../testing/clients/osgi/OsgiConsoleClient.java    | 20 ++++----
 .../sling/testing/clients/osgi/ServiceInfo.java    |  6 +--
 .../sling/testing/clients/osgi/ServicesInfo.java   | 12 ++---
 .../apache/sling/testing/clients/package-info.java |  2 +-
 .../sling/testing/clients/query/QueryClient.java   |  6 ++-
 .../sling/testing/clients/util/HttpUtils.java      | 19 ++++----
 .../clients/util/InputStreamBodyWithLength.java    | 10 ++--
 .../sling/testing/clients/util/JsonUtils.java      |  6 ++-
 22 files changed, 206 insertions(+), 77 deletions(-)

diff --git a/src/main/java/org/apache/sling/testing/clients/AbstractSlingClient.java b/src/main/java/org/apache/sling/testing/clients/AbstractSlingClient.java
index 59622c8..bc0ce7a 100644
--- a/src/main/java/org/apache/sling/testing/clients/AbstractSlingClient.java
+++ b/src/main/java/org/apache/sling/testing/clients/AbstractSlingClient.java
@@ -25,6 +25,7 @@ import org.apache.http.client.utils.URIBuilder;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.message.BasicHttpRequest;
 import org.apache.http.protocol.HttpContext;
+import org.apache.sling.testing.clients.exceptions.TestingIOException;
 import org.apache.sling.testing.clients.util.HttpUtils;
 import org.slf4j.LoggerFactory;
 
@@ -227,7 +228,7 @@ public class AbstractSlingClient implements HttpClient, Closeable {
             Constructor cons = clientClass.getConstructor(CloseableHttpClient.class, SlingClientConfig.class);
             client = (T) cons.newInstance(this.http, this.config);
         } catch (Exception e) {
-            throw new ClientException("Could not initialize client: '" + clientClass.getCanonicalName() + "'.", e);
+            throw new TestingIOException("Could not initialize client: '" + clientClass.getCanonicalName() + "'.", e);
         }
         return client;
     }
@@ -334,7 +335,7 @@ public class AbstractSlingClient implements HttpClient, Closeable {
 
             return response;
         } catch (IOException e) {
-            throw new ClientException("Could not execute http request", e, request, response);
+            throw new TestingIOException("Could not execute http request", e, request, response);
         }
     }
 
@@ -386,7 +387,7 @@ public class AbstractSlingClient implements HttpClient, Closeable {
 
             return response;
         } catch (IOException e) {
-            throw new ClientException("Could not execute http request", e);
+            throw new TestingIOException("Could not execute http request", e);
         }
     }
 
diff --git a/src/main/java/org/apache/sling/testing/clients/ClientException.java b/src/main/java/org/apache/sling/testing/clients/ClientException.java
index e89dbc9..0579acd 100644
--- a/src/main/java/org/apache/sling/testing/clients/ClientException.java
+++ b/src/main/java/org/apache/sling/testing/clients/ClientException.java
@@ -19,7 +19,15 @@ package org.apache.sling.testing.clients;
 import org.apache.http.client.methods.HttpUriRequest;
 
 /**
- * An exception thrown when something went wrong with using the sling testing clients
+ * An exception thrown when something went wrong with using the sling testing clients;
+ * Do not instantiate this class directly, but rather use one of its sub-classes:
+ * <ul>
+ *   <li>TestingIOException for any type of network or IO related error</li>
+ *   <li>TestValidationException for any type of unmet expectation</li>
+ * </ul>
+ * 
+ * Note: This change is done in preparation to make this class abstract.
+ * 
  */
 public class ClientException extends Exception {
 
@@ -28,23 +36,28 @@ public class ClientException extends Exception {
     private HttpUriRequest request;
     private SlingHttpResponse response;
 
+    @Deprecated
     public ClientException(String message) {
         this(message, null);
     }
 
+    @Deprecated
     public ClientException(String message, Throwable throwable) {
         this(message, -1, throwable);
     }
 
+    @Deprecated
     public ClientException(String message, int httpStatusCode) {
         this(message, httpStatusCode, null);
     }
 
+    @Deprecated
     public ClientException(String message, int httpStatusCode, Throwable throwable) {
         super(message, throwable);
         this.httpStatusCode = httpStatusCode;
     }
 
+    @Deprecated
     public ClientException(String message, Throwable throwable, HttpUriRequest request, SlingHttpResponse response) {
         this(message, throwable);
         this.request = request;
diff --git a/src/main/java/org/apache/sling/testing/clients/SlingClient.java b/src/main/java/org/apache/sling/testing/clients/SlingClient.java
index baa3a0c..9156d90 100644
--- a/src/main/java/org/apache/sling/testing/clients/SlingClient.java
+++ b/src/main/java/org/apache/sling/testing/clients/SlingClient.java
@@ -41,6 +41,7 @@ import org.apache.http.entity.ContentType;
 import org.apache.http.entity.mime.MultipartEntityBuilder;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.sling.testing.clients.exceptions.TestValidationException;
 import org.apache.sling.testing.clients.interceptors.DelayRequestInterceptor;
 import org.apache.sling.testing.clients.interceptors.HttpRequestResponseInterceptor;
 import org.apache.sling.testing.clients.interceptors.TestDescriptionInterceptor;
@@ -250,7 +251,7 @@ public class SlingClient extends AbstractSlingClient {
 
         boolean found = poller.callUntilCondition();
         if (!found) {
-            throw new ClientException("path " + path + " does not exist after " + retryCount + " retries");
+            throw new TestValidationException("path " + path + " does not exist after " + retryCount + " retries");
         }
     }
 
diff --git a/src/main/java/org/apache/sling/testing/clients/SlingClientConfig.java b/src/main/java/org/apache/sling/testing/clients/SlingClientConfig.java
index 825dfe8..126b315 100644
--- a/src/main/java/org/apache/sling/testing/clients/SlingClientConfig.java
+++ b/src/main/java/org/apache/sling/testing/clients/SlingClientConfig.java
@@ -30,6 +30,7 @@ import org.apache.http.impl.auth.BasicScheme;
 import org.apache.http.impl.client.BasicAuthCache;
 import org.apache.http.impl.client.BasicCookieStore;
 import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.sling.testing.clients.exceptions.TestingIOException;
 
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -209,12 +210,12 @@ public class SlingClientConfig {
 
         public SlingClientConfig build() throws ClientException {
             if (!this.url.isAbsolute()) {
-                throw new ClientException("Url must be absolute: " + url);
+                throw new TestingIOException("Url must be absolute: " + url);
             }
 
             HttpHost targetHost = URIUtils.extractHost(this.url);
             if (targetHost == null) {
-                throw new ClientException("Failed to extract hostname from url " + url);
+                throw new TestingIOException("Failed to extract hostname from url " + url);
             }
 
             // Create default CredentialsProvider if not set
diff --git a/src/main/java/org/apache/sling/testing/clients/SlingHttpResponse.java b/src/main/java/org/apache/sling/testing/clients/SlingHttpResponse.java
index 9f19f9e..596d4fc 100644
--- a/src/main/java/org/apache/sling/testing/clients/SlingHttpResponse.java
+++ b/src/main/java/org/apache/sling/testing/clients/SlingHttpResponse.java
@@ -31,6 +31,7 @@ import org.apache.http.ProtocolVersion;
 import org.apache.http.StatusLine;
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.util.EntityUtils;
+import org.apache.sling.testing.clients.exceptions.TestValidationException;
 
 public class SlingHttpResponse implements CloseableHttpResponse {
 
@@ -81,7 +82,7 @@ public class SlingHttpResponse implements CloseableHttpResponse {
      */
     public void checkStatus(int expected) throws ClientException {
         if (this.getStatusLine().getStatusCode() != expected) {
-            throw new ClientException(this + " has wrong response status ("
+            throw new TestValidationException(this + " has wrong response status ("
                     + this.getStatusLine().getStatusCode() + "). Expected " + expected);
         }
     }
@@ -101,7 +102,7 @@ public class SlingHttpResponse implements CloseableHttpResponse {
 
         // check for match
         if (!contentType.equals(expected)) {
-            throw new ClientException(this + " has wrong content type (" + contentType + "). Expected " + expected);
+            throw new TestValidationException(this + " has wrong content type (" + contentType + "). Expected " + expected);
         }
     }
 
@@ -127,7 +128,7 @@ public class SlingHttpResponse implements CloseableHttpResponse {
             }
 
             if (!matched) {
-                throw new ClientException("Pattern " + p + " didn't match any line in content. Content is: \n\n" + getContent());
+                throw new TestValidationException("Pattern " + p + " didn't match any line in content. Content is: \n\n" + getContent());
             }
         }
     }
@@ -141,7 +142,7 @@ public class SlingHttpResponse implements CloseableHttpResponse {
     public void checkContentContains(String... expected) throws ClientException {
         for (String s : expected) {
             if (!this.getContent().contains(s)) {
-                throw new ClientException("Content does not contain string " + s + ". Content is: \n\n" + getContent());
+                throw new TestValidationException("Content does not contain string " + s + ". Content is: \n\n" + getContent());
             }
         }
     }
diff --git a/src/main/java/org/apache/sling/testing/clients/email/SlingEmailClient.java b/src/main/java/org/apache/sling/testing/clients/email/SlingEmailClient.java
index a41c72f..10d2641 100644
--- a/src/main/java/org/apache/sling/testing/clients/email/SlingEmailClient.java
+++ b/src/main/java/org/apache/sling/testing/clients/email/SlingEmailClient.java
@@ -34,6 +34,8 @@ import org.apache.sling.testing.clients.ClientException;
 import org.apache.sling.testing.clients.SlingClient;
 import org.apache.sling.testing.clients.SlingClientConfig;
 import org.apache.sling.testing.clients.SlingHttpResponse;
+import org.apache.sling.testing.clients.exceptions.TestValidationException;
+import org.apache.sling.testing.clients.exceptions.TestingIOException;
 import org.codehaus.jackson.JsonNode;
 import org.codehaus.jackson.map.ObjectMapper;
 
@@ -74,7 +76,7 @@ public final class SlingEmailClient extends SlingClient {
 			JsonNode configNode = mapper.readTree(mockEmailConfig.getContent());
 			return configNode.get("bindPort").getIntValue();
 		} catch (IOException e) {
-			throw new ClientException("Failed retrieving configuration", e);
+			throw new TestingIOException("Failed retrieving configuration", e);
 		}
 	}
 	
@@ -104,7 +106,7 @@ public final class SlingEmailClient extends SlingClient {
 				emails.add(msg);
 			}
 		} catch (IOException e) {
-			throw new ClientException("Failed retrieving email messages", e);
+			throw new TestValidationException("Failed retrieving email messages", e);
 		}
         
         
diff --git a/src/main/java/org/apache/sling/testing/clients/exceptions/TestValidationException.java b/src/main/java/org/apache/sling/testing/clients/exceptions/TestValidationException.java
new file mode 100644
index 0000000..fddd3af
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/clients/exceptions/TestValidationException.java
@@ -0,0 +1,55 @@
+/*
+ * 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.exceptions;
+
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.sling.testing.clients.ClientException;
+import org.apache.sling.testing.clients.SlingHttpResponse;
+
+/**
+ * indicates that an expected status is not given, this can include
+ * <ul>
+ *   <li>HTTP status codes</li>
+ *   <li>Content within the response</li>
+ *   <li>...
+ * </ul>
+ *
+ */
+
+public class TestValidationException extends ClientException {
+
+	public TestValidationException(String message) {
+		super(message);
+	}
+	
+    public TestValidationException(String message, Throwable throwable) {
+        super(message, throwable);
+    }
+
+    public TestValidationException(String message, int httpStatusCode) {
+        super(message,httpStatusCode);
+    }
+
+    public TestValidationException(String message, int httpStatusCode, Throwable throwable) {
+    	super(message,httpStatusCode, throwable);
+    }
+
+    public TestValidationException(String message, Throwable throwable, HttpUriRequest request, SlingHttpResponse response) {
+    	super(message,throwable,request,response);
+    }
+	
+}
diff --git a/src/main/java/org/apache/sling/testing/clients/exceptions/TestingIOException.java b/src/main/java/org/apache/sling/testing/clients/exceptions/TestingIOException.java
new file mode 100644
index 0000000..39e83c2
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/clients/exceptions/TestingIOException.java
@@ -0,0 +1,48 @@
+/*
+ * 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.exceptions;
+
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.sling.testing.clients.ClientException;
+import org.apache.sling.testing.clients.SlingHttpResponse;
+
+/**
+ * Indicates any type of network and connection issue
+ *
+ */
+public class TestingIOException extends ClientException {
+
+	public TestingIOException(String message) {
+		super(message);
+	}
+
+    public TestingIOException(String message, Throwable throwable) {
+        super(message, throwable);
+    }
+
+    public TestingIOException(String message, int httpStatusCode) {
+        super(message,httpStatusCode);
+    }
+
+    public TestingIOException(String message, int httpStatusCode, Throwable throwable) {
+    	super(message,httpStatusCode, throwable);
+    }
+
+    public TestingIOException(String message, Throwable throwable, HttpUriRequest request, SlingHttpResponse response) {
+    	super(message,throwable,request,response);
+    }
+}
diff --git a/src/main/java/org/apache/sling/testing/clients/indexing/IndexingClient.java b/src/main/java/org/apache/sling/testing/clients/indexing/IndexingClient.java
index 1af1e4a..9a35ab8 100644
--- a/src/main/java/org/apache/sling/testing/clients/indexing/IndexingClient.java
+++ b/src/main/java/org/apache/sling/testing/clients/indexing/IndexingClient.java
@@ -23,6 +23,7 @@ import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.sling.testing.clients.ClientException;
 import org.apache.sling.testing.clients.SlingClient;
 import org.apache.sling.testing.clients.SlingClientConfig;
+import org.apache.sling.testing.clients.exceptions.TestValidationException;
 import org.apache.sling.testing.clients.osgi.OsgiConsoleClient;
 import org.apache.sling.testing.clients.query.QueryClient;
 import org.apache.sling.testing.clients.util.poller.Polling;
@@ -202,7 +203,7 @@ public class IndexingClient extends SlingClient {
         if (configs instanceof String[]) {
             return Stream.of((String[]) configs).map(e -> e.split(":")[0]).collect(Collectors.toList());
         } else {
-            throw new ClientException("Cannot retrieve config from AsyncIndexerService, asyncConfigs is not a String[]");
+            throw new TestValidationException("Cannot retrieve config from AsyncIndexerService, asyncConfigs is not a String[]");
         }
     }
 
diff --git a/src/main/java/org/apache/sling/testing/clients/osgi/BundleInfo.java b/src/main/java/org/apache/sling/testing/clients/osgi/BundleInfo.java
index 33cdd22..0d85737 100644
--- a/src/main/java/org/apache/sling/testing/clients/osgi/BundleInfo.java
+++ b/src/main/java/org/apache/sling/testing/clients/osgi/BundleInfo.java
@@ -17,7 +17,7 @@
 
 package org.apache.sling.testing.clients.osgi;
 
-import org.apache.sling.testing.clients.ClientException;
+import org.apache.sling.testing.clients.exceptions.TestValidationException;
 import org.codehaus.jackson.JsonNode;
 
 import java.util.HashMap;
@@ -28,15 +28,15 @@ public class BundleInfo {
 
     private JsonNode bundle;
 
-    public BundleInfo(JsonNode root) throws ClientException {
+    public BundleInfo(JsonNode root) throws TestValidationException {
         if(root.get("id") != null) {
             if(root.get("id") == null) {
-                throw new ClientException("No Bundle Info returned");
+                throw new TestValidationException("No Bundle Info returned");
             }
             bundle = root;
         } else {
             if(root.get("data") == null && root.get("data").size() < 1) {
-                throw new ClientException("No Bundle Info returned");
+                throw new TestValidationException("No Bundle Info returned");
             }
             bundle = root.get("data").get(0);
         }
diff --git a/src/main/java/org/apache/sling/testing/clients/osgi/BundlesInfo.java b/src/main/java/org/apache/sling/testing/clients/osgi/BundlesInfo.java
index b99fbf5..239e0d1 100644
--- a/src/main/java/org/apache/sling/testing/clients/osgi/BundlesInfo.java
+++ b/src/main/java/org/apache/sling/testing/clients/osgi/BundlesInfo.java
@@ -17,7 +17,7 @@
 
 package org.apache.sling.testing.clients.osgi;
 
-import org.apache.sling.testing.clients.ClientException;
+import org.apache.sling.testing.clients.exceptions.TestValidationException;
 import org.codehaus.jackson.JsonNode;
 
 import java.util.Iterator;
@@ -37,13 +37,13 @@ public class BundlesInfo {
      * @param root the root JSON node of the bundles info.
      * @throws ClientException if the json does not contain the proper info
      */
-    public BundlesInfo(JsonNode root) throws ClientException {
+    public BundlesInfo(JsonNode root) throws TestValidationException {
         this.root = root;
         // some simple sanity checks
         if(root.get("s") == null)
-            throw new ClientException("No Status Info returned!");
+            throw new TestValidationException("No Status Info returned!");
         if(root.get("s").size() != 5)
-            throw new ClientException("Wrong number of status numbers listed!");
+            throw new TestValidationException("Wrong number of status numbers listed!");
         status = root.get("s");
     }
 
@@ -51,9 +51,9 @@ public class BundlesInfo {
      * @return the status message of the bundle context
      * @throws ClientException if the request cannot be completed
      */
-    public String getStatusMessage() throws ClientException {
+    public String getStatusMessage() throws TestValidationException {
         if(root.get("status") == null)
-            throw new ClientException("No Status message returned!");
+            throw new TestValidationException("No Status message returned!");
         return root.get("status").getValueAsText();
     }
 
@@ -96,7 +96,7 @@ public class BundlesInfo {
      * @return the BundleInfo
      * @throws ClientException if the info could not be retrieved
      */
-    public BundleInfo forId(String id) throws ClientException {
+    public BundleInfo forId(String id) throws TestValidationException {
         JsonNode bundle = findBy("id", id);
         return (bundle != null) ? new BundleInfo(bundle) : null;
     }
@@ -108,7 +108,7 @@ public class BundlesInfo {
      * @return the info, or {@code null} if the bundle is not found
      * @throws ClientException if the info cannot be retrieved
      */
-    public BundleInfo forName(String name) throws ClientException {
+    public BundleInfo forName(String name) throws TestValidationException {
         JsonNode bundle = findBy("name", name);
         return (bundle != null) ? new BundleInfo(bundle) : null;
     }
@@ -120,7 +120,7 @@ public class BundlesInfo {
      * @return the info, or {@code null} if the bundle is not found
      * @throws ClientException if the info cannot be retrieved
      */
-    public BundleInfo forSymbolicName(String name) throws ClientException {
+    public BundleInfo forSymbolicName(String name) throws TestValidationException {
         JsonNode bundle = findBy("symbolicName", name);
         return (bundle != null) ? new BundleInfo(bundle) : null;
     }
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 bf342a6..d67e5e3 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
@@ -17,6 +17,7 @@
 package org.apache.sling.testing.clients.osgi;
 
 import org.apache.sling.testing.clients.ClientException;
+import org.apache.sling.testing.clients.exceptions.TestValidationException;
 import org.apache.sling.testing.clients.util.poller.Polling;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -59,7 +60,7 @@ public class BundlesInstaller {
             return false;
         } catch (IOException e) {
             log.debug("Failed to retrieve bundle symbolic name from file. ", e);
-            throw new ClientException("Failed to retrieve bundle symbolic name from file. ", e);
+            throw new TestValidationException("Failed to retrieve bundle symbolic name from file. ", e);
         }
     }
 
diff --git a/src/main/java/org/apache/sling/testing/clients/osgi/ComponentInfo.java b/src/main/java/org/apache/sling/testing/clients/osgi/ComponentInfo.java
index 32ed94c..4e7a3af 100644
--- a/src/main/java/org/apache/sling/testing/clients/osgi/ComponentInfo.java
+++ b/src/main/java/org/apache/sling/testing/clients/osgi/ComponentInfo.java
@@ -17,22 +17,22 @@
 
 package org.apache.sling.testing.clients.osgi;
 
-import org.apache.sling.testing.clients.ClientException;
+import org.apache.sling.testing.clients.exceptions.TestValidationException;
 import org.codehaus.jackson.JsonNode;
 
 public class ComponentInfo {
 
     private JsonNode component;
 
-    public ComponentInfo(JsonNode root) throws ClientException {
+    public ComponentInfo(JsonNode root) throws TestValidationException {
         if(root.get("id") != null) {
             if(root.get("id") == null) {
-                throw new ClientException("No Component Info returned");
+                throw new TestValidationException("No Component Info returned");
             }
             component = root;
         } else {
             if(root.get("data") == null && root.get("data").size() < 1) {
-                throw new ClientException("No Component Info returned");
+                throw new TestValidationException("No Component Info returned");
             }
             component = root.get("data").get(0);
         }
diff --git a/src/main/java/org/apache/sling/testing/clients/osgi/ComponentsInfo.java b/src/main/java/org/apache/sling/testing/clients/osgi/ComponentsInfo.java
index 49b7b14..91d0751 100644
--- a/src/main/java/org/apache/sling/testing/clients/osgi/ComponentsInfo.java
+++ b/src/main/java/org/apache/sling/testing/clients/osgi/ComponentsInfo.java
@@ -17,7 +17,7 @@
 
 package org.apache.sling.testing.clients.osgi;
 
-import org.apache.sling.testing.clients.ClientException;
+import org.apache.sling.testing.clients.exceptions.TestValidationException;
 import org.codehaus.jackson.JsonNode;
 
 import java.util.Iterator;
@@ -35,7 +35,7 @@ public class ComponentsInfo {
      * @param rootNode the root JSON node of the components info.
      * @throws ClientException if the info cannot be retrieved
      */
-    public ComponentsInfo(JsonNode rootNode) throws ClientException {
+    public ComponentsInfo(JsonNode rootNode) {
         this.root = rootNode;
     }
 
@@ -43,9 +43,9 @@ public class ComponentsInfo {
      * @return the number of installed components
      * @throws ClientException if the info cannot be retrieved
      */
-    public int getNumberOfInstalledComponents() throws ClientException {
+    public int getNumberOfInstalledComponents() throws TestValidationException {
         if(root.get("status") == null)
-            throw new ClientException("Number of installed Components not defined!");
+            throw new TestValidationException("Number of installed Components not defined!");
         return Integer.parseInt(root.get("status").getValueAsText());
     }
 
@@ -54,7 +54,7 @@ public class ComponentsInfo {
      * @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 {
+    public ComponentInfo forId(String id) throws TestValidationException {
         JsonNode component = findBy("id", id);
         return (component != null) ? new ComponentInfo(component) : null;
     }
@@ -64,7 +64,7 @@ public class ComponentsInfo {
      * @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 {
+    public ComponentInfo forName(String name) throws TestValidationException {
         JsonNode component = findBy("name", name);
         return (component != null) ? new ComponentInfo(component) : null;
     }
@@ -74,7 +74,7 @@ public class ComponentsInfo {
      * @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 {
+    public ComponentInfo forPid(String pid) throws TestValidationException {
         JsonNode component = findBy("pid", pid);
         return (component != null) ? new ComponentInfo(component) : null;
     }
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 842015b..f60d1d6 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
@@ -41,6 +41,8 @@ import org.apache.sling.testing.clients.ClientException;
 import org.apache.sling.testing.clients.SlingClient;
 import org.apache.sling.testing.clients.SlingClientConfig;
 import org.apache.sling.testing.clients.SlingHttpResponse;
+import org.apache.sling.testing.clients.exceptions.TestValidationException;
+import org.apache.sling.testing.clients.exceptions.TestingIOException;
 import org.apache.sling.testing.clients.util.FormEntityBuilder;
 import org.apache.sling.testing.clients.util.HttpUtils;
 import org.apache.sling.testing.clients.util.JsonUtils;
@@ -393,7 +395,7 @@ public class OsgiConsoleClient extends SlingClient {
         try {
             poller.poll(500L * waitCount, 500);
         } catch (TimeoutException e) {
-            throw new ClientException("Cannot retrieve configuration.", e);
+            throw new TestValidationException("Cannot retrieve configuration.", e);
         }
         return poller.getConfig();
     }
@@ -623,7 +625,7 @@ public class OsgiConsoleClient extends SlingClient {
         try {
             return this.checkBundleInstalled(OsgiConsoleClient.getBundleSymbolicName(f), waitTime, retries);
         } catch (IOException e) {
-            throw new ClientException("Cannot get bundle symbolic name", e);
+            throw new TestingIOException("Cannot get bundle symbolic name", e);
         }
     }
 
@@ -645,7 +647,7 @@ public class OsgiConsoleClient extends SlingClient {
         try {
             waitBundleInstalled(getBundleSymbolicName(f), timeout, delay);
         } catch (IOException e) {
-            throw new ClientException("Cannot get bundle symbolic name", e);
+            throw new TestingIOException("Cannot get bundle symbolic name", e);
         }
     }
 
@@ -721,7 +723,7 @@ public class OsgiConsoleClient extends SlingClient {
         final JsonNode idNode = bundle.get(JSON_KEY_ID);
 
         if (idNode == null) {
-            throw new ClientException("Cannot get id from bundle json");
+            throw new TestValidationException("Cannot get id from bundle json");
         }
 
         return idNode.getLongValue();
@@ -738,7 +740,7 @@ public class OsgiConsoleClient extends SlingClient {
         final JsonNode versionNode = bundle.get(JSON_KEY_VERSION);
 
         if (versionNode == null) {
-            throw new ClientException("Cannot get version from bundle json");
+            throw new TestValidationException("Cannot get version from bundle json");
         }
 
         return versionNode.getTextValue();
@@ -755,7 +757,7 @@ public class OsgiConsoleClient extends SlingClient {
         final JsonNode stateNode = bundle.get(JSON_KEY_STATE);
 
         if (stateNode == null) {
-            throw new ClientException("Cannot get state from bundle json");
+            throw new TestValidationException("Cannot get state from bundle json");
         }
 
         return stateNode.getTextValue();
@@ -868,17 +870,17 @@ public class OsgiConsoleClient extends SlingClient {
         final JsonNode root = JsonUtils.getJsonNodeFromString(content);
 
         if (root.get(JSON_KEY_DATA) == null) {
-            throw new ClientException(path + " does not provide '" + JSON_KEY_DATA + "' element, JSON content=" + content);
+            throw new TestValidationException(path + " does not provide '" + JSON_KEY_DATA + "' element, JSON content=" + content);
         }
 
         Iterator<JsonNode> data = root.get(JSON_KEY_DATA).getElements();
         if (!data.hasNext()) {
-            throw new ClientException(path + "." + JSON_KEY_DATA + " is empty, JSON content=" + content);
+            throw new TestValidationException(path + "." + JSON_KEY_DATA + " is empty, JSON content=" + content);
         }
 
         final JsonNode bundle = data.next();
         if (bundle.get(JSON_KEY_STATE) == null) {
-            throw new ClientException(path + ".data[0].state missing, JSON content=" + content);
+            throw new TestValidationException(path + ".data[0].state missing, JSON content=" + content);
         }
 
         return bundle;
diff --git a/src/main/java/org/apache/sling/testing/clients/osgi/ServiceInfo.java b/src/main/java/org/apache/sling/testing/clients/osgi/ServiceInfo.java
index 2d703d8..3515e7f 100644
--- a/src/main/java/org/apache/sling/testing/clients/osgi/ServiceInfo.java
+++ b/src/main/java/org/apache/sling/testing/clients/osgi/ServiceInfo.java
@@ -19,19 +19,19 @@ package org.apache.sling.testing.clients.osgi;
 
 import java.util.List;
 
-import org.apache.sling.testing.clients.ClientException;
+import org.apache.sling.testing.clients.exceptions.TestValidationException;
 import org.codehaus.jackson.JsonNode;
 
 public class ServiceInfo {
 
     private JsonNode service;
 
-    public ServiceInfo(JsonNode root) throws ClientException {
+    public ServiceInfo(JsonNode root) throws TestValidationException {
         if(root.get("id") != null) {
             service = root;
         } else {
             if(root.get("data") == null && root.get("data").size() < 1) {
-                throw new ClientException("No service info returned");
+                throw new TestValidationException("No service info returned");
             }
             service = root.get("data").get(0);
         }
diff --git a/src/main/java/org/apache/sling/testing/clients/osgi/ServicesInfo.java b/src/main/java/org/apache/sling/testing/clients/osgi/ServicesInfo.java
index 8ec3bbd..88b6fc6 100644
--- a/src/main/java/org/apache/sling/testing/clients/osgi/ServicesInfo.java
+++ b/src/main/java/org/apache/sling/testing/clients/osgi/ServicesInfo.java
@@ -17,7 +17,7 @@
 
 package org.apache.sling.testing.clients.osgi;
 
-import org.apache.sling.testing.clients.ClientException;
+import org.apache.sling.testing.clients.exceptions.TestValidationException;
 import org.codehaus.jackson.JsonNode;
 
 import java.util.Arrays;
@@ -40,13 +40,13 @@ public class ServicesInfo {
      * @param root the root JSON node of the bundles info.
      * @throws ClientException if the json does not contain the proper info
      */
-    public ServicesInfo(JsonNode root) throws ClientException {
+    public ServicesInfo(JsonNode root) throws TestValidationException {
         this.root = root;
         // some simple sanity checks
         if(root.get("status") == null)
-            throw new ClientException("No Status returned!");
+            throw new TestValidationException("No Status returned!");
         if(root.get("serviceCount") == null)
-            throw new ClientException("No serviceCount returned!");
+            throw new TestValidationException("No serviceCount returned!");
     }
 
     /**
@@ -63,7 +63,7 @@ public class ServicesInfo {
      * @return the BundleInfo
      * @throws ClientException if the info could not be retrieved
      */
-    public ServiceInfo forId(String id) throws ClientException {
+    public ServiceInfo forId(String id) throws TestValidationException {
         JsonNode serviceInfo = findBy("id", id);
         return (serviceInfo != null) ? new ServiceInfo(serviceInfo) : null;
     }
@@ -75,7 +75,7 @@ public class ServicesInfo {
      * @return a Collection of {@link ServiceInfo}s of all services with the given type. Might be empty, never {@code null}
      * @throws ClientException if the info cannot be retrieved
      */
-    public Collection<ServiceInfo> forType(String type) throws ClientException {
+    public Collection<ServiceInfo> forType(String type) throws TestValidationException {
         List<ServiceInfo> results = new LinkedList<>();
         List<JsonNode> serviceInfoNodes = findAllContainingValueInArray("types", type);
         for (JsonNode serviceInfoNode : serviceInfoNodes) {
diff --git a/src/main/java/org/apache/sling/testing/clients/package-info.java b/src/main/java/org/apache/sling/testing/clients/package-info.java
index 9e17199..8b3a108 100644
--- a/src/main/java/org/apache/sling/testing/clients/package-info.java
+++ b/src/main/java/org/apache/sling/testing/clients/package-info.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-@Version("2.3.0")
+@Version("2.4.0")
 package org.apache.sling.testing.clients;
 
 import org.osgi.annotation.versioning.Version;
diff --git a/src/main/java/org/apache/sling/testing/clients/query/QueryClient.java b/src/main/java/org/apache/sling/testing/clients/query/QueryClient.java
index bfad020..e16768f 100644
--- a/src/main/java/org/apache/sling/testing/clients/query/QueryClient.java
+++ b/src/main/java/org/apache/sling/testing/clients/query/QueryClient.java
@@ -22,6 +22,8 @@ import org.apache.sling.testing.clients.ClientException;
 import org.apache.sling.testing.clients.SlingClient;
 import org.apache.sling.testing.clients.SlingClientConfig;
 import org.apache.sling.testing.clients.SlingHttpResponse;
+import org.apache.sling.testing.clients.exceptions.TestValidationException;
+import org.apache.sling.testing.clients.exceptions.TestingIOException;
 import org.apache.sling.testing.clients.osgi.OsgiConsoleClient;
 import org.apache.sling.testing.clients.query.servlet.QueryServlet;
 import org.apache.sling.testing.clients.util.JsonUtils;
@@ -203,9 +205,9 @@ public class QueryClient extends SlingClient {
 
             LOG.info("query servlet installed at {}", getUrl(QueryServlet.SERVLET_PATH));
         } catch (IOException e) {
-            throw new ClientException("Failed to create the query servlet bundle", e);
+            throw new TestingIOException("Failed to create the query servlet bundle", e);
         } catch (TimeoutException e) {
-            throw new ClientException("The query servlet bundle did not successfully start", e);
+            throw new TestValidationException("The query servlet bundle did not successfully start", e);
         }
 
         return this;
diff --git a/src/main/java/org/apache/sling/testing/clients/util/HttpUtils.java b/src/main/java/org/apache/sling/testing/clients/util/HttpUtils.java
index ba17460..9d3ce30 100644
--- a/src/main/java/org/apache/sling/testing/clients/util/HttpUtils.java
+++ b/src/main/java/org/apache/sling/testing/clients/util/HttpUtils.java
@@ -20,7 +20,7 @@ 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 org.apache.sling.testing.clients.exceptions.TestValidationException;
 
 import java.net.URI;
 
@@ -35,7 +35,7 @@ public class HttpUtils {
      * @param expectedStatus List of acceptable HTTP Statuses
      * @throws ClientException if status is not expected
      */
-    public static void verifyHttpStatus(SlingHttpResponse response, int... expectedStatus) throws ClientException {
+    public static void verifyHttpStatus(SlingHttpResponse response, int... expectedStatus) throws TestValidationException {
         if (!checkStatus(response, expectedStatus)) {
             throwError(response, buildDefaultErrorMessage(response), expectedStatus);
         }
@@ -56,8 +56,7 @@ public class HttpUtils {
         }
     }
 
-    private static boolean checkStatus(HttpResponse response, int... expectedStatus)
-            throws ClientException {
+    private static boolean checkStatus(HttpResponse response, int... expectedStatus) {
 
         // if no HttpResponse was given
         if (response == null) {
@@ -83,7 +82,7 @@ public class HttpUtils {
     }
 
     private static boolean throwError(HttpResponse response, String errorMessage, int... expectedStatus)
-            throws ClientException {
+            throws TestValidationException {
         // build error message
         String errorMsg = "Expected HTTP Status: ";
         for (int expected : expectedStatus) {
@@ -99,7 +98,7 @@ public class HttpUtils {
         }
 
         // throw the exception
-        throw new ClientException(errorMsg, givenStatus);
+        throw new TestValidationException(errorMsg, givenStatus);
     }
 
 
@@ -133,7 +132,7 @@ public class HttpUtils {
      * @return The HTTP Status of the response
      * @throws ClientException never (kept for uniformity)
      */
-    public static int getHttpStatus(HttpResponse response) throws ClientException {
+    public static int getHttpStatus(HttpResponse response) {
         return response.getStatusLine().getStatusCode();
     }
 
@@ -144,8 +143,8 @@ public class HttpUtils {
      * @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!");
+    public static String getLocationHeader(HttpResponse response) throws TestValidationException {
+        if (response == null) throw new TestValidationException("Response must not be null!");
 
         String locationPath = null;
         Header locationHeader = response.getFirstHeader("Location");
@@ -156,7 +155,7 @@ public class HttpUtils {
         }
 
         if (locationPath == null) {
-            throw new ClientException("not able to determine location path");
+            throw new TestValidationException("not able to determine location path");
         }
         return locationPath;
     }
diff --git a/src/main/java/org/apache/sling/testing/clients/util/InputStreamBodyWithLength.java b/src/main/java/org/apache/sling/testing/clients/util/InputStreamBodyWithLength.java
index 36361ef..2865c26 100644
--- a/src/main/java/org/apache/sling/testing/clients/util/InputStreamBodyWithLength.java
+++ b/src/main/java/org/apache/sling/testing/clients/util/InputStreamBodyWithLength.java
@@ -18,7 +18,7 @@ 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 org.apache.sling.testing.clients.exceptions.TestingIOException;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -29,7 +29,7 @@ import java.io.InputStream;
 public class InputStreamBodyWithLength extends InputStreamBody {
     private long streamLength;
 
-    public InputStreamBodyWithLength(String resourcePath, String contentType, String fileName) throws ClientException {
+    public InputStreamBodyWithLength(String resourcePath, String contentType, String fileName) throws TestingIOException {
         super(ResourceUtil.getResourceAsStream(resourcePath), ContentType.create(contentType), fileName);
         this.streamLength = getResourceStreamLength(resourcePath);
     }
@@ -47,7 +47,7 @@ public class InputStreamBodyWithLength extends InputStreamBody {
      * @param resourcePath path to the file
      * @return the size of the resource
      */
-    private static long getResourceStreamLength(String resourcePath) throws ClientException {
+    private static long getResourceStreamLength(String resourcePath) throws TestingIOException {
         int streamLength = 0;
         InputStream stream = ResourceUtil.getResourceAsStream(resourcePath);
         try {
@@ -56,12 +56,12 @@ public class InputStreamBodyWithLength extends InputStreamBody {
                 stream.skip(avail);
             }
         } catch (IOException e) {
-            throw new ClientException("Could not read " + resourcePath + "!", e);
+            throw new TestingIOException("Could not read " + resourcePath + "!", e);
         } finally {
             try {
                 stream.close();
             } catch (IOException e) {
-                throw new ClientException("Could not close Inputstream for " + resourcePath + "!", e);
+                throw new TestingIOException("Could not close Inputstream for " + resourcePath + "!", e);
             }
         }
         return streamLength;
diff --git a/src/main/java/org/apache/sling/testing/clients/util/JsonUtils.java b/src/main/java/org/apache/sling/testing/clients/util/JsonUtils.java
index e64ac8d..8d4b1d5 100644
--- a/src/main/java/org/apache/sling/testing/clients/util/JsonUtils.java
+++ b/src/main/java/org/apache/sling/testing/clients/util/JsonUtils.java
@@ -17,6 +17,8 @@
 package org.apache.sling.testing.clients.util;
 
 import org.apache.sling.testing.clients.ClientException;
+import org.apache.sling.testing.clients.exceptions.TestValidationException;
+import org.apache.sling.testing.clients.exceptions.TestingIOException;
 import org.codehaus.jackson.JsonNode;
 import org.codehaus.jackson.JsonProcessingException;
 import org.codehaus.jackson.map.ObjectMapper;
@@ -36,9 +38,9 @@ public class JsonUtils {
             ObjectMapper mapper = new ObjectMapper();
             return mapper.readTree(jsonString);
         } catch (JsonProcessingException e) {
-            throw new ClientException("Could not read json file.", e);
+            throw new TestValidationException("Could not read json file.", e);
         } catch (IOException e) {
-            throw new ClientException("Could not read json node.", e);
+            throw new TestingIOException("Could not read json node.", e);
         }
     }
 }
\ No newline at end of file