camel-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From lburgazz...@apache.org
Subject [2/4] camel git commit: CAMEL-10795: PingCheck API - includes CAMEL-10923, CAMEL-10924
Date Fri, 10 Mar 2017 13:38:15 GMT
http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpComponentVerifier.java
----------------------------------------------------------------------
diff --git a/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpComponentVerifier.java b/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpComponentVerifier.java
new file mode 100644
index 0000000..aa459cc
--- /dev/null
+++ b/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpComponentVerifier.java
@@ -0,0 +1,211 @@
+/**
+ * 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.camel.component.http4;
+
+import java.net.UnknownHostException;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.camel.ComponentVerifier;
+import org.apache.camel.http.common.HttpHelper;
+import org.apache.camel.impl.verifier.DefaultComponentVerifier;
+import org.apache.camel.impl.verifier.ResultBuilder;
+import org.apache.camel.impl.verifier.ResultErrorBuilder;
+import org.apache.camel.impl.verifier.ResultErrorHelper;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+
+final class HttpComponentVerifier extends DefaultComponentVerifier {
+    private final HttpComponent component;
+
+    HttpComponentVerifier(HttpComponent component) {
+        super(component.getCamelContext());
+
+        this.component = component;
+    }
+
+    // *********************************
+    // Parameters validation
+    // *********************************
+
+    @Override
+    protected Result verifyParameters(Map<String, Object> parameters) {
+        // The default is success
+        ResultBuilder builder = ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.PARAMETERS);
+
+        // The httpUri is mandatory
+        builder.error(ResultErrorHelper.requiresOption("httpUri", parameters));
+
+        return builder.build();
+    }
+
+    // *********************************
+    // Connectivity validation
+    // *********************************
+
+    @Override
+    protected Result verifyConnectivity(Map<String, Object> parameters) {
+        // Default is success
+        ResultBuilder builder = ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.CONNECTIVITY);
+
+        Optional<String> uri = getOption(parameters, "httpUri", String.class);
+        if (!uri.isPresent()) {
+            // lack of httpUri is a blocking issue
+            builder.error(ResultErrorHelper.requiresOption("httpUri", parameters));
+        } else {
+            builder.error(parameters, this::verifyHttpConnectivity);
+        }
+
+        return builder.build();
+    }
+
+    private void verifyHttpConnectivity(ResultBuilder builder, Map<String, Object> parameters) throws Exception {
+        Optional<String> uri = getOption(parameters, "httpUri", String.class);
+
+        CloseableHttpClient httpclient = createHttpClient(parameters);
+        HttpUriRequest request = new HttpGet(uri.get());
+
+        try (CloseableHttpResponse response = httpclient.execute(request)) {
+            int code = response.getStatusLine().getStatusCode();
+            String okCodes = getOption(parameters, "okStatusCodeRange", String.class).orElse("200-299");
+
+            if (!HttpHelper.isStatusCodeOk(code, okCodes)) {
+                if (code == 401) {
+                    // Unauthorized, add authUsername and authPassword to the list
+                    // of parameters in error
+                    builder.error(
+                        ResultErrorBuilder.withHttpCode(code)
+                            .description(response.getStatusLine().getReasonPhrase())
+                            .parameter("authUsername")
+                            .parameter("authPassword")
+                            .build()
+                    );
+                } else if (code >= 300 && code < 400) {
+                    // redirect
+                    builder.error(
+                        ResultErrorBuilder.withHttpCode(code)
+                            .description(response.getStatusLine().getReasonPhrase())
+                            .parameter("httpUri")
+                            .attribute(ComponentVerifier.HTTP_REDIRECT, true)
+                            .attribute(ComponentVerifier.HTTP_REDIRECT_LOCATION, () -> HttpUtil.responseHeaderValue(response, "location"))
+                            .build()
+                    );
+                } else if (code >= 400) {
+                    // generic http error
+                    builder.error(
+                        ResultErrorBuilder.withHttpCode(code)
+                            .description(response.getStatusLine().getReasonPhrase())
+                            .build()
+                    );
+                }
+            }
+        } catch (UnknownHostException e) {
+            builder.error(
+                ResultErrorBuilder.withException(e)
+                    .parameter("httpUri")
+                    .build()
+            );
+        }
+    }
+
+    // *********************************
+    // Helpers
+    // *********************************
+
+    private Optional<HttpClientConfigurer> configureAuthentication(Map<String, Object> parameters) {
+        Optional<String> authUsername = getOption(parameters, "authUsername", String.class);
+        Optional<String> authPassword = getOption(parameters, "authPassword", String.class);
+
+        if (authUsername.isPresent() && authPassword.isPresent()) {
+            Optional<String> authDomain = getOption(parameters, "authDomain", String.class);
+            Optional<String> authHost = getOption(parameters, "authHost", String.class);
+
+            return Optional.of(
+                new BasicAuthenticationHttpClientConfigurer(
+                    authUsername.get(),
+                    authPassword.get(),
+                    authDomain.orElse(null),
+                    authHost.orElse(null)
+                )
+            );
+        }
+
+        return Optional.empty();
+    }
+
+    private Optional<HttpClientConfigurer> configureProxy(Map<String, Object> parameters) {
+        Optional<String> uri = getOption(parameters, "httpUri", String.class);
+        Optional<String> proxyAuthHost = getOption(parameters, "proxyAuthHost", String.class);
+        Optional<Integer> proxyAuthPort = getOption(parameters, "proxyAuthPort", Integer.class);
+
+        if (proxyAuthHost.isPresent() && proxyAuthPort.isPresent()) {
+            Optional<String> proxyAuthScheme = getOption(parameters, "proxyAuthScheme", String.class);
+            Optional<String> proxyAuthUsername = getOption(parameters, "proxyAuthUsername", String.class);
+            Optional<String> proxyAuthPassword = getOption(parameters, "proxyAuthPassword", String.class);
+            Optional<String> proxyAuthDomain = getOption(parameters, "proxyAuthDomain", String.class);
+            Optional<String> proxyAuthNtHost = getOption(parameters, "proxyAuthNtHost", String.class);
+
+            if (!proxyAuthScheme.isPresent()) {
+                proxyAuthScheme = Optional.of(HttpHelper.isSecureConnection(uri.get()) ? "https" : "http");
+            }
+
+            if (proxyAuthUsername != null && proxyAuthPassword != null) {
+                return Optional.of(
+                    new ProxyHttpClientConfigurer(
+                        proxyAuthHost.get(),
+                        proxyAuthPort.get(),
+                        proxyAuthScheme.get(),
+                        proxyAuthUsername.orElse(null),
+                        proxyAuthPassword.orElse(null),
+                        proxyAuthDomain.orElse(null),
+                        proxyAuthNtHost.orElse(null))
+                );
+            } else {
+                return Optional.of(
+                    new ProxyHttpClientConfigurer(
+                        proxyAuthHost.get(),
+                        proxyAuthPort.get(),
+                        proxyAuthScheme.get())
+                );
+            }
+        }
+
+        return Optional.empty();
+    }
+
+    private CloseableHttpClient createHttpClient(Map<String, Object> parameters) throws Exception {
+        CompositeHttpConfigurer configurer = new CompositeHttpConfigurer();
+        configureAuthentication(parameters).ifPresent(configurer::addConfigurer);
+        configureProxy(parameters).ifPresent(configurer::addConfigurer);
+
+        HttpClientBuilder builder = HttpClientBuilder.create();
+        configurer.configureHttpClient(builder);
+
+        RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
+
+        // Apply custom http client properties like httpClient.redirectsEnabled
+        setProperties(builder, "httpClient.", parameters);
+        setProperties(requestConfigBuilder, "httpClient.", parameters);
+
+        return builder.setDefaultRequestConfig(requestConfigBuilder.build())
+            .build();
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpUtil.java
----------------------------------------------------------------------
diff --git a/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpUtil.java b/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpUtil.java
new file mode 100644
index 0000000..8a6a8e4
--- /dev/null
+++ b/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpUtil.java
@@ -0,0 +1,36 @@
+/**
+ * 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.camel.component.http4;
+
+import java.util.Optional;
+
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+
+
+public final class HttpUtil {
+    private HttpUtil() {
+    }
+
+    public static Optional<Header> responseHeader(HttpResponse response, String headerName) {
+        return Optional.ofNullable(response.getFirstHeader(headerName));
+    }
+
+    public static Optional<String> responseHeaderValue(HttpResponse response, String headerName) {
+        return responseHeader(response, headerName).map(Header::getValue);
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-http4/src/main/java/org/apache/camel/component/http4/ProxyHttpClientConfigurer.java
----------------------------------------------------------------------
diff --git a/components/camel-http4/src/main/java/org/apache/camel/component/http4/ProxyHttpClientConfigurer.java b/components/camel-http4/src/main/java/org/apache/camel/component/http4/ProxyHttpClientConfigurer.java
index 35d9234..492c21a 100644
--- a/components/camel-http4/src/main/java/org/apache/camel/component/http4/ProxyHttpClientConfigurer.java
+++ b/components/camel-http4/src/main/java/org/apache/camel/component/http4/ProxyHttpClientConfigurer.java
@@ -66,4 +66,6 @@ public class ProxyHttpClientConfigurer implements HttpClientConfigurer {
             clientBuilder.setDefaultCredentialsProvider(credentialsProvider);
         }
     }
+
+
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-http4/src/test/java/org/apache/camel/component/http4/CamelComponentVerifierTest.java
----------------------------------------------------------------------
diff --git a/components/camel-http4/src/test/java/org/apache/camel/component/http4/CamelComponentVerifierTest.java b/components/camel-http4/src/test/java/org/apache/camel/component/http4/CamelComponentVerifierTest.java
new file mode 100644
index 0000000..effca79
--- /dev/null
+++ b/components/camel-http4/src/test/java/org/apache/camel/component/http4/CamelComponentVerifierTest.java
@@ -0,0 +1,230 @@
+/**
+ * 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.camel.component.http4;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.ComponentVerifier;
+import org.apache.camel.component.http4.handler.AuthenticationValidationHandler;
+import org.apache.camel.component.http4.handler.BasicValidationHandler;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.impl.bootstrap.HttpServer;
+import org.apache.http.impl.bootstrap.ServerBootstrap;
+import org.apache.http.localserver.RequestBasicAuth;
+import org.apache.http.localserver.ResponseBasicUnauthorized;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.apache.http.protocol.ImmutableHttpProcessor;
+import org.apache.http.protocol.ResponseContent;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CamelComponentVerifierTest extends BaseHttpTest {
+    private static final String AUTH_USERNAME = "camel";
+    private static final String AUTH_PASSWORD = "password";
+
+    private HttpServer localServer;
+    
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        localServer = ServerBootstrap.bootstrap()
+            .setHttpProcessor(getHttpProcessor())
+            .registerHandler("/basic", new BasicValidationHandler("GET", null, null, getExpectedContent()))
+            .registerHandler("/auth", new AuthenticationValidationHandler("GET", null, null, getExpectedContent(), AUTH_USERNAME, AUTH_PASSWORD))
+            .registerHandler("/redirect", redirectTo(HttpStatus.SC_MOVED_PERMANENTLY, "/redirected"))
+            .registerHandler("/redirected", new BasicValidationHandler("GET", null, null, getExpectedContent()))
+            .create();
+
+        localServer.start();
+
+        super.setUp();
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+
+        if (localServer != null) {
+            localServer.stop();
+        }
+    }
+
+    @Override
+    public boolean isUseRouteBuilder() {
+        return false;
+    }
+
+    private HttpProcessor getHttpProcessor() {
+        return new ImmutableHttpProcessor(
+            Arrays.asList(
+                new RequestBasicAuth()
+            ),
+            Arrays.asList(
+                new ResponseContent(),
+                new ResponseBasicUnauthorized())
+        );
+    }
+
+    // *************************************************
+    // Helpers
+    // *************************************************
+
+    protected String getLocalServerUri(String contextPath) {
+        return new StringBuilder()
+            .append("http://")
+            .append(localServer.getInetAddress().getHostName())
+            .append(":")
+            .append(localServer.getLocalPort())
+            .append(contextPath != null
+                ? contextPath.startsWith("/") ? contextPath : "/" + contextPath
+                : "")
+            .toString();
+    }
+
+    private HttpRequestHandler redirectTo(int code, String path) {
+        return new HttpRequestHandler() {
+            @Override
+            public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException {
+                response.setHeader("location", getLocalServerUri(path));
+                response.setStatusCode(code);
+            }
+        };
+    }
+
+    // *************************************************
+    // Tests
+    // *************************************************
+
+    @Test
+    public void testConnectivity() throws Exception {
+        HttpComponent component = context().getComponent("http4", HttpComponent.class);
+        HttpComponentVerifier verifier = (HttpComponentVerifier)component.getVerifier();
+
+        Map<String, Object> parameters = new HashMap<>();
+        parameters.put("httpUri", getLocalServerUri("/basic"));
+
+        ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.OK, result.getStatus());
+    }
+
+    @Test
+    public void testConnectivityWithWrongUri() throws Exception {
+        HttpComponent component = context().getComponent("http4", HttpComponent.class);
+        HttpComponentVerifier verifier = (HttpComponentVerifier)component.getVerifier();
+
+        Map<String, Object> parameters = new HashMap<>();
+        parameters.put("httpUri", "http://www.not-existing-uri.unknown");
+
+        ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+        Assert.assertEquals(1, result.getErrors().size());
+
+        ComponentVerifier.Error error = result.getErrors().get(0);
+
+        Assert.assertEquals(ComponentVerifier.CODE_EXCEPTION, error.getCode());
+        Assert.assertEquals(ComponentVerifier.ERROR_TYPE_EXCEPTION, error.getAttributes().get(ComponentVerifier.ERROR_TYPE_ATTRIBUTE));
+        Assert.assertTrue(error.getParameters().contains("httpUri"));
+    }
+
+    @Test
+    public void testConnectivityWithAuthentication() throws Exception {
+        HttpComponent component = context().getComponent("http4", HttpComponent.class);
+        HttpComponentVerifier verifier = (HttpComponentVerifier)component.getVerifier();
+
+        Map<String, Object> parameters = new HashMap<>();
+        parameters.put("httpUri", getLocalServerUri("/auth"));
+        parameters.put("authUsername", AUTH_USERNAME);
+        parameters.put("authPassword", AUTH_PASSWORD);
+
+        ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.OK, result.getStatus());
+    }
+
+    @Test
+    public void testConnectivityWithWrongAuthenticationData() throws Exception {
+        HttpComponent component = context().getComponent("http4", HttpComponent.class);
+        HttpComponentVerifier verifier = (HttpComponentVerifier)component.getVerifier();
+
+        Map<String, Object> parameters = new HashMap<>();
+        parameters.put("httpUri", getLocalServerUri("/auth"));
+        parameters.put("authUsername", "unknown");
+        parameters.put("authPassword", AUTH_PASSWORD);
+
+        ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+        Assert.assertEquals(1, result.getErrors().size());
+
+        ComponentVerifier.Error error = result.getErrors().get(0);
+
+        Assert.assertEquals("401", error.getCode());
+        Assert.assertEquals(ComponentVerifier.ERROR_TYPE_HTTP, error.getAttributes().get(ComponentVerifier.ERROR_TYPE_ATTRIBUTE));
+        Assert.assertEquals(401, error.getAttributes().get(ComponentVerifier.HTTP_CODE));
+        Assert.assertTrue(error.getParameters().contains("authUsername"));
+        Assert.assertTrue(error.getParameters().contains("authPassword"));
+    }
+
+    @Test
+    public void testConnectivityWithRedirect() throws Exception {
+        HttpComponent component = context().getComponent("http4", HttpComponent.class);
+        HttpComponentVerifier verifier = (HttpComponentVerifier)component.getVerifier();
+
+        Map<String, Object> parameters = new HashMap<>();
+        parameters.put("httpUri", getLocalServerUri("/redirect"));
+
+        ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.OK, result.getStatus());
+    }
+
+    @Test
+    public void testConnectivityWithRedirectDisabled() throws Exception {
+        HttpComponent component = context().getComponent("http4", HttpComponent.class);
+        HttpComponentVerifier verifier = (HttpComponentVerifier)component.getVerifier();
+
+        Map<String, Object> parameters = new HashMap<>();
+        parameters.put("httpUri", getLocalServerUri("/redirect"));
+        parameters.put("httpClient.redirectsEnabled", "false");
+
+        ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+        Assert.assertEquals(1, result.getErrors().size());
+
+        ComponentVerifier.Error error = result.getErrors().get(0);
+
+        Assert.assertEquals("301", error.getCode());
+        Assert.assertEquals(ComponentVerifier.ERROR_TYPE_HTTP, error.getAttributes().get(ComponentVerifier.ERROR_TYPE_ATTRIBUTE));
+        Assert.assertEquals(true, error.getAttributes().get("http.redirect"));
+        Assert.assertEquals(getLocalServerUri("/redirected"), error.getAttributes().get("http.redirect.location"));
+        Assert.assertTrue(error.getParameters().contains("httpUri"));
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpRedirectTest.java
----------------------------------------------------------------------
diff --git a/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpRedirectTest.java b/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpRedirectTest.java
index 6a40340..ed90a1c 100644
--- a/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpRedirectTest.java
+++ b/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpRedirectTest.java
@@ -50,7 +50,6 @@ public class HttpRedirectTest extends BaseHttpTest {
                 setResponseFactory(getHttpResponseFactory()).
                 setExpectationVerifier(getHttpExpectationVerifier()).
                 setSslContext(getSSLContext()).
-                registerHandler("/test", new RedirectHandler(HttpStatus.SC_MOVED_PERMANENTLY)).
                 registerHandler("/someplaceelse", new BasicValidationHandler("GET", null, null, "Bye World")).
                 registerHandler("/test", new RedirectHandler(HttpStatus.SC_MOVED_PERMANENTLY)).
                 create();

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponent.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponent.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponent.java
index e95668d..64fce53 100644
--- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponent.java
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponent.java
@@ -30,7 +30,9 @@ import java.util.regex.Pattern;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.ComponentConfiguration;
+import org.apache.camel.ComponentVerifier;
 import org.apache.camel.Endpoint;
+import org.apache.camel.VerifiableComponent;
 import org.apache.camel.component.salesforce.api.SalesforceException;
 import org.apache.camel.component.salesforce.api.dto.AbstractQueryRecordsBase;
 import org.apache.camel.component.salesforce.api.dto.AbstractSObjectBase;
@@ -61,13 +63,14 @@ import static org.apache.camel.component.salesforce.SalesforceLoginConfig.DEFAUL
 /**
  * Represents the component that manages {@link SalesforceEndpoint}.
  */
-public class SalesforceComponent extends UriEndpointComponent implements EndpointCompleter {
+@Metadata(label = "verifiers", enums = "PARAMETERS,CONNECTIVITY")
+public class SalesforceComponent extends UriEndpointComponent implements EndpointCompleter, VerifiableComponent {
 
     private static final Logger LOG = LoggerFactory.getLogger(SalesforceComponent.class);
 
-    private static final int CONNECTION_TIMEOUT = 60000;
-    private static final Pattern SOBJECT_NAME_PATTERN = Pattern.compile("^.*[\\?&]sObjectName=([^&,]+).*$");
-    private static final String APEX_CALL_PREFIX = OperationName.APEX_CALL.value() + "/";
+    static final int CONNECTION_TIMEOUT = 60000;
+    static final Pattern SOBJECT_NAME_PATTERN = Pattern.compile("^.*[\\?&]sObjectName=([^&,]+).*$");
+    static final String APEX_CALL_PREFIX = OperationName.APEX_CALL.value() + "/";
 
     @Metadata(label = "security")
     private SalesforceLoginConfig loginConfig;
@@ -702,4 +705,11 @@ public class SalesforceComponent extends UriEndpointComponent implements Endpoin
     public Map<String, Class<?>> getClassMap() {
         return classMap;
     }
+
+    /**
+     * TODO: document
+     */
+    public ComponentVerifier getVerifier() {
+        return new SalesforceComponentVerifier(getCamelContext());
+    }
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponentVerifier.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponentVerifier.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponentVerifier.java
new file mode 100644
index 0000000..11b331c
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceComponentVerifier.java
@@ -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.camel.component.salesforce;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.ComponentVerifier;
+import org.apache.camel.NoSuchOptionException;
+import org.apache.camel.component.salesforce.api.SalesforceException;
+import org.apache.camel.component.salesforce.api.dto.RestError;
+import org.apache.camel.component.salesforce.internal.SalesforceSession;
+import org.apache.camel.component.salesforce.internal.client.DefaultRestClient;
+import org.apache.camel.impl.verifier.DefaultComponentVerifier;
+import org.apache.camel.impl.verifier.ResultBuilder;
+import org.apache.camel.impl.verifier.ResultErrorBuilder;
+import org.apache.camel.impl.verifier.ResultErrorHelper;
+import org.apache.camel.util.jsse.SSLContextParameters;
+import org.eclipse.jetty.client.HttpProxy;
+import org.eclipse.jetty.client.Origin;
+import org.eclipse.jetty.client.Socks4Proxy;
+import org.eclipse.jetty.client.util.BasicAuthentication;
+import org.eclipse.jetty.client.util.DigestAuthentication;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+public class SalesforceComponentVerifier extends DefaultComponentVerifier {
+    SalesforceComponentVerifier(CamelContext camelContext) {
+        super(camelContext);
+    }
+
+    // *********************************
+    // Parameters validation
+    // *********************************
+
+    @Override
+    protected ComponentVerifier.Result verifyParameters(Map<String, Object> parameters) {
+        return ResultBuilder.withStatusAndScope(ComponentVerifier.Result.Status.OK, ComponentVerifier.Scope.PARAMETERS)
+            .error(ResultErrorHelper.requiresOption("clientId", parameters))
+            .error(ResultErrorHelper.requiresOption("clientSecret", parameters))
+            .error(ResultErrorHelper.requiresOption("userName", parameters))
+            .error(ResultErrorHelper.requiresOption("password", parameters))
+            .build();
+    }
+
+    // *********************************
+    // Connectivity validation
+    // *********************************
+
+    @Override
+    protected Result verifyConnectivity(Map<String, Object> parameters) {
+        // Default is success
+        ResultBuilder builder = ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.CONNECTIVITY);
+
+        try {
+            SalesforceEndpointConfig configuration = new SalesforceEndpointConfig();
+            setProperties(configuration, parameters);
+
+            SalesforceLoginConfig loginConfig = new SalesforceLoginConfig();
+            setProperties(loginConfig, parameters);
+
+            // Create a dummy SslContextFactory which is needed by SalesforceHttpClient
+            // or the underlying jetty client fails with a NPE
+            SSLContextParameters contextParameters = new SSLContextParameters();
+            SslContextFactory sslContextFactory = new SslContextFactory();
+            sslContextFactory.setSslContext(contextParameters.createSSLContext(getCamelContext()));
+
+            SalesforceHttpClient httpClient = new SalesforceHttpClient(sslContextFactory);
+            httpClient.setConnectTimeout(SalesforceComponent.CONNECTION_TIMEOUT);
+            configureHttpProxy(httpClient, parameters);
+
+            SalesforceSession session = new SalesforceSession(httpClient, httpClient.getTimeout(), loginConfig);
+            DefaultRestClient client = new DefaultRestClient(httpClient, configuration.getApiVersion(), configuration.getFormat(), session);
+
+            httpClient.setSession(session);
+            httpClient.start();
+
+            // For authentication check is is enough to use
+            session.start();
+
+            client.start();
+            client.getVersions((response, exception) -> processSalesforceException(builder, Optional.ofNullable(exception)));
+            client.stop();
+
+            session.stop();
+
+            httpClient.stop();
+            httpClient.destroy();
+        } catch(NoSuchOptionException e) {
+            builder.error(
+                ResultErrorBuilder.withMissingOption(e.getOptionName()).build()
+            );
+        } catch(SalesforceException e) {
+            processSalesforceException(builder, Optional.of(e));
+        } catch (Exception e) {
+            builder.error(
+                ResultErrorBuilder.withException(e).build()
+            );
+
+            throw new RuntimeException(e);
+        }
+
+        return builder.build();
+    }
+
+    // *********************************
+    // Helpers
+    // *********************************
+
+    private void processSalesforceException(ResultBuilder builder, Optional<SalesforceException> exception) {
+        exception.ifPresent(e -> {
+            builder.error(
+                ResultErrorBuilder.withException(e)
+                    .attribute(ComponentVerifier.HTTP_CODE, e.getStatusCode())
+                    .build()
+            );
+
+            for (RestError error : e.getErrors()) {
+                builder.error(
+                    ResultErrorBuilder.withCode(error.getErrorCode())
+                        .description(error.getMessage())
+                        .attribute(ComponentVerifier.ERROR_TYPE_ATTRIBUTE, "salesforce")
+                        .parameters(error.getFields())
+                        .build()
+                );
+            }
+        });
+    }
+
+    private void configureHttpProxy(SalesforceHttpClient httpClient, Map<String, Object> parameters) throws NoSuchOptionException, URISyntaxException {
+        Optional<String> httpProxyHost = getOption(parameters, "httpProxyHost", String.class);
+        Optional<Integer> httpProxyPort = getOption(parameters, "httpProxyPort", Integer.class);
+        Optional<String> httpProxyUsername = getOption(parameters, "httpProxyUsername", String.class);
+        Optional<String> httpProxyPassword = getOption(parameters, "httpProxyPassword", String.class);
+
+        if (httpProxyHost.isPresent() && httpProxyPort.isPresent()) {
+            Origin.Address address = new Origin.Address(httpProxyHost.get(), httpProxyPort.get());
+            Boolean isHttpProxySocks4 = getOption(parameters, "isHttpProxySocks4", Boolean.class, () -> false);
+            Boolean isHttpProxySecure = getOption(parameters, "isHttpProxySecure", Boolean.class, () -> true);
+
+            if (isHttpProxySocks4) {
+                httpClient.getProxyConfiguration().getProxies().add(
+                    new Socks4Proxy(address, isHttpProxySecure)
+                );
+            } else {
+                httpClient.getProxyConfiguration().getProxies().add(
+                    new HttpProxy(address, isHttpProxySecure)
+                );
+            }
+        }
+
+        if (httpProxyUsername.isPresent() && httpProxyPassword.isPresent()) {
+            Boolean httpProxyUseDigestAuth = getOption(parameters, "httpProxyUseDigestAuth", Boolean.class, () -> false);
+            String httpProxyAuthUri = getMandatoryOption(parameters, "httpProxyAuthUri", String.class);
+            String httpProxyRealm = getMandatoryOption(parameters, "httpProxyRealm", String.class);
+
+            if (httpProxyUseDigestAuth) {
+                httpClient.getAuthenticationStore().addAuthentication(new DigestAuthentication(
+                    new URI(httpProxyAuthUri),
+                    httpProxyRealm,
+                    httpProxyUsername.get(),
+                    httpProxyPassword.get())
+                );
+            } else {
+                httpClient.getAuthenticationStore().addAuthentication(new BasicAuthentication(
+                    new URI(httpProxyAuthUri),
+                    httpProxyRealm,
+                    httpProxyUsername.get(),
+                    httpProxyPassword.get())
+                );
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceComponentVerifierTest.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceComponentVerifierTest.java b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceComponentVerifierTest.java
new file mode 100644
index 0000000..542139e
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceComponentVerifierTest.java
@@ -0,0 +1,143 @@
+/**
+ * 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.camel.component.salesforce;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.ComponentVerifier;
+import org.apache.camel.component.salesforce.api.SalesforceException;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.apache.camel.util.ObjectHelper;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+
+public class SalesforceComponentVerifierTest extends CamelTestSupport {
+    private static final String clientId = getSystemPropertyOrEnvVar("salesforce.clientid");
+    private static final String clientSecret = getSystemPropertyOrEnvVar("salesforce.clientsecret");
+    private static final String userName = getSystemPropertyOrEnvVar("salesforce.userName");
+    private static final String password = getSystemPropertyOrEnvVar("salesforce.password");
+
+    @Override
+    protected void doPreSetup() throws Exception {
+        Assume.assumeNotNull(clientId);
+        Assume.assumeNotNull(clientSecret);
+        Assume.assumeNotNull(userName);
+        Assume.assumeNotNull(password);
+    }
+
+    @Override
+    public boolean isUseRouteBuilder() {
+        return false;
+    }
+
+    // *********************************
+    // Helpers
+    // *********************************
+
+    protected Map<String, Object> getParameters() {
+        HashMap<String, Object> parameters = new HashMap<>();
+        parameters.put("clientId", clientId);
+        parameters.put("clientSecret", clientSecret);
+        parameters.put("userName", userName);
+        parameters.put("password", password);
+
+        return parameters;
+    }
+
+    public static String getSystemPropertyOrEnvVar(String systemProperty) {
+        String answer = System.getProperty(systemProperty);
+        if (ObjectHelper.isEmpty(answer)) {
+            String envProperty = systemProperty.toUpperCase().replaceAll("[.-]", "_");
+            answer = System.getenv(envProperty);
+        }
+
+        return answer;
+    }
+
+    protected SalesforceComponentVerifier getVerifier() {
+        SalesforceComponent component = context().getComponent("salesforce", SalesforceComponent.class);
+        SalesforceComponentVerifier verifier = (SalesforceComponentVerifier)component.getVerifier();
+
+        return verifier;
+    }
+
+    // *********************************
+    // Parameters validation
+    // *********************************
+
+
+    // *********************************
+    // Connectivity validation
+    // *********************************
+
+    @Test
+    public void testConnectivity() {
+        Map<String, Object> parameters = getParameters();
+        ComponentVerifier.Result result = getVerifier().verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.OK, result.getStatus());
+    }
+
+    @Test
+    public void testConnectivityWithWrongUserName() {
+        Map<String, Object> parameters = getParameters();
+        parameters.put("userName", "not-a-salesforce-user");
+
+        ComponentVerifier.Result result = getVerifier().verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+        Assert.assertEquals(2, result.getErrors().size());
+
+        // Exception
+        Assert.assertEquals(ComponentVerifier.CODE_EXCEPTION, result.getErrors().get(0).getCode());
+        Assert.assertNotNull(result.getErrors().get(0).getAttributes().get(ComponentVerifier.EXCEPTION_INSTANCE));
+        Assert.assertTrue(result.getErrors().get(0).getAttributes().get(ComponentVerifier.EXCEPTION_INSTANCE) instanceof SalesforceException);
+        Assert.assertEquals(400, result.getErrors().get(0).getAttributes().get(ComponentVerifier.HTTP_CODE));
+
+        // Salesforce Error
+        Assert.assertEquals("salesforce", result.getErrors().get(1).getAttributes().get(ComponentVerifier.ERROR_TYPE_ATTRIBUTE));
+        Assert.assertEquals("authentication failure", result.getErrors().get(1).getDescription());
+    }
+
+    @Test
+    public void testConnectivityWithWrongSecrets() {
+        Map<String, Object> parameters = getParameters();
+        parameters.put("clientId", "wrong-client-id");
+        parameters.put("clientSecret", "wrong-client-secret");
+
+        ComponentVerifier.Result result = getVerifier().verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+        Assert.assertEquals(2, result.getErrors().size());
+
+        // Exception
+        Assert.assertEquals(ComponentVerifier.CODE_EXCEPTION, result.getErrors().get(0).getCode());
+        Assert.assertNotNull(result.getErrors().get(0).getAttributes().get(ComponentVerifier.EXCEPTION_INSTANCE));
+        Assert.assertTrue(result.getErrors().get(0).getAttributes().get(ComponentVerifier.EXCEPTION_INSTANCE) instanceof SalesforceException);
+        Assert.assertEquals(400, result.getErrors().get(0).getAttributes().get(ComponentVerifier.HTTP_CODE));
+
+        // Salesforce Error
+        Assert.assertEquals("salesforce", result.getErrors().get(1).getAttributes().get(ComponentVerifier.ERROR_TYPE_ATTRIBUTE));
+        Assert.assertEquals("client identifier invalid", result.getErrors().get(1).getDescription());
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowClient.java
----------------------------------------------------------------------
diff --git a/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowClient.java b/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowClient.java
index fa33607..465a692 100644
--- a/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowClient.java
+++ b/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowClient.java
@@ -24,7 +24,6 @@ import javax.ws.rs.core.Response;
 
 import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
 import org.apache.camel.CamelContext;
-import org.apache.camel.CamelException;
 import org.apache.camel.Message;
 import org.apache.camel.component.servicenow.auth.AuthenticationRequestFilter;
 import org.apache.camel.util.jsse.SSLContextParameters;
@@ -44,8 +43,7 @@ public final class ServiceNowClient {
             configuration.getApiUrl(),
             Arrays.asList(
                 new AuthenticationRequestFilter(configuration),
-                new JacksonJsonProvider(configuration.getMapper()),
-                new ServiceNowExceptionMapper(configuration.getMapper())
+                new JacksonJsonProvider(configuration.getMapper())
             ),
             true
         );
@@ -131,10 +129,17 @@ public final class ServiceNowClient {
         case 405:
         case 406:
         case 415:
-            throw response.readEntity(ServiceNowException.class);
+            ServiceNowExceptionModel model = response.readEntity(ServiceNowExceptionModel.class);
+            throw new ServiceNowException(
+                code,
+                model.getStatus(),
+                model.getError().get("message"),
+                model.getError().get("detail")
+            );
         default:
-            throw new CamelException(
-                String.format("Status (%d): %s", code, response.readEntity(Map.class))
+            throw new ServiceNowException(
+                code,
+                response.readEntity(Map.class)
             );
         }
 

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowComponent.java
----------------------------------------------------------------------
diff --git a/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowComponent.java b/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowComponent.java
index ab3f7e0..63c5996 100644
--- a/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowComponent.java
+++ b/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowComponent.java
@@ -19,7 +19,9 @@ package org.apache.camel.component.servicenow;
 import java.util.Map;
 
 import org.apache.camel.CamelContext;
+import org.apache.camel.ComponentVerifier;
 import org.apache.camel.Endpoint;
+import org.apache.camel.VerifiableComponent;
 import org.apache.camel.impl.UriEndpointComponent;
 import org.apache.camel.spi.Metadata;
 import org.apache.camel.util.EndpointHelper;
@@ -28,7 +30,8 @@ import org.apache.camel.util.IntrospectionSupport;
 /**
  * Represents the component that manages {@link ServiceNowEndpoint}.
  */
-public class ServiceNowComponent extends UriEndpointComponent {
+@Metadata(label = "verifiers", enums = "PARAMETERS,CONNECTIVITY")
+public class ServiceNowComponent extends UriEndpointComponent implements VerifiableComponent {
 
     @Metadata(label = "advanced")
     private ServiceNowConfiguration configuration;
@@ -71,7 +74,7 @@ public class ServiceNowComponent extends UriEndpointComponent {
         if (!configuration.hasApiUrl()) {
             configuration.setApiUrl(String.format("https://%s.service-now.com/api", instanceName));
         }
-        if (!configuration.hasOautTokenUrl()) {
+        if (!configuration.hasOauthTokenUrl()) {
             configuration.setOauthTokenUrl(String.format("https://%s.service-now.com/oauth_token.do", instanceName));
         }
 
@@ -159,4 +162,11 @@ public class ServiceNowComponent extends UriEndpointComponent {
     public void setOauthTokenUrl(String oauthTokenUrl) {
         configuration.setOauthTokenUrl(oauthTokenUrl);
     }
+
+    /**
+     * TODO: document
+     */
+    public ComponentVerifier getVerifier() {
+        return new ServiceNowComponentVerifier(this);
+    }
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowComponentVerifier.java
----------------------------------------------------------------------
diff --git a/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowComponentVerifier.java b/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowComponentVerifier.java
new file mode 100644
index 0000000..f106632
--- /dev/null
+++ b/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowComponentVerifier.java
@@ -0,0 +1,113 @@
+/**
+ * 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.camel.component.servicenow;
+
+import java.util.Map;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.camel.ComponentVerifier;
+import org.apache.camel.NoSuchOptionException;
+import org.apache.camel.impl.verifier.DefaultComponentVerifier;
+import org.apache.camel.impl.verifier.ResultBuilder;
+import org.apache.camel.impl.verifier.ResultErrorBuilder;
+import org.apache.camel.impl.verifier.ResultErrorHelper;
+
+public class ServiceNowComponentVerifier extends DefaultComponentVerifier {
+    private final ServiceNowComponent component;
+
+    ServiceNowComponentVerifier(ServiceNowComponent component) {
+        super(component.getCamelContext());
+
+        this.component = component;
+    }
+
+    // *********************************
+    // Parameters validation
+    // *********************************
+
+    @Override
+    protected Result verifyParameters(Map<String, Object> parameters) {
+        return ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.PARAMETERS)
+            .error(ResultErrorHelper.requiresOption("instanceName", parameters))
+            .error(ResultErrorHelper.requiresOption("userName", parameters))
+            .error(ResultErrorHelper.requiresOption("password", parameters))
+            .build();
+    }
+
+    // *********************************
+    // Connectivity validation
+    // *********************************
+
+    @Override
+    protected Result verifyConnectivity(Map<String, Object> parameters) {
+        // Default is success
+        ResultBuilder builder = ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.CONNECTIVITY);
+
+        try {
+            // Load ServiceNow Configuration
+            ServiceNowConfiguration configuration = new ServiceNowConfiguration();
+            setProperties(configuration, parameters);
+
+            String instanceName = getMandatoryOption(parameters, "instanceName", String.class);
+            String tableName = configuration.getTable() != null ? configuration.getTable() : "incident";
+
+            // Configure Api and OAuthToken ULRs using instanceName
+            if (!configuration.hasApiUrl()) {
+                configuration.setApiUrl(String.format("https://%s.service-now.com/api", instanceName));
+            }
+            if (!configuration.hasOauthTokenUrl()) {
+                configuration.setOauthTokenUrl(String.format("https://%s.service-now.com/oauth_token.do", instanceName));
+            }
+
+            new ServiceNowClient(getCamelContext(), configuration)
+                .types(MediaType.APPLICATION_JSON_TYPE)
+                .path("now")
+                .path(configuration.getApiVersion())
+                .path("table")
+                .path(tableName)
+                .query(ServiceNowParams.SYSPARM_LIMIT.getId(), 1L)
+                .invoke(HttpMethod.GET);
+        } catch(NoSuchOptionException e) {
+            builder.error(
+                ResultErrorBuilder.withMissingOption(e.getOptionName()).build()
+            );
+        } catch (ServiceNowException e) {
+            ResultErrorBuilder errorBuilder = ResultErrorBuilder.withException(e)
+                .attribute(ComponentVerifier.HTTP_CODE, e.getCode())
+                .attribute("servicenow.error.message", e.getMessage())
+                .attribute("servicenow.error.status", e.getStatus())
+                .attribute("servicenow.error.detail", e.getDetail())
+                .attribute("servicenow.error.detail", e.getDetail());
+
+            if (e.getCode() == 401) {
+                errorBuilder.parameter("userName");
+                errorBuilder.parameter("password");
+                errorBuilder.parameter("oauthClientId");
+                errorBuilder.parameter("oauthClientSecret");
+            }
+
+            builder.error(errorBuilder.build());
+        } catch (Exception e) {
+            builder.error(
+                ResultErrorBuilder.withException(e).build()
+            );
+        }
+
+        return builder.build();
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowConfiguration.java
----------------------------------------------------------------------
diff --git a/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowConfiguration.java b/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowConfiguration.java
index dd23df9..d27d29e 100644
--- a/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowConfiguration.java
+++ b/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowConfiguration.java
@@ -194,7 +194,7 @@ public class ServiceNowConfiguration implements Cloneable {
         return oauthTokenUrl;
     }
 
-    public boolean hasOautTokenUrl() {
+    public boolean hasOauthTokenUrl() {
         return oauthTokenUrl != null;
     }
 

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowException.java
----------------------------------------------------------------------
diff --git a/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowException.java b/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowException.java
index cb1ce8b..42ffbac 100644
--- a/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowException.java
+++ b/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowException.java
@@ -16,30 +16,35 @@
  */
 package org.apache.camel.component.servicenow;
 
+import java.util.Collections;
 import java.util.Map;
 
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
 import org.apache.camel.CamelException;
 
-@JsonIgnoreProperties(ignoreUnknown = true)
 public class ServiceNowException extends CamelException {
+    private final Integer code;
     private final String status;
     private final String detail;
+    private final Map<Object, Object> attributes;
 
-    public ServiceNowException(
-        @JsonProperty("status") String status,
-        @JsonProperty("error") Map<String, String> error) {
-        super(error.get("message"));
+    public ServiceNowException(Integer code, String status, String message, String detail) {
+        super(message);
+        this.code = code;
         this.status = status;
-        this.detail = error.get("detail");
+        this.detail = detail;
+        this.attributes = Collections.emptyMap();
     }
 
-    public ServiceNowException(Throwable cause) {
-        super(cause);
-
+    public ServiceNowException(Integer code, Map<Object, Object> attributes) {
+        super(String.format("Status (%d)"));
+        this.code = code;
         this.status = null;
         this.detail = null;
+        this.attributes = Collections.unmodifiableMap(attributes);
+    }
+
+    public Integer getCode() {
+        return code;
     }
 
     public String getStatus() {
@@ -50,10 +55,15 @@ public class ServiceNowException extends CamelException {
         return detail;
     }
 
+    public Map<Object, Object> getAttributes() {
+        return attributes;
+    }
+
     @Override
     public String toString() {
         return getMessage() != null
             ? "" + this.status + ": " + getMessage()
             : super.toString();
     }
+
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowExceptionMapper.java
----------------------------------------------------------------------
diff --git a/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowExceptionMapper.java b/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowExceptionMapper.java
deleted file mode 100644
index d864246..0000000
--- a/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowExceptionMapper.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.component.servicenow;
-
-import java.io.IOException;
-import java.io.InputStream;
-import javax.annotation.Priority;
-import javax.ws.rs.Priorities;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.ext.Provider;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.cxf.jaxrs.client.ResponseExceptionMapper;
-
-@Provider
-@Priority(Priorities.USER)
-public class ServiceNowExceptionMapper implements ResponseExceptionMapper<ServiceNowException> {
-    private final ObjectMapper mapper;
-
-    public ServiceNowExceptionMapper(ObjectMapper mapper) {
-        this.mapper = mapper;
-    }
-
-    @Override
-    public ServiceNowException fromResponse(Response r) {
-        int code = r.getStatus();
-
-        try {
-            // Only ServiceNow known error status codes are mapped
-            // See http://wiki.servicenow.com/index.php?title=REST_API#REST_Response_HTTP_Status_Codes
-            switch(code) {
-            case 200:
-            case 201:
-            case 204:
-                // Success
-                break;
-            case 400:
-            case 401:
-            case 403:
-            case 404:
-            case 405:
-            case 406:
-            case 415:
-                return mapper.readValue(r.readEntity(InputStream.class), ServiceNowException.class);
-            default:
-                break;
-            }
-        } catch (IOException e) {
-            return new ServiceNowException(e);
-        }
-
-        return null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowExceptionModel.java
----------------------------------------------------------------------
diff --git a/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowExceptionModel.java b/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowExceptionModel.java
new file mode 100644
index 0000000..63c5519
--- /dev/null
+++ b/components/camel-servicenow/src/main/java/org/apache/camel/component/servicenow/ServiceNowExceptionModel.java
@@ -0,0 +1,27 @@
+package org.apache.camel.component.servicenow;
+
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public final class ServiceNowExceptionModel {
+    private final String status;
+    private final Map<String, String> error;
+
+    public ServiceNowExceptionModel(
+        @JsonProperty("status") String status,
+        @JsonProperty("error") Map<String, String> error) {
+        this.status = status;
+        this.error = error;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public Map<String, String> getError() {
+        return error;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-servicenow/src/test/java/org/apache/camel/component/servicenow/ServiceNowComponentVerifierTest.java
----------------------------------------------------------------------
diff --git a/components/camel-servicenow/src/test/java/org/apache/camel/component/servicenow/ServiceNowComponentVerifierTest.java b/components/camel-servicenow/src/test/java/org/apache/camel/component/servicenow/ServiceNowComponentVerifierTest.java
new file mode 100644
index 0000000..3cff7bb
--- /dev/null
+++ b/components/camel-servicenow/src/test/java/org/apache/camel/component/servicenow/ServiceNowComponentVerifierTest.java
@@ -0,0 +1,151 @@
+/**
+ * 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.camel.component.servicenow;
+
+import java.util.Map;
+
+import javax.ws.rs.ProcessingException;
+
+import org.apache.camel.ComponentVerifier;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ServiceNowComponentVerifierTest extends ServiceNowTestSupport {
+    public ServiceNowComponentVerifierTest() {
+        super(false);
+    }
+
+    @Override
+    public boolean isUseRouteBuilder() {
+        return false;
+    }
+
+    protected ServiceNowComponentVerifier getVerifier() {
+        ServiceNowComponent component = context().getComponent("servicenow", ServiceNowComponent.class);
+        ServiceNowComponentVerifier verifier = (ServiceNowComponentVerifier)component.getVerifier();
+
+        return verifier;
+    }
+
+    // *********************************
+    // Parameters validation
+    // *********************************
+
+    @Test
+    public void testParameter() {
+        Map<String, Object> parameters = getParameters();
+        ComponentVerifier.Result result = getVerifier().verify(ComponentVerifier.Scope.PARAMETERS, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.OK, result.getStatus());
+    }
+
+    @Test
+    public void testMissingMandatoryParameter() {
+        Map<String, Object> parameters = getParameters();
+        parameters.remove("instanceName");
+        ComponentVerifier.Result result = getVerifier().verify(ComponentVerifier.Scope.PARAMETERS, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+        Assert.assertEquals(1, result.getErrors().size());
+        Assert.assertEquals(ComponentVerifier.CODE_MISSING_OPTION, result.getErrors().get(0).getCode());
+        Assert.assertEquals("instanceName", result.getErrors().get(0).getParameters().iterator().next());
+    }
+
+    @Test
+    public void testMissingMandatoryAuthenticationParameter() {
+        Map<String, Object> parameters = getParameters();
+        parameters.remove("userName");
+        ComponentVerifier.Result result = getVerifier().verify(ComponentVerifier.Scope.PARAMETERS, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+        Assert.assertEquals(1, result.getErrors().size());
+        Assert.assertEquals(ComponentVerifier.CODE_MISSING_OPTION, result.getErrors().get(0).getCode());
+        Assert.assertEquals("userName", result.getErrors().get(0).getParameters().iterator().next());
+    }
+
+    // *********************************
+    // Connectivity validation
+    // *********************************
+
+    @Test
+    public void testConnectivity() {
+        Map<String, Object> parameters = getParameters();
+        ComponentVerifier.Result result = getVerifier().verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.OK, result.getStatus());
+    }
+
+    @Test
+    public void testConnectivityOnCustomTable() {
+        Map<String, Object> parameters = getParameters();
+        parameters.put("table", "ticket");
+
+        ComponentVerifier.Result result = getVerifier().verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.OK, result.getStatus());
+    }
+
+    @Test
+    public void testConnectivityWithWrongInstance() {
+        Map<String, Object> parameters = getParameters();
+        parameters.put("instanceName", "unknown-instance");
+
+        ComponentVerifier.Result result = getVerifier().verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+        Assert.assertEquals(1, result.getErrors().size());
+        Assert.assertEquals(ComponentVerifier.CODE_EXCEPTION, result.getErrors().get(0).getCode());
+        Assert.assertNotNull(result.getErrors().get(0).getAttributes().get(ComponentVerifier.EXCEPTION_INSTANCE));
+        Assert.assertTrue(result.getErrors().get(0).getAttributes().get(ComponentVerifier.EXCEPTION_INSTANCE) instanceof ProcessingException);
+    }
+
+    @Test
+    public void testConnectivityWithWrongTable() {
+        Map<String, Object> parameters = getParameters();
+        parameters.put("table", "unknown");
+
+        ComponentVerifier.Result result = getVerifier().verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+        Assert.assertEquals(1, result.getErrors().size());
+        Assert.assertEquals(ComponentVerifier.CODE_EXCEPTION, result.getErrors().get(0).getCode());
+        Assert.assertNotNull(result.getErrors().get(0).getAttributes().get(ComponentVerifier.EXCEPTION_INSTANCE));
+        Assert.assertEquals(400, result.getErrors().get(0).getAttributes().get(ComponentVerifier.HTTP_CODE));
+        Assert.assertTrue(result.getErrors().get(0).getAttributes().get(ComponentVerifier.EXCEPTION_INSTANCE) instanceof ServiceNowException);
+    }
+
+    @Test
+    public void testConnectivityWithWrongAuthentication() {
+        Map<String, Object> parameters = getParameters();
+        parameters.put("userName", "unknown-user");
+        parameters.remove("oauthClientId");
+        parameters.remove("oauthClientSecret");
+
+        ComponentVerifier.Result result = getVerifier().verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+        Assert.assertEquals(1, result.getErrors().size());
+        Assert.assertEquals(ComponentVerifier.CODE_EXCEPTION, result.getErrors().get(0).getCode());
+        Assert.assertNotNull(result.getErrors().get(0).getAttributes().get(ComponentVerifier.EXCEPTION_INSTANCE));
+        Assert.assertEquals(401, result.getErrors().get(0).getAttributes().get(ComponentVerifier.HTTP_CODE));
+        Assert.assertTrue(result.getErrors().get(0).getAttributes().get(ComponentVerifier.EXCEPTION_INSTANCE) instanceof ServiceNowException);
+        Assert.assertTrue(result.getErrors().get(0).getParameters().contains("userName"));
+        Assert.assertTrue(result.getErrors().get(0).getParameters().contains("password"));
+        Assert.assertTrue(result.getErrors().get(0).getParameters().contains("oauthClientId"));
+        Assert.assertTrue(result.getErrors().get(0).getParameters().contains("oauthClientSecret"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-servicenow/src/test/java/org/apache/camel/component/servicenow/ServiceNowTestSupport.java
----------------------------------------------------------------------
diff --git a/components/camel-servicenow/src/test/java/org/apache/camel/component/servicenow/ServiceNowTestSupport.java b/components/camel-servicenow/src/test/java/org/apache/camel/component/servicenow/ServiceNowTestSupport.java
index 8ed29b2..d9cdf49 100644
--- a/components/camel-servicenow/src/test/java/org/apache/camel/component/servicenow/ServiceNowTestSupport.java
+++ b/components/camel-servicenow/src/test/java/org/apache/camel/component/servicenow/ServiceNowTestSupport.java
@@ -29,12 +29,38 @@ import org.slf4j.LoggerFactory;
 class ServiceNowTestSupport extends CamelTestSupport {
     protected static final Logger LOGGER = LoggerFactory.getLogger(ServiceNowTestSupport.class);
 
+    private final boolean setUpComponent;
+
+    public ServiceNowTestSupport() {
+        this(true);
+    }
+
+    public ServiceNowTestSupport(boolean setUpComponent) {
+        this.setUpComponent = setUpComponent;
+    }
+
     @Override
     protected CamelContext createCamelContext() throws Exception {
-        return configureServicenowComponent(super.createCamelContext());
+        CamelContext context = super.createCamelContext();
+        if (setUpComponent) {
+            configureServicenowComponent(context);
+        }
+
+        return context;
     }
 
-    protected CamelContext configureServicenowComponent(CamelContext camelContext) throws Exception {
+    protected Map<String, Object> getParameters() {
+        HashMap<String, Object> parameters = new HashMap<>();
+        parameters.put("instanceName", getSystemPropertyOrEnvVar("servicenow.instance"));
+        parameters.put("userName", getSystemPropertyOrEnvVar("servicenow.username"));
+        parameters.put("password", getSystemPropertyOrEnvVar("servicenow.password"));
+        parameters.put("oauthClientId", getSystemPropertyOrEnvVar("servicenow.oauth2.client.id"));
+        parameters.put("oauthClientSecret", getSystemPropertyOrEnvVar("servicenow.oauth2.client.secret"));
+
+        return parameters;
+    }
+
+    public void configureServicenowComponent(CamelContext camelContext) throws Exception {
         String userName = getSystemPropertyOrEnvVar("servicenow.username");
         String password = getSystemPropertyOrEnvVar("servicenow.password");
         String oauthClientId = getSystemPropertyOrEnvVar("servicenow.oauth2.client.id");
@@ -52,11 +78,9 @@ class ServiceNowTestSupport extends CamelTestSupport {
 
             camelContext.addComponent("servicenow", component);
         }
-
-        return camelContext;
     }
 
-    protected String getSystemPropertyOrEnvVar(String systemProperty) {
+    public static String getSystemPropertyOrEnvVar(String systemProperty) {
         String answer = System.getProperty(systemProperty);
         if (ObjectHelper.isEmpty(answer)) {
             String envProperty = systemProperty.toUpperCase().replaceAll("[.-]", "_");

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterComponent.java
----------------------------------------------------------------------
diff --git a/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterComponent.java b/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterComponent.java
index adc4f28..9e33c2e 100644
--- a/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterComponent.java
+++ b/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterComponent.java
@@ -18,14 +18,17 @@ package org.apache.camel.component.twitter;
 
 import java.util.Map;
 
+import org.apache.camel.ComponentVerifier;
 import org.apache.camel.Endpoint;
+import org.apache.camel.VerifiableComponent;
 import org.apache.camel.impl.UriEndpointComponent;
 import org.apache.camel.spi.Metadata;
 
 /**
  * Twitter component
  */
-public class TwitterComponent extends UriEndpointComponent {
+@Metadata(label = "verifiers", enums = "PARAMETERS,CONNECTIVITY")
+public class TwitterComponent extends UriEndpointComponent implements VerifiableComponent {
 
     @Metadata(label = "security", secret = true)
     private String consumerKey;
@@ -171,4 +174,10 @@ public class TwitterComponent extends UriEndpointComponent {
         return httpProxyPort;
     }
 
+    /**
+     * TODO: document
+     */
+    public ComponentVerifier getVerifier() {
+        return new TwitterComponentVerifier(getCamelContext());
+    }
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterComponentVerifier.java
----------------------------------------------------------------------
diff --git a/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterComponentVerifier.java b/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterComponentVerifier.java
new file mode 100644
index 0000000..062736c
--- /dev/null
+++ b/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterComponentVerifier.java
@@ -0,0 +1,84 @@
+/**
+ * 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.camel.component.twitter;
+
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.verifier.DefaultComponentVerifier;
+import org.apache.camel.impl.verifier.ResultBuilder;
+import org.apache.camel.impl.verifier.ResultErrorBuilder;
+import org.apache.camel.impl.verifier.ResultErrorHelper;
+import twitter4j.Twitter;
+import twitter4j.TwitterException;
+
+final class TwitterComponentVerifier extends DefaultComponentVerifier {
+    TwitterComponentVerifier(CamelContext camelContext) {
+        super(camelContext);
+    }
+
+    // *********************************
+    // Parameters validation
+    // *********************************
+
+    @Override
+    protected Result verifyParameters(Map<String, Object> parameters) {
+        return ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.PARAMETERS)
+            .error(ResultErrorHelper.requiresOption("consumerKey", parameters))
+            .error(ResultErrorHelper.requiresOption("consumerSecret", parameters))
+            .error(ResultErrorHelper.requiresOption("accessToken", parameters))
+            .error(ResultErrorHelper.requiresOption("accessTokenSecret", parameters))
+            .build();
+    }
+
+    // *********************************
+    // Connectivity validation
+    // *********************************
+
+    @Override
+    protected Result verifyConnectivity(Map<String, Object> parameters) {
+        return ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.CONNECTIVITY)
+            .error(parameters, this::verifyCredentials)
+            .build();
+    }
+
+    private void verifyCredentials(ResultBuilder builder, Map<String, Object> parameters) throws Exception {
+        try {
+            TwitterConfiguration configuration = setProperties(new TwitterConfiguration(), parameters);
+            Twitter twitter = configuration.getTwitter();
+
+            twitter.verifyCredentials();
+        } catch(TwitterException e) {
+            // verifyCredentials throws TwitterException when Twitter service or
+            // network is unavailable or if supplied credential is wrong
+            ResultErrorBuilder errorBuilder = ResultErrorBuilder.withHttpCodeAndText(e.getStatusCode(), e.getErrorMessage())
+                .attribute("twitter.error.code", e.getErrorCode())
+                .attribute("twitter.status.code", e.getStatusCode())
+                .attribute("twitter.exception.code", e.getExceptionCode())
+                .attribute("twitter.exception.message", e.getMessage())
+                .attribute("twitter.exception.instance", e);
+
+            // For a complete list of error codes see:
+            //   https://dev.twitter.com/overview/api/response-codes
+            if (e.getErrorCode() == 89) {
+                errorBuilder.parameter("accessToken");
+            }
+
+            builder.error(errorBuilder.build());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterConfiguration.java
----------------------------------------------------------------------
diff --git a/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterConfiguration.java b/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterConfiguration.java
index f2ce700..336fc65 100644
--- a/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterConfiguration.java
+++ b/components/camel-twitter/src/main/java/org/apache/camel/component/twitter/TwitterConfiguration.java
@@ -21,6 +21,7 @@ import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.UriParam;
 import org.apache.camel.spi.UriParams;
 import org.apache.camel.spi.UriPath;
+import org.apache.camel.util.ObjectHelper;
 import twitter4j.Twitter;
 import twitter4j.TwitterFactory;
 import twitter4j.TwitterStream;
@@ -96,7 +97,7 @@ public class TwitterConfiguration {
      */
     public void checkComplete() {
         if (twitter == null && twitterStream == null
-                && (consumerKey.isEmpty() || consumerSecret.isEmpty() || accessToken.isEmpty() || accessTokenSecret.isEmpty())) {
+                && (ObjectHelper.isEmpty(consumerKey) || ObjectHelper.isEmpty(consumerSecret) || ObjectHelper.isEmpty(accessToken) ||  ObjectHelper.isEmpty(accessTokenSecret))) {
             throw new IllegalArgumentException("twitter or twitterStream or all of consumerKey, consumerSecret, accessToken, and accessTokenSecret must be set!");
         }
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-twitter/src/test/java/org/apache/camel/component/twitter/CamelComponentVerifierTest.java
----------------------------------------------------------------------
diff --git a/components/camel-twitter/src/test/java/org/apache/camel/component/twitter/CamelComponentVerifierTest.java b/components/camel-twitter/src/test/java/org/apache/camel/component/twitter/CamelComponentVerifierTest.java
new file mode 100644
index 0000000..7fcd3c7
--- /dev/null
+++ b/components/camel-twitter/src/test/java/org/apache/camel/component/twitter/CamelComponentVerifierTest.java
@@ -0,0 +1,107 @@
+/**
+ * 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.camel.component.twitter;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.camel.ComponentVerifier;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class CamelComponentVerifierTest extends CamelTwitterTestSupport {
+    @Override
+    public boolean isUseRouteBuilder() {
+        return false;
+    }
+
+    @Test
+    public void testConnectivity() {
+        TwitterComponent component = context().getComponent("twitter", TwitterComponent.class);
+        TwitterComponentVerifier verifier = (TwitterComponentVerifier)component.getVerifier();
+
+        Map<String, Object> parameters = getParameters();
+        ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.OK, result.getStatus());
+    }
+
+    @Test
+    public void testInvalidKeyConfiguration() {
+        TwitterComponent component = context().getComponent("twitter", TwitterComponent.class);
+        TwitterComponentVerifier verifier = (TwitterComponentVerifier)component.getVerifier();
+
+        Map<String, Object> parameters = getParameters();
+        parameters.put("consumerKey", "invalid");
+
+        ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+        Assert.assertEquals(1, result.getErrors().size());
+        Assert.assertEquals("401", result.getErrors().get(0).getCode());
+        Assert.assertEquals(401, result.getErrors().get(0).getAttributes().get("twitter.status.code"));
+        Assert.assertEquals(32, result.getErrors().get(0).getAttributes().get("twitter.error.code"));
+    }
+
+    @Test
+    public void testInvalidTokenConfiguration() {
+        TwitterComponent component = context().getComponent("twitter", TwitterComponent.class);
+        TwitterComponentVerifier verifier = (TwitterComponentVerifier)component.getVerifier();
+
+        Map<String, Object> parameters = getParameters();
+        parameters.put("accessToken", "invalid");
+
+        ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+        Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+        Assert.assertEquals(1, result.getErrors().size());
+        Assert.assertEquals("401", result.getErrors().get(0).getCode());
+        Assert.assertEquals(401, result.getErrors().get(0).getAttributes().get("twitter.status.code"));
+        Assert.assertEquals(89, result.getErrors().get(0).getAttributes().get("twitter.error.code"));
+        Assert.assertEquals(1, result.getErrors().get(0).getParameters().size());
+        Assert.assertEquals("accessToken", result.getErrors().get(0).getParameters().iterator().next());
+    }
+
+    @Test
+    public void testEmptyConfiguration() {
+        TwitterComponent component = context().getComponent("twitter", TwitterComponent.class);
+        TwitterComponentVerifier verifier = (TwitterComponentVerifier)component.getVerifier();
+
+        {
+            // Parameters validation
+            ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.PARAMETERS, Collections.emptyMap());
+
+            Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+            Assert.assertEquals(4, result.getErrors().size());
+            Assert.assertTrue(result.getErrors().get(0).getParameters().contains("consumerKey"));
+            Assert.assertTrue(result.getErrors().get(1).getParameters().contains("consumerSecret"));
+            Assert.assertTrue(result.getErrors().get(2).getParameters().contains("accessToken"));
+            Assert.assertTrue(result.getErrors().get(3).getParameters().contains("accessTokenSecret"));
+        }
+
+        {
+            // Connectivity validation
+            ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.CONNECTIVITY, Collections.emptyMap());
+
+            Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+            Assert.assertEquals(1, result.getErrors().size());
+            Assert.assertEquals(ComponentVerifier.CODE_EXCEPTION, result.getErrors().get(0).getCode());
+            Assert.assertNotNull(result.getErrors().get(0).getAttributes().get(ComponentVerifier.EXCEPTION_INSTANCE));
+            Assert.assertTrue(result.getErrors().get(0).getAttributes().get(ComponentVerifier.EXCEPTION_INSTANCE) instanceof IllegalArgumentException);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c6d54c03/components/camel-twitter/src/test/java/org/apache/camel/component/twitter/CamelTwitterTestSupport.java
----------------------------------------------------------------------
diff --git a/components/camel-twitter/src/test/java/org/apache/camel/component/twitter/CamelTwitterTestSupport.java b/components/camel-twitter/src/test/java/org/apache/camel/component/twitter/CamelTwitterTestSupport.java
index de189c4..19b4bdd 100644
--- a/components/camel-twitter/src/test/java/org/apache/camel/component/twitter/CamelTwitterTestSupport.java
+++ b/components/camel-twitter/src/test/java/org/apache/camel/component/twitter/CamelTwitterTestSupport.java
@@ -19,6 +19,8 @@ package org.apache.camel.component.twitter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Properties;
 
 import org.apache.camel.test.junit4.CamelTestSupport;
@@ -74,6 +76,16 @@ public class CamelTwitterTestSupport extends CamelTestSupport {
             + "&accessTokenSecret=" + accessTokenSecret;
     }
 
+    protected Map<String, Object> getParameters() {
+        Map<String, Object> parameters = new HashMap<>();
+        parameters.put("consumerKey", this.consumerKey);
+        parameters.put("consumerSecret", this.consumerSecret);
+        parameters.put("accessToken", this.accessToken);
+        parameters.put("accessTokenSecret", this.accessTokenSecret);
+
+        return parameters;
+    }
+
     protected void addProperty(Properties properties, String name, String envName) {
         if (!properties.containsKey(name)) {
             String value = System.getenv(envName);


Mime
View raw message