Repository: camel
Updated Branches:
refs/heads/master 95314057c -> 1c6cf329c
CAMEL-11118: PingCheck : validate rest component
Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/1c6cf329
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/1c6cf329
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/1c6cf329
Branch: refs/heads/master
Commit: 1c6cf329c3ea91a7ab4417bd0e9fa43d12f8b1d8
Parents: 9531405
Author: lburgazzoli <lburgazzoli@gmail.com>
Authored: Thu Apr 6 18:53:36 2017 +0200
Committer: lburgazzoli <lburgazzoli@gmail.com>
Committed: Tue Apr 11 14:09:33 2017 +0200
----------------------------------------------------------------------
.../camel/component/rest/RestComponent.java | 15 +-
.../component/rest/RestComponentVerifier.java | 152 ++++++++++++
.../verifier/CatalogVerifierCustomizer.java | 104 ++++++++
.../impl/verifier/DefaultComponentVerifier.java | 60 +++--
.../camel/impl/verifier/ResultBuilder.java | 4 +
.../camel/impl/verifier/ResultErrorBuilder.java | 16 ++
.../apache/camel/util/function/Suppliers.java | 14 ++
.../util/function/ThrowingTriConsumer.java | 22 ++
.../apache/camel/util/function/TriConsumer.java | 22 ++
.../rest/RestComponentVerifierTest.java | 137 +++++++++++
.../component/http4/HttpComponentVerifier.java | 156 +++++++-----
.../rest/RestCamelComponentVerifierTest.java | 178 ++++++++++++++
.../component/undertow/UndertowComponent.java | 19 +-
.../undertow/UndertowComponentVerifier.java | 239 +++++++++++++++++++
.../undertow/UndertowComponentVerifierTest.java | 103 ++++++++
.../rest/RestUndertowComponentVerifierTest.java | 126 ++++++++++
16 files changed, 1282 insertions(+), 85 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/camel/blob/1c6cf329/camel-core/src/main/java/org/apache/camel/component/rest/RestComponent.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/component/rest/RestComponent.java b/camel-core/src/main/java/org/apache/camel/component/rest/RestComponent.java
index 567d315..d1c1963 100644
--- a/camel-core/src/main/java/org/apache/camel/component/rest/RestComponent.java
+++ b/camel-core/src/main/java/org/apache/camel/component/rest/RestComponent.java
@@ -24,7 +24,9 @@ import java.util.function.Consumer;
import java.util.function.Supplier;
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.DefaultComponent;
import org.apache.camel.model.rest.RestConstants;
import org.apache.camel.spi.Metadata;
@@ -33,13 +35,13 @@ import org.apache.camel.util.CamelContextHelper;
import org.apache.camel.util.FileUtil;
import org.apache.camel.util.IntrospectionSupport;
import org.apache.camel.util.ObjectHelper;
-import org.apache.camel.util.StringHelper;
import org.apache.camel.util.URISupport;
/**
* Rest component.
*/
-public class RestComponent extends DefaultComponent {
+@Metadata(label = "verifiers", enums = "parameters,connectivity")
+public class RestComponent extends DefaultComponent implements VerifiableComponent {
@Metadata(label = "common")
private String componentName;
@@ -230,4 +232,13 @@ public class RestComponent extends DefaultComponent {
}
}
+ /**
+ * Get the {@link ComponentVerifier}
+ *
+ * @return the Component Verifier
+ */
+ @Override
+ public ComponentVerifier getVerifier() {
+ return new RestComponentVerifier(this);
+ }
}
http://git-wip-us.apache.org/repos/asf/camel/blob/1c6cf329/camel-core/src/main/java/org/apache/camel/component/rest/RestComponentVerifier.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/component/rest/RestComponentVerifier.java b/camel-core/src/main/java/org/apache/camel/component/rest/RestComponentVerifier.java
new file mode 100644
index 0000000..5d19e0d
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/component/rest/RestComponentVerifier.java
@@ -0,0 +1,152 @@
+/**
+ * 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.rest;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.Component;
+import org.apache.camel.ComponentVerifier;
+import org.apache.camel.VerifiableComponent;
+import org.apache.camel.catalog.JSonSchemaHelper;
+import org.apache.camel.catalog.RuntimeCamelCatalog;
+import org.apache.camel.impl.verifier.CatalogVerifierCustomizer;
+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.spi.RestConsumerFactory;
+import org.apache.camel.spi.RestProducerFactory;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.function.Suppliers;
+
+public class RestComponentVerifier extends DefaultComponentVerifier {
+ private static final CatalogVerifierCustomizer CUSTOMIZER = new CatalogVerifierCustomizer().excludeUnknown();
+ private final RestComponent component;
+
+ RestComponentVerifier(RestComponent component) {
+ super("rest", component.getCamelContext());
+
+ this.component = component;
+ }
+
+ // *********************************
+ // Parameters validation
+ // *********************************
+
+ @Override
+ protected Result verifyParameters(Map<String, Object> parameters) {
+ ResultBuilder builder = ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.PARAMETERS);
+
+ // Validate using the catalog but do not report unknown options as error
+ // as the may be used to customize the underlying component
+ super.verifyParametersAgainstCatalog(builder, parameters, CUSTOMIZER);
+
+ verifyUnderlyingComponent(Scope.PARAMETERS, builder, parameters);
+
+ return builder.build();
+ }
+
+ // *********************************
+ // Connectivity validation
+ // *********************************
+
+ @Override
+ protected Result verifyConnectivity(Map<String, Object> parameters) {
+ ResultBuilder builder = ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.CONNECTIVITY);
+
+ verifyUnderlyingComponent(Scope.CONNECTIVITY, builder, parameters);
+
+ return builder.build();
+ }
+
+ // *********************************
+ // Helpers
+ // *********************************
+
+ protected void verifyUnderlyingComponent(Scope scope, ResultBuilder builder, Map<String, Object> parameters) {
+ // componentName is required for validation even at runtime camel might
+ // be able to find a suitable component at runtime.
+ String componentName = (String)parameters.get("componentName");
+ if (ObjectHelper.isNotEmpty(componentName)) {
+ try {
+ Component component = getTransportComponent(componentName);
+ if (component instanceof VerifiableComponent) {
+ final RuntimeCamelCatalog catalog = getCamelContext().getRuntimeCamelCatalog();
+ final String json = catalog.componentJSonSchema("rest");
+ final Map<String, Object> restParameters = new HashMap<>(parameters);
+
+ for (Map<String, String> m : JSonSchemaHelper.parseJsonSchema("componentProperties", json, true)) {
+ String name = m.get("name");
+ Object val = restParameters.remove(name);
+ if (val != null) {
+ // Add rest prefix to properties belonging to the rest
+ // component so the underlying component know we want
+ // to validate rest-related stuffs.
+ restParameters.put("rest." + name, parameters.get(name));
+ }
+ }
+ for (Map<String, String> m : JSonSchemaHelper.parseJsonSchema("properties", json, true)) {
+ String name = m.get("name");
+ Object val = restParameters.remove(name);
+ if (val != null) {
+ // Add rest prefix to properties belonging to the rest
+ // component so the underlying component know we want
+ // to validate rest-related stuffs.
+ restParameters.put("rest." + name, parameters.get(name));
+ }
+ }
+
+ ComponentVerifier verifier = ((VerifiableComponent)component).getVerifier();
+
+ // restParameters now should contains rest-component related
+ // properties with "rest." prefix and all the remaining can
+ // be used to customize the underlying component (i.e. http
+ // proxies, auth, etc)
+ Result result = verifier.verify(scope, restParameters);
+
+ // Combine errors and add an information about the component
+ // they comes from
+ for (VerificationError error : result.getErrors()) {
+ builder.error(
+ ResultErrorBuilder.fromError(error)
+ .detail("component", componentName)
+ .build()
+ );
+ }
+ } else {
+ builder.error(
+ ResultErrorBuilder.withUnsupportedComponent(componentName).build()
+ );
+ }
+ } catch (Exception e) {
+ builder.error(
+ ResultErrorBuilder.withException(e).build()
+ );
+ }
+ } else {
+ builder.error(ResultErrorBuilder.withMissingOption("componentName").build());
+ }
+ }
+
+ private Component getTransportComponent(String componentName) throws Exception {
+ return Suppliers.firstMatching(
+ comp -> comp != null && (comp instanceof RestConsumerFactory || comp instanceof RestProducerFactory),
+ () -> getCamelContext().getRegistry().lookupByNameAndType(componentName, Component.class),
+ () -> getCamelContext().getComponent(componentName, true, false)
+ ).orElse(null);
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/1c6cf329/camel-core/src/main/java/org/apache/camel/impl/verifier/CatalogVerifierCustomizer.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/impl/verifier/CatalogVerifierCustomizer.java b/camel-core/src/main/java/org/apache/camel/impl/verifier/CatalogVerifierCustomizer.java
new file mode 100644
index 0000000..43d6993
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/impl/verifier/CatalogVerifierCustomizer.java
@@ -0,0 +1,104 @@
+/**
+ * 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.impl.verifier;
+
+public class CatalogVerifierCustomizer {
+ private boolean includeUnknown = true;
+ private boolean includeRequired = true;
+ private boolean includeInvalidBoolean = true;
+ private boolean includeInvalidInteger = true;
+ private boolean includeInvalidNumber = true;
+ private boolean includeInvalidEnum = true;
+
+ public boolean isIncludeUnknown() {
+ return includeUnknown;
+ }
+
+ public void setIncludeUnknown(boolean includeUnknown) {
+ this.includeUnknown = includeUnknown;
+ }
+
+ public boolean isIncludeRequired() {
+ return includeRequired;
+ }
+
+ public void setIncludeRequired(boolean includeRequired) {
+ this.includeRequired = includeRequired;
+ }
+
+ public boolean isIncludeInvalidBoolean() {
+ return includeInvalidBoolean;
+ }
+
+ public void setIncludeInvalidBoolean(boolean includeInvalidBoolean) {
+ this.includeInvalidBoolean = includeInvalidBoolean;
+ }
+
+ public boolean isIncludeInvalidInteger() {
+ return includeInvalidInteger;
+ }
+
+ public void setIncludeInvalidInteger(boolean includeInvalidInteger) {
+ this.includeInvalidInteger = includeInvalidInteger;
+ }
+
+ public boolean isIncludeInvalidNumber() {
+ return includeInvalidNumber;
+ }
+
+ public void setIncludeInvalidNumber(boolean includeInvalidNumber) {
+ this.includeInvalidNumber = includeInvalidNumber;
+ }
+
+ public boolean isIncludeInvalidEnum() {
+ return includeInvalidEnum;
+ }
+
+ public void setIncludeInvalidEnum(boolean includeInvalidEnum) {
+ this.includeInvalidEnum = includeInvalidEnum;
+ }
+
+ public CatalogVerifierCustomizer excludeUnknown() {
+ this.includeUnknown = false;
+ return this;
+ }
+
+ public CatalogVerifierCustomizer excludeRequired() {
+ this.includeRequired = false;
+ return this;
+ }
+
+ public CatalogVerifierCustomizer excludeInvalidBoolean() {
+ this.includeInvalidBoolean = false;
+ return this;
+ }
+
+ public CatalogVerifierCustomizer excludeInvalidInteger() {
+ this.includeInvalidInteger = false;
+ return this;
+ }
+
+ public CatalogVerifierCustomizer excludeInvalidNumber() {
+ this.includeInvalidNumber = false;
+ return this;
+ }
+
+ public CatalogVerifierCustomizer excludeInvalidEnum() {
+ this.includeInvalidEnum = false;
+ return this;
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/1c6cf329/camel-core/src/main/java/org/apache/camel/impl/verifier/DefaultComponentVerifier.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/impl/verifier/DefaultComponentVerifier.java b/camel-core/src/main/java/org/apache/camel/impl/verifier/DefaultComponentVerifier.java
index 28908b0..b9d9998 100644
--- a/camel-core/src/main/java/org/apache/camel/impl/verifier/DefaultComponentVerifier.java
+++ b/camel-core/src/main/java/org/apache/camel/impl/verifier/DefaultComponentVerifier.java
@@ -62,7 +62,7 @@ public class DefaultComponentVerifier implements ComponentVerifier {
return verifyConnectivity(parameters);
}
- throw new IllegalArgumentException("Unsupported Verifier scope: " + scope);
+ return ResultBuilder.unsupportedScope(scope).build();
}
protected Result verifyConnectivity(Map<String, Object> parameters) {
@@ -83,6 +83,10 @@ public class DefaultComponentVerifier implements ComponentVerifier {
// *************************************
protected void verifyParametersAgainstCatalog(ResultBuilder builder, Map<String, Object> parameters) {
+ verifyParametersAgainstCatalog(builder, parameters, new CatalogVerifierCustomizer());
+ }
+
+ protected void verifyParametersAgainstCatalog(ResultBuilder builder, Map<String, Object> parameters, CatalogVerifierCustomizer customizer) {
String scheme = defaultScheme;
if (parameters.containsKey("scheme")) {
scheme = parameters.get("scheme").toString();
@@ -105,27 +109,39 @@ public class DefaultComponentVerifier implements ComponentVerifier {
);
if (!result.isSuccess()) {
- stream(result.getUnknown())
- .map(option -> ResultErrorBuilder.withUnknownOption(option).build())
- .forEach(builder::error);
- stream(result.getRequired())
- .map(option -> ResultErrorBuilder.withMissingOption(option).build())
- .forEach(builder::error);
- stream(result.getInvalidBoolean())
- .map(entry -> ResultErrorBuilder.withIllegalOption(entry.getKey(), entry.getValue()).build())
- .forEach(builder::error);
- stream(result.getInvalidInteger())
- .map(entry -> ResultErrorBuilder.withIllegalOption(entry.getKey(), entry.getValue()).build())
- .forEach(builder::error);
- stream(result.getInvalidNumber())
- .map(entry -> ResultErrorBuilder.withIllegalOption(entry.getKey(), entry.getValue()).build())
- .forEach(builder::error);
- stream(result.getInvalidEnum())
- .map(entry ->
- ResultErrorBuilder.withIllegalOption(entry.getKey(), entry.getValue())
- .detail("enum.values", result.getEnumChoices(entry.getKey()))
- .build())
- .forEach(builder::error);
+ if (customizer.isIncludeUnknown()) {
+ stream(result.getUnknown())
+ .map(option -> ResultErrorBuilder.withUnknownOption(option).build())
+ .forEach(builder::error);
+ }
+ if (customizer.isIncludeRequired()) {
+ stream(result.getRequired())
+ .map(option -> ResultErrorBuilder.withMissingOption(option).build())
+ .forEach(builder::error);
+ }
+ if (customizer.isIncludeInvalidBoolean()) {
+ stream(result.getInvalidBoolean())
+ .map(entry -> ResultErrorBuilder.withIllegalOption(entry.getKey(), entry.getValue()).build())
+ .forEach(builder::error);
+ }
+ if (customizer.isIncludeInvalidInteger()) {
+ stream(result.getInvalidInteger())
+ .map(entry -> ResultErrorBuilder.withIllegalOption(entry.getKey(), entry.getValue()).build())
+ .forEach(builder::error);
+ }
+ if (customizer.isIncludeInvalidNumber()) {
+ stream(result.getInvalidNumber())
+ .map(entry -> ResultErrorBuilder.withIllegalOption(entry.getKey(), entry.getValue()).build())
+ .forEach(builder::error);
+ }
+ if (customizer.isIncludeInvalidEnum()) {
+ stream(result.getInvalidEnum())
+ .map(entry ->
+ ResultErrorBuilder.withIllegalOption(entry.getKey(), entry.getValue())
+ .detail("enum.values", result.getEnumChoices(entry.getKey()))
+ .build())
+ .forEach(builder::error);
+ }
}
}
http://git-wip-us.apache.org/repos/asf/camel/blob/1c6cf329/camel-core/src/main/java/org/apache/camel/impl/verifier/ResultBuilder.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/impl/verifier/ResultBuilder.java b/camel-core/src/main/java/org/apache/camel/impl/verifier/ResultBuilder.java
index ce049c0..5e8ddae 100644
--- a/camel-core/src/main/java/org/apache/camel/impl/verifier/ResultBuilder.java
+++ b/camel-core/src/main/java/org/apache/camel/impl/verifier/ResultBuilder.java
@@ -136,4 +136,8 @@ public final class ResultBuilder {
public static ResultBuilder unsupported() {
return withStatusAndScope(ComponentVerifier.Result.Status.UNSUPPORTED, ComponentVerifier.Scope.PARAMETERS);
}
+
+ public static ResultBuilder unsupportedScope(ComponentVerifier.Scope scope) {
+ return withStatusAndScope(ComponentVerifier.Result.Status.UNSUPPORTED, scope);
+ }
}
http://git-wip-us.apache.org/repos/asf/camel/blob/1c6cf329/camel-core/src/main/java/org/apache/camel/impl/verifier/ResultErrorBuilder.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/impl/verifier/ResultErrorBuilder.java b/camel-core/src/main/java/org/apache/camel/impl/verifier/ResultErrorBuilder.java
index dbef3e4..daadb48 100644
--- a/camel-core/src/main/java/org/apache/camel/impl/verifier/ResultErrorBuilder.java
+++ b/camel-core/src/main/java/org/apache/camel/impl/verifier/ResultErrorBuilder.java
@@ -101,6 +101,14 @@ public final class ResultErrorBuilder {
return this;
}
+ public ResultErrorBuilder details(Map<VerificationError.Attribute, Object> details) {
+ for (Map.Entry<VerificationError.Attribute, Object> entry : details.entrySet()) {
+ detail(entry.getKey(), entry.getValue());
+ }
+
+ return this;
+ }
+
// **********************************
// Build
// **********************************
@@ -118,6 +126,14 @@ public final class ResultErrorBuilder {
// Helpers
// **********************************
+ public static ResultErrorBuilder fromError(VerificationError error) {
+ return new ResultErrorBuilder()
+ .code(error.getCode())
+ .description(error.getDescription())
+ .parameterKeys(error.getParameterKeys())
+ .details(error.getDetails());
+ }
+
public static ResultErrorBuilder withCode(VerificationError.Code code) {
return new ResultErrorBuilder().code(code);
}
http://git-wip-us.apache.org/repos/asf/camel/blob/1c6cf329/camel-core/src/main/java/org/apache/camel/util/function/Suppliers.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/function/Suppliers.java b/camel-core/src/main/java/org/apache/camel/util/function/Suppliers.java
index b7d874c..84a43b2 100644
--- a/camel-core/src/main/java/org/apache/camel/util/function/Suppliers.java
+++ b/camel-core/src/main/java/org/apache/camel/util/function/Suppliers.java
@@ -20,6 +20,7 @@ import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
+import java.util.function.Predicate;
import java.util.function.Supplier;
public final class Suppliers {
@@ -76,4 +77,17 @@ public final class Suppliers {
return Optional.ofNullable(answer);
}
+
+ public static <T> Optional<T> firstMatching(Predicate<T> predicate, ThrowingSupplier<T, Exception>... suppliers) throws Exception {
+ T answer = null;
+
+ for (int i = 0; i < suppliers.length; i++) {
+ answer = suppliers[i].get();
+ if (predicate.test(answer)) {
+ break;
+ }
+ }
+
+ return Optional.ofNullable(answer);
+ }
}
http://git-wip-us.apache.org/repos/asf/camel/blob/1c6cf329/camel-core/src/main/java/org/apache/camel/util/function/ThrowingTriConsumer.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/function/ThrowingTriConsumer.java b/camel-core/src/main/java/org/apache/camel/util/function/ThrowingTriConsumer.java
new file mode 100644
index 0000000..ee3a626
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/util/function/ThrowingTriConsumer.java
@@ -0,0 +1,22 @@
+/**
+ * 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.util.function;
+
+@FunctionalInterface
+public interface ThrowingTriConsumer<I1, I2, I3, T extends Throwable> {
+ void accept(I1 i1, I2 i2, I3 i3) throws T;
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/camel/blob/1c6cf329/camel-core/src/main/java/org/apache/camel/util/function/TriConsumer.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/function/TriConsumer.java b/camel-core/src/main/java/org/apache/camel/util/function/TriConsumer.java
new file mode 100644
index 0000000..f119d92
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/util/function/TriConsumer.java
@@ -0,0 +1,22 @@
+/**
+ * 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.util.function;
+
+@FunctionalInterface
+public interface TriConsumer<I1, I2, I3> {
+ void accept(I1 i1, I2 i2, I3 i3);
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/camel/blob/1c6cf329/camel-core/src/test/java/org/apache/camel/component/rest/RestComponentVerifierTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/component/rest/RestComponentVerifierTest.java b/camel-core/src/test/java/org/apache/camel/component/rest/RestComponentVerifierTest.java
new file mode 100644
index 0000000..73fb130
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/component/rest/RestComponentVerifierTest.java
@@ -0,0 +1,137 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.rest;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.ComponentVerifier;
+import org.apache.camel.Consumer;
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Endpoint;
+import org.apache.camel.Processor;
+import org.apache.camel.Producer;
+import org.apache.camel.VerifiableComponent;
+import org.apache.camel.impl.DefaultComponent;
+import org.apache.camel.impl.JndiRegistry;
+import org.apache.camel.impl.verifier.ResultBuilder;
+import org.apache.camel.impl.verifier.ResultErrorHelper;
+import org.apache.camel.spi.RestConfiguration;
+import org.apache.camel.spi.RestConsumerFactory;
+import org.apache.camel.spi.RestProducerFactory;
+import org.junit.Assert;
+
+public class RestComponentVerifierTest extends ContextTestSupport {
+ @Override
+ protected JndiRegistry createRegistry() throws Exception {
+ JndiRegistry registry = super.createRegistry();
+ registry.bind("rest", new RestComponent());
+ registry.bind("rest-component", new MyComponent());
+ return registry;
+ }
+
+ public void testParameters() throws Exception {
+ RestComponent component = context.getComponent("rest", RestComponent.class);
+ RestComponentVerifier verifier = (RestComponentVerifier)component.getVerifier();
+
+ Map<String, Object> parameters = new HashMap<>();
+ parameters.put("componentName", "rest-component");
+ parameters.put("host", "http://localhost:1234");
+ parameters.put("path", "verify");
+ parameters.put("method", "get");
+
+ // This parameter does not belong to the rest component and validation
+ // is delegated to the underlying component
+ parameters.put("authProxy", "http://localhost:8080");
+
+ ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.PARAMETERS, parameters);
+
+ Assert.assertEquals(ComponentVerifier.Result.Status.OK, result.getStatus());
+ }
+
+ public void testMissingParameters() throws Exception {
+ RestComponent component = context.getComponent("rest", RestComponent.class);
+ RestComponentVerifier verifier = (RestComponentVerifier)component.getVerifier();
+
+ Map<String, Object> parameters = new HashMap<>();
+ parameters.put("componentName", "rest-component");
+ parameters.put("host", "http://localhost:" + 1234);
+ parameters.put("path", "verify");
+
+ // This parameter does not belong to the rest component and validation
+ // is delegated to the underlying component
+ parameters.put("authProxy", "http://localhost:8080");
+
+ ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.PARAMETERS, parameters);
+
+ Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+ Assert.assertEquals(1, result.getErrors().size());
+ Assert.assertEquals(ComponentVerifier.VerificationError.StandardCode.MISSING_PARAMETER, result.getErrors().get(0).getCode());
+ Assert.assertEquals(1, result.getErrors().get(0).getParameterKeys().size());
+ Assert.assertTrue(result.getErrors().get(0).getParameterKeys().contains("method"));
+ }
+
+ // ***************************************************
+ //
+ // ***************************************************
+
+ private final class MyComponent extends DefaultComponent implements RestProducerFactory, RestConsumerFactory, VerifiableComponent {
+ @Override
+ protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Producer createProducer(
+ CamelContext camelContext,
+ String host,
+ String verb,
+ String basePath,
+ String uriTemplate,
+ String queryParameters,
+ String consumes,
+ String produces,
+ Map<String, Object> parameters)
+ throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Consumer createConsumer(
+ CamelContext camelContext,
+ Processor processor,
+ String verb,
+ String basePath,
+ String uriTemplate,
+ String consumes,
+ String produces,
+ RestConfiguration configuration,
+ Map<String, Object> parameters)
+ throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ComponentVerifier getVerifier() {
+ return (scope, parameters) ->
+ ResultBuilder.withStatusAndScope(ComponentVerifier.Result.Status.OK, scope)
+ .error(ResultErrorHelper.requiresOption("authProxy", parameters))
+ .build();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/1c6cf329/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
index ed55b12..46e3bc4 100644
--- 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
@@ -17,15 +17,16 @@
package org.apache.camel.component.http4;
import java.net.UnknownHostException;
+import java.util.HashMap;
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.camel.util.FileUtil;
+import org.apache.camel.util.ObjectHelper;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
@@ -48,11 +49,23 @@ final class HttpComponentVerifier extends DefaultComponentVerifier {
@Override
protected Result verifyParameters(Map<String, Object> parameters) {
- // The default is success
- ResultBuilder builder = ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.PARAMETERS);
+ // Default is success
+ final ResultBuilder builder = ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.PARAMETERS);
+ // Make a copy to avoid clashing with parent validation
+ final HashMap<String, Object> verifyParams = new HashMap<>(parameters);
+ // Check if validation is rest-related
+ final boolean isRest = verifyParams.entrySet().stream().anyMatch(e -> e.getKey().startsWith("rest."));
+
+ if (isRest) {
+ // Build the httpUri from rest configuration
+ verifyParams.put("httpUri", buildHttpUriFromRestParameters(parameters));
+
+ // Cleanup parameters map from rest related stuffs
+ verifyParams.entrySet().removeIf(e -> e.getKey().startsWith("rest."));
+ }
// Validate using the catalog
- super.verifyParametersAgainstCatalog(builder, parameters);
+ super.verifyParametersAgainstCatalog(builder, verifyParams);
return builder.build();
}
@@ -64,71 +77,98 @@ final class HttpComponentVerifier extends DefaultComponentVerifier {
@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);
+ final ResultBuilder builder = ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.CONNECTIVITY);
+ // Make a copy to avoid clashing with parent validation
+ final HashMap<String, Object> verifyParams = new HashMap<>(parameters);
+ // Check if validation is rest-related
+ final boolean isRest = verifyParams.entrySet().stream().anyMatch(e -> e.getKey().startsWith("rest."));
+
+ if (isRest) {
+ // Build the httpUri from rest configuration
+ verifyParams.put("httpUri", buildHttpUriFromRestParameters(parameters));
+
+ // Cleanup parameters from rest related stuffs
+ verifyParams.entrySet().removeIf(e -> e.getKey().startsWith("rest."));
}
- 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())
- .parameterKey("authUsername")
- .parameterKey("authPassword")
- .build()
- );
- } else if (code >= 300 && code < 400) {
- // redirect
- builder.error(
- ResultErrorBuilder.withHttpCode(code)
- .description(response.getStatusLine().getReasonPhrase())
- .parameterKey("httpUri")
- .detail(VerificationError.HttpAttribute.HTTP_REDIRECT, () -> 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) {
+ String httpUri = getOption(verifyParams, "httpUri", String.class).orElse(null);
+ if (ObjectHelper.isEmpty(httpUri)) {
builder.error(
- ResultErrorBuilder.withException(e)
- .parameterKey("httpUri")
+ ResultErrorBuilder.withMissingOption("httpUri")
+ .detail("rest", isRest)
.build()
);
}
+
+ try {
+ CloseableHttpClient httpclient = createHttpClient(verifyParams);
+ HttpUriRequest request = new HttpGet(httpUri);
+
+ try (CloseableHttpResponse response = httpclient.execute(request)) {
+ int code = response.getStatusLine().getStatusCode();
+ String okCodes = getOption(verifyParams, "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())
+ .parameterKey("authUsername")
+ .parameterKey("authPassword")
+ .build()
+ );
+ } else if (code >= 300 && code < 400) {
+ // redirect
+ builder.error(
+ ResultErrorBuilder.withHttpCode(code)
+ .description(response.getStatusLine().getReasonPhrase())
+ .parameterKey("httpUri")
+ .detail(VerificationError.HttpAttribute.HTTP_REDIRECT, () -> 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)
+ .parameterKey("httpUri")
+ .build()
+ );
+ }
+ } catch (Exception e) {
+ builder.error(ResultErrorBuilder.withException(e).build());
+ }
+
+ return builder.build();
}
// *********************************
// Helpers
// *********************************
+ private String buildHttpUriFromRestParameters(Map<String, Object> parameters) {
+ // We are doing rest endpoint validation but as today the endpoint
+ // can't do any param substitution so the validation is performed
+ // against the http uri
+ String httpUri = getOption(parameters, "rest.host", String.class).orElse(null);
+ String path = getOption(parameters, "rest.path", String.class).map(FileUtil::stripLeadingSeparator).orElse(null);
+
+ if (ObjectHelper.isNotEmpty(httpUri) && ObjectHelper.isNotEmpty(path)) {
+ httpUri = httpUri + "/" + path;
+ }
+
+ return httpUri;
+ }
+
private Optional<HttpClientConfigurer> configureAuthentication(Map<String, Object> parameters) {
Optional<String> authUsername = getOption(parameters, "authUsername", String.class);
Optional<String> authPassword = getOption(parameters, "authPassword", String.class);
http://git-wip-us.apache.org/repos/asf/camel/blob/1c6cf329/components/camel-http4/src/test/java/org/apache/camel/component/http4/rest/RestCamelComponentVerifierTest.java
----------------------------------------------------------------------
diff --git a/components/camel-http4/src/test/java/org/apache/camel/component/http4/rest/RestCamelComponentVerifierTest.java b/components/camel-http4/src/test/java/org/apache/camel/component/http4/rest/RestCamelComponentVerifierTest.java
new file mode 100644
index 0000000..7fb540f
--- /dev/null
+++ b/components/camel-http4/src/test/java/org/apache/camel/component/http4/rest/RestCamelComponentVerifierTest.java
@@ -0,0 +1,178 @@
+/**
+ * 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.rest;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.ComponentVerifier;
+import org.apache.camel.component.http4.BaseHttpTest;
+import org.apache.camel.component.http4.handler.BasicValidationHandler;
+import org.apache.camel.component.rest.RestComponent;
+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.HttpProcessor;
+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 RestCamelComponentVerifierTest extends BaseHttpTest {
+ private HttpServer localServer;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ localServer = ServerBootstrap.bootstrap()
+ .setHttpProcessor(getHttpProcessor())
+ .registerHandler("/verify", 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();
+ }
+
+ // *************************************************
+ // Tests
+ // *************************************************
+ @Test
+ public void testParameters() throws Exception {
+ RestComponent component = context().getComponent("rest", RestComponent.class);
+ ComponentVerifier verifier = component.getVerifier();
+
+ Map<String, Object> parameters = new HashMap<>();
+ parameters.put("componentName", "http4");
+ parameters.put("host", "http://localhost:" + localServer.getLocalPort());
+ parameters.put("path", "verify");
+ parameters.put("method", "get");
+
+ ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.PARAMETERS, parameters);
+
+ Assert.assertEquals(ComponentVerifier.Result.Status.OK, result.getStatus());
+ }
+
+ @Test
+ public void testMissingRestParameters() throws Exception {
+ RestComponent component = context.getComponent("rest", RestComponent.class);
+ ComponentVerifier verifier = component.getVerifier();
+
+ Map<String, Object> parameters = new HashMap<>();
+ parameters.put("componentName", "http4");
+ parameters.put("host", "http://localhost:" + localServer.getLocalPort());
+ parameters.put("path", "verify");
+
+ // This parameter does not belong to the rest component and validation
+ // is delegated to the transport component
+ parameters.put("copyHeaders", false);
+
+ ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.PARAMETERS, parameters);
+
+ Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+ Assert.assertEquals(1, result.getErrors().size());
+ Assert.assertEquals(ComponentVerifier.VerificationError.StandardCode.MISSING_PARAMETER, result.getErrors().get(0).getCode());
+ Assert.assertEquals(1, result.getErrors().get(0).getParameterKeys().size());
+ Assert.assertTrue(result.getErrors().get(0).getParameterKeys().contains("method"));
+ }
+
+ @Test
+ public void testWrongComponentParameters() throws Exception {
+ RestComponent component = context.getComponent("rest", RestComponent.class);
+ ComponentVerifier verifier = component.getVerifier();
+
+ Map<String, Object> parameters = new HashMap<>();
+ parameters.put("componentName", "http4");
+ parameters.put("host", "http://localhost:" + localServer.getLocalPort());
+ parameters.put("path", "verify");
+ parameters.put("method", "get");
+
+ // This parameter does not belong to the rest component and validation
+ // is delegated to the transport component
+ parameters.put("nonExistingOption", true);
+
+ ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.PARAMETERS, parameters);
+
+ Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+ Assert.assertEquals(1, result.getErrors().size());
+ Assert.assertEquals(ComponentVerifier.VerificationError.StandardCode.UNKNOWN_PARAMETER, result.getErrors().get(0).getCode());
+ Assert.assertEquals(1, result.getErrors().get(0).getParameterKeys().size());
+ Assert.assertTrue(result.getErrors().get(0).getParameterKeys().contains("nonExistingOption"));
+ }
+
+ @Test
+ public void testConnectivity() throws Exception {
+ RestComponent component = context().getComponent("rest", RestComponent.class);
+ ComponentVerifier verifier = component.getVerifier();
+
+ Map<String, Object> parameters = new HashMap<>();
+ parameters.put("componentName", "http4");
+ parameters.put("host", "http://localhost:" + localServer.getLocalPort());
+ parameters.put("path", "verify");
+ parameters.put("method", "get");
+
+ ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+ Assert.assertEquals(ComponentVerifier.Result.Status.OK, result.getStatus());
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/camel/blob/1c6cf329/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java
----------------------------------------------------------------------
diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java
index 09d2ee0..86760ab 100644
--- a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java
+++ b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java
@@ -24,11 +24,13 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.camel.CamelContext;
+import org.apache.camel.ComponentVerifier;
import org.apache.camel.Consumer;
import org.apache.camel.Endpoint;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
-import org.apache.camel.impl.UriEndpointComponent;
+import org.apache.camel.VerifiableComponent;
+import org.apache.camel.impl.DefaultComponent;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.RestApiConsumerFactory;
import org.apache.camel.spi.RestConfiguration;
@@ -48,7 +50,8 @@ import org.slf4j.LoggerFactory;
/**
* Represents the component that manages {@link UndertowEndpoint}.
*/
-public class UndertowComponent extends UriEndpointComponent implements RestConsumerFactory, RestApiConsumerFactory, RestProducerFactory {
+@Metadata(label = "verifiers", enums = "parameters,connectivity")
+public class UndertowComponent extends DefaultComponent implements RestConsumerFactory, RestApiConsumerFactory, RestProducerFactory, VerifiableComponent {
private static final Logger LOG = LoggerFactory.getLogger(UndertowEndpoint.class);
private Map<UndertowHostKey, UndertowHost> undertowRegistry = new ConcurrentHashMap<UndertowHostKey, UndertowHost>();
@@ -61,7 +64,10 @@ public class UndertowComponent extends UriEndpointComponent implements RestConsu
private UndertowHostOptions hostOptions;
public UndertowComponent() {
- super(UndertowEndpoint.class);
+ }
+
+ public UndertowComponent(CamelContext context) {
+ super(context);
}
@Override
@@ -326,4 +332,11 @@ public class UndertowComponent extends UriEndpointComponent implements RestConsu
this.hostOptions = hostOptions;
}
+ /**
+ *
+ */
+ public ComponentVerifier getVerifier() {
+ return new UndertowComponentVerifier(this);
+ }
+
}
http://git-wip-us.apache.org/repos/asf/camel/blob/1c6cf329/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponentVerifier.java
----------------------------------------------------------------------
diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponentVerifier.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponentVerifier.java
new file mode 100644
index 0000000..637820d
--- /dev/null
+++ b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponentVerifier.java
@@ -0,0 +1,239 @@
+/**
+ * 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.undertow;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import io.undertow.client.ClientCallback;
+import io.undertow.client.ClientConnection;
+import io.undertow.client.ClientExchange;
+import io.undertow.client.ClientRequest;
+import io.undertow.client.ClientResponse;
+import io.undertow.client.UndertowClient;
+import io.undertow.connector.ByteBufferPool;
+import io.undertow.server.DefaultByteBufferPool;
+import io.undertow.util.Headers;
+import io.undertow.util.HttpString;
+import io.undertow.util.Methods;
+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.util.FileUtil;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.URISupport;
+import org.apache.camel.util.UnsafeUriCharactersEncoder;
+import org.xnio.AbstractIoFuture;
+import org.xnio.IoFuture;
+import org.xnio.OptionMap;
+import org.xnio.Xnio;
+import org.xnio.XnioWorker;
+
+public final class UndertowComponentVerifier extends DefaultComponentVerifier {
+ private final UndertowComponent component;
+
+ public UndertowComponentVerifier(UndertowComponent component) {
+ super("undertow", component.getCamelContext());
+
+ this.component = component;
+ }
+
+ // *********************************
+ // Parameters validation
+ // *********************************
+
+ @Override
+ protected Result verifyParameters(Map<String, Object> parameters) {
+ // Default is success
+ final ResultBuilder builder = ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.PARAMETERS);
+ // Make a copy to avoid clashing with parent validation
+ final HashMap<String, Object> verifyParams = new HashMap<>(parameters);
+ // Check if validation is rest-related
+ final boolean isRest = verifyParams.entrySet().stream().anyMatch(e -> e.getKey().startsWith("rest."));
+
+ if (isRest) {
+ String httpUri = getOption(verifyParams, "rest.host", String.class).orElse(null);
+ String path = getOption(verifyParams, "rest.path", String.class).map(FileUtil::stripLeadingSeparator).orElse(null);
+
+ if (ObjectHelper.isNotEmpty(httpUri) && ObjectHelper.isNotEmpty(path)) {
+ httpUri = httpUri + "/" + path;
+ }
+
+ verifyParams.put("httpURI", httpUri);
+
+ // Cleanup parameters map from rest related stuffs
+ verifyParams.entrySet().removeIf(e -> e.getKey().startsWith("rest."));
+ }
+
+ // Validate using the catalog
+ super.verifyParametersAgainstCatalog(builder, verifyParams);
+
+ return builder.build();
+ }
+
+ // *********************************
+ // Connectivity validation
+ // *********************************
+
+ @Override
+ protected Result verifyConnectivity(Map<String, Object> parameters) {
+ // Default is success
+ final ResultBuilder builder = ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.CONNECTIVITY);
+ // Make a copy to avoid clashing with parent validation
+ final HashMap<String, Object> verifyParams = new HashMap<>(parameters);
+ // Check if validation is rest-related
+ final boolean isRest = verifyParams.entrySet().stream().anyMatch(e -> e.getKey().startsWith("rest."));
+
+ String httpUri;
+ Optional<String> httpMethod;
+
+ if (isRest) {
+ // We are doing rest endpoint validation but as today the endpoint
+ // can't do any param substitution so the validation is performed
+ // against the http uri
+ httpUri = getOption(verifyParams, "rest.host", String.class).orElse(null);
+ httpMethod = getOption(verifyParams, "rest.method", String.class);
+
+ String path = getOption(verifyParams, "rest.path", String.class).map(FileUtil::stripLeadingSeparator).orElse(null);
+ if (ObjectHelper.isNotEmpty(httpUri) && ObjectHelper.isNotEmpty(path)) {
+ httpUri = httpUri + "/" + path;
+ }
+
+ verifyParams.put("httpURI", httpUri);
+
+ // Cleanup parameters from rest related stuffs
+ verifyParams.entrySet().removeIf(e -> e.getKey().startsWith("rest."));
+ }
+
+ httpUri = getOption(verifyParams, "httpURI", String.class).orElse(null);
+ httpMethod = Optional.empty();
+
+ // Check whether the http uri is null or empty
+ if (ObjectHelper.isEmpty(httpUri)) {
+ builder.error(
+ ResultErrorBuilder.withMissingOption("httpURI")
+ .detail("rest", isRest)
+ .build()
+ );
+
+ // lack of httpURI is a blocking issue so no need to go further
+ // with the validation
+ return builder.build();
+ }
+
+ try {
+ final UndertowClientWrapper wrapper = new UndertowClientWrapper();
+ final ClientResponse response = wrapper.send(httpUri, httpMethod);
+
+ if (response != null) {
+ int code = response.getResponseCode();
+ if (code == 401) {
+ // Unauthorized, add authUsername and authPassword to the list
+ // of parameters in error
+ builder.error(
+ ResultErrorBuilder.withHttpCode(code)
+ .description(response.getStatus())
+ .build()
+ );
+ } else if (code >= 300 && code < 400) {
+ // redirect
+ builder.error(
+ ResultErrorBuilder.withHttpCode(code)
+ .description(response.getStatus())
+ .parameterKey("httpURI")
+ .detail(
+ VerificationError.HttpAttribute.HTTP_REDIRECT,
+ () -> Optional.ofNullable(response.getResponseHeaders().get(Headers.LOCATION).getFirst()))
+ .build()
+ );
+ } else if (code >= 400) {
+ // generic http error
+ builder.error(
+ ResultErrorBuilder.withHttpCode(code)
+ .description(response.getStatus())
+ .build()
+ );
+ }
+ }
+
+ } catch (Exception e) {
+ builder.error(
+ ResultErrorBuilder.withException(e).build()
+ );
+ }
+
+ return builder.build();
+ }
+
+ // *********************************
+ // Helpers
+ // *********************************
+
+ private final class UndertowClientWrapper {
+ private final XnioWorker worker;
+ private final ByteBufferPool pool;
+ private UndertowClient client;
+
+ private UndertowClientWrapper() throws IOException, URISyntaxException {
+ this.worker = Xnio.getInstance().createWorker(OptionMap.EMPTY);
+ this.pool = new DefaultByteBufferPool(true, 17 * 1024);
+ this.client = UndertowClient.getInstance(getCamelContext().getApplicationContextClassLoader());
+ }
+
+ public ClientResponse send(String httpUri, Optional<String> httpMethod) throws Exception {
+ URI uri = new URI(UnsafeUriCharactersEncoder.encodeHttpURI(httpUri));
+ HttpString method = httpMethod.map(Methods::fromString).orElse(Methods.GET);
+
+ ClientRequest request = new ClientRequest();
+ request.setMethod(method);
+ request.setPath(URISupport.pathAndQueryOf(uri));
+
+ IoFuture<ClientConnection> connectFuture = client.connect(uri, worker, pool, OptionMap.EMPTY);
+ UndertowClientResponseFuture responseFuture = new UndertowClientResponseFuture();
+
+ connectFuture.get().sendRequest(request, responseFuture);
+
+ // We should set a timeout
+ return responseFuture.get().getResponse();
+ }
+ }
+
+ private static final class UndertowClientResponseFuture extends AbstractIoFuture<ClientExchange> implements ClientCallback<ClientExchange> {
+ @Override
+ public void completed(ClientExchange result) {
+ result.setResponseListener(new ClientCallback<ClientExchange>() {
+ @Override
+ public void completed(ClientExchange result) {
+ setResult(result);
+ }
+ @Override
+ public void failed(IOException e) {
+ setException(e);
+ }
+ });
+ }
+
+ @Override
+ public void failed(IOException e) {
+ setException(e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/1c6cf329/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/UndertowComponentVerifierTest.java
----------------------------------------------------------------------
diff --git a/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/UndertowComponentVerifierTest.java b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/UndertowComponentVerifierTest.java
new file mode 100644
index 0000000..4dc3586
--- /dev/null
+++ b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/UndertowComponentVerifierTest.java
@@ -0,0 +1,103 @@
+/**
+ * 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.undertow;
+
+import java.nio.channels.UnresolvedAddressException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.ComponentVerifier;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class UndertowComponentVerifierTest extends BaseUndertowTest {
+ @Test
+ public void testParameters() throws Exception {
+ UndertowComponent component = context().getComponent("undertow", UndertowComponent.class);
+ ComponentVerifier verifier = component.getVerifier();
+
+ Map<String, Object> parameters = new HashMap<>();
+ parameters.put("httpURI", "http://localhost:" + getPort());
+ parameters.put("tcpNoDelay", "true");
+
+ ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.PARAMETERS, parameters);
+
+ Assert.assertEquals(ComponentVerifier.Result.Status.OK, result.getStatus());
+ }
+
+ @Test
+ public void testMissingParameters() throws Exception {
+ UndertowComponent component = context.getComponent("undertow", UndertowComponent.class);
+ ComponentVerifier verifier = component.getVerifier();
+
+ Map<String, Object> parameters = new HashMap<>();
+ parameters.put("tcpNoDelay", "true");
+
+ ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.PARAMETERS, parameters);
+
+ Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+ Assert.assertEquals(1, result.getErrors().size());
+ Assert.assertEquals(ComponentVerifier.VerificationError.StandardCode.MISSING_PARAMETER, result.getErrors().get(0).getCode());
+ Assert.assertEquals(1, result.getErrors().get(0).getParameterKeys().size());
+ Assert.assertTrue(result.getErrors().get(0).getParameterKeys().contains("httpURI"));
+ }
+
+ @Test
+ public void testConnectivity() throws Exception {
+ UndertowComponent component = context().getComponent("undertow", UndertowComponent.class);
+ ComponentVerifier verifier = component.getVerifier();
+
+ Map<String, Object> parameters = new HashMap<>();
+ parameters.put("httpURI", "http://localhost:" + getPort());
+ parameters.put("tcpNoDelay", "true");
+
+ ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+ Assert.assertEquals(ComponentVerifier.Result.Status.OK, result.getStatus());
+ }
+
+ @Test
+ public void testConnectivityError() throws Exception {
+ UndertowComponent component = context().getComponent("undertow", UndertowComponent.class);
+ ComponentVerifier verifier = component.getVerifier();
+
+ Map<String, Object> parameters = new HashMap<>();
+ parameters.put("httpURI", "http://no-host:" + getPort());
+
+ ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+ Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+ Assert.assertEquals(1, result.getErrors().size());
+
+ ComponentVerifier.VerificationError error = result.getErrors().get(0);
+
+ Assert.assertEquals(ComponentVerifier.VerificationError.StandardCode.EXCEPTION, error.getCode());
+ Assert.assertTrue(error.getDetail(ComponentVerifier.VerificationError.ExceptionAttribute.EXCEPTION_INSTANCE) instanceof UnresolvedAddressException);
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("undertow:http://localhost:{{port}}")
+ .process(e -> e.getOut().setBody("ok"));
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/1c6cf329/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/rest/RestUndertowComponentVerifierTest.java
----------------------------------------------------------------------
diff --git a/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/rest/RestUndertowComponentVerifierTest.java b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/rest/RestUndertowComponentVerifierTest.java
new file mode 100644
index 0000000..8108134
--- /dev/null
+++ b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/rest/RestUndertowComponentVerifierTest.java
@@ -0,0 +1,126 @@
+/**
+ * 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.undertow.rest;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.ComponentVerifier;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.rest.RestComponent;
+import org.apache.camel.component.undertow.BaseUndertowTest;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class RestUndertowComponentVerifierTest extends BaseUndertowTest {
+ @Test
+ public void testParameters() throws Exception {
+ RestComponent component = context().getComponent("rest", RestComponent.class);
+ ComponentVerifier verifier = component.getVerifier();
+
+ Map<String, Object> parameters = new HashMap<>();
+ parameters.put("componentName", "undertow");
+ parameters.put("host", "http://localhost:" + getPort());
+ parameters.put("path", "verify");
+ parameters.put("method", "get");
+
+ ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.PARAMETERS, parameters);
+
+ Assert.assertEquals(ComponentVerifier.Result.Status.OK, result.getStatus());
+ }
+
+ @Test
+ public void testMissingRestParameters() throws Exception {
+ RestComponent component = context.getComponent("rest", RestComponent.class);
+ ComponentVerifier verifier = component.getVerifier();
+
+ Map<String, Object> parameters = new HashMap<>();
+ parameters.put("componentName", "undertow");
+ parameters.put("host", "http://localhost:" + getPort());
+ parameters.put("path", "verify");
+
+ // This parameter does not belong to the rest component and validation
+ // is delegated to the transport component
+ parameters.put("tcpNoDelay", true);
+
+ ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.PARAMETERS, parameters);
+
+ Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+ Assert.assertEquals(1, result.getErrors().size());
+ Assert.assertEquals(ComponentVerifier.VerificationError.StandardCode.MISSING_PARAMETER, result.getErrors().get(0).getCode());
+ Assert.assertEquals(1, result.getErrors().get(0).getParameterKeys().size());
+ Assert.assertTrue(result.getErrors().get(0).getParameterKeys().contains("method"));
+ }
+
+ @Test
+ public void testWrongComponentParameters() throws Exception {
+ RestComponent component = context.getComponent("rest", RestComponent.class);
+ ComponentVerifier verifier = component.getVerifier();
+
+ Map<String, Object> parameters = new HashMap<>();
+ parameters.put("componentName", "undertow");
+ parameters.put("host", "http://localhost:" + getPort());
+ parameters.put("path", "verify");
+ parameters.put("method", "get");
+
+ // This parameter does not belong to the rest component and validation
+ // is delegated to the transport component
+ parameters.put("nonExistingOption", true);
+
+ ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.PARAMETERS, parameters);
+
+ Assert.assertEquals(ComponentVerifier.Result.Status.ERROR, result.getStatus());
+ Assert.assertEquals(1, result.getErrors().size());
+ Assert.assertEquals(ComponentVerifier.VerificationError.StandardCode.UNKNOWN_PARAMETER, result.getErrors().get(0).getCode());
+ Assert.assertEquals(1, result.getErrors().get(0).getParameterKeys().size());
+ Assert.assertTrue(result.getErrors().get(0).getParameterKeys().contains("nonExistingOption"));
+ }
+
+ @Test
+ public void testConnectivity() throws Exception {
+ RestComponent component = context().getComponent("rest", RestComponent.class);
+ ComponentVerifier verifier = component.getVerifier();
+
+ Map<String, Object> parameters = new HashMap<>();
+ parameters.put("componentName", "undertow");
+ parameters.put("host", "http://localhost:" + getPort());
+ parameters.put("path", "verify");
+ parameters.put("method", "get");
+
+ ComponentVerifier.Result result = verifier.verify(ComponentVerifier.Scope.CONNECTIVITY, parameters);
+
+ Assert.assertEquals(ComponentVerifier.Result.Status.OK, result.getStatus());
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ restConfiguration()
+ .component("undertow")
+ .host("localhost")
+ .port(getPort());
+
+ rest("/")
+ .get("/verify")
+ .route()
+ .process(e -> e.getOut().setBody("ok"));
+ }
+ };
+ }
+}
|