001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.camel.component.extension.verifier; 018 019import java.util.Map; 020import java.util.Optional; 021import java.util.function.Supplier; 022import java.util.stream.Collectors; 023 024import org.apache.camel.CamelContext; 025import org.apache.camel.CamelContextAware; 026import org.apache.camel.Component; 027import org.apache.camel.ComponentAware; 028import org.apache.camel.ComponentVerifier; 029import org.apache.camel.TypeConverter; 030import org.apache.camel.component.extension.ComponentVerifierExtension; 031import org.apache.camel.runtimecatalog.EndpointValidationResult; 032import org.apache.camel.runtimecatalog.RuntimeCamelCatalog; 033import org.apache.camel.util.CamelContextHelper; 034import org.apache.camel.util.EndpointHelper; 035import org.apache.camel.util.IntrospectionSupport; 036 037import static org.apache.camel.util.StreamUtils.stream; 038 039public class DefaultComponentVerifierExtension implements ComponentVerifierExtension, ComponentVerifier, CamelContextAware, ComponentAware { 040 private final String defaultScheme; 041 private Component component; 042 private CamelContext camelContext; 043 044 protected DefaultComponentVerifierExtension(String defaultScheme) { 045 this(defaultScheme, null, null); 046 } 047 048 protected DefaultComponentVerifierExtension(String defaultScheme, CamelContext camelContext) { 049 this(defaultScheme, camelContext, null); 050 } 051 052 protected DefaultComponentVerifierExtension(String defaultScheme, CamelContext camelContext, Component component) { 053 this.defaultScheme = defaultScheme; 054 this.camelContext = camelContext; 055 this.component = component; 056 } 057 058 // ************************************* 059 // 060 // ************************************* 061 062 @Override 063 public void setCamelContext(CamelContext camelContext) { 064 this.camelContext = camelContext; 065 } 066 067 @Override 068 public CamelContext getCamelContext() { 069 return camelContext; 070 } 071 072 @Override 073 public Component getComponent() { 074 return component; 075 } 076 077 @Override 078 public void setComponent(Component component) { 079 this.component = component; 080 } 081 082 @Override 083 public Result verify(Scope scope, Map<String, Object> parameters) { 084 // Camel context is mandatory 085 if (this.camelContext == null) { 086 return ResultBuilder.withStatusAndScope(Result.Status.ERROR, scope) 087 .error(ResultErrorBuilder.withCodeAndDescription(VerificationError.StandardCode.INTERNAL, "Missing camel-context").build()) 088 .build(); 089 } 090 091 if (scope == Scope.PARAMETERS) { 092 return verifyParameters(parameters); 093 } 094 if (scope == Scope.CONNECTIVITY) { 095 return verifyConnectivity(parameters); 096 } 097 098 return ResultBuilder.unsupportedScope(scope).build(); 099 } 100 101 protected Result verifyConnectivity(Map<String, Object> parameters) { 102 return ResultBuilder.withStatusAndScope(Result.Status.UNSUPPORTED, Scope.CONNECTIVITY).build(); 103 } 104 105 protected Result verifyParameters(Map<String, Object> parameters) { 106 ResultBuilder builder = ResultBuilder.withStatusAndScope(Result.Status.OK, Scope.PARAMETERS); 107 108 // Validate against catalog 109 verifyParametersAgainstCatalog(builder, parameters); 110 111 return builder.build(); 112 } 113 114 // ************************************* 115 // Helpers :: Parameters validation 116 // ************************************* 117 118 protected void verifyParametersAgainstCatalog(ResultBuilder builder, Map<String, Object> parameters) { 119 verifyParametersAgainstCatalog(builder, parameters, new CatalogVerifierCustomizer()); 120 } 121 122 protected void verifyParametersAgainstCatalog(ResultBuilder builder, Map<String, Object> parameters, CatalogVerifierCustomizer customizer) { 123 String scheme = defaultScheme; 124 if (parameters.containsKey("scheme")) { 125 scheme = parameters.get("scheme").toString(); 126 } 127 128 // Grab the runtime catalog to check parameters 129 RuntimeCamelCatalog catalog = camelContext.getRuntimeCamelCatalog(); 130 131 // Convert from Map<String, Object> to Map<String, String> as required 132 // by the Camel Catalog 133 EndpointValidationResult result = catalog.validateProperties( 134 scheme, 135 parameters.entrySet().stream() 136 .collect( 137 Collectors.toMap( 138 Map.Entry::getKey, 139 e -> camelContext.getTypeConverter().convertTo(String.class, e.getValue()) 140 ) 141 ) 142 ); 143 144 if (!result.isSuccess()) { 145 if (customizer.isIncludeUnknown()) { 146 stream(result.getUnknown()) 147 .map(option -> ResultErrorBuilder.withUnknownOption(option).build()) 148 .forEach(builder::error); 149 } 150 if (customizer.isIncludeRequired()) { 151 stream(result.getRequired()) 152 .map(option -> ResultErrorBuilder.withMissingOption(option).build()) 153 .forEach(builder::error); 154 } 155 if (customizer.isIncludeInvalidBoolean()) { 156 stream(result.getInvalidBoolean()) 157 .map(entry -> ResultErrorBuilder.withIllegalOption(entry.getKey(), entry.getValue()).build()) 158 .forEach(builder::error); 159 } 160 if (customizer.isIncludeInvalidInteger()) { 161 stream(result.getInvalidInteger()) 162 .map(entry -> ResultErrorBuilder.withIllegalOption(entry.getKey(), entry.getValue()).build()) 163 .forEach(builder::error); 164 } 165 if (customizer.isIncludeInvalidNumber()) { 166 stream(result.getInvalidNumber()) 167 .map(entry -> ResultErrorBuilder.withIllegalOption(entry.getKey(), entry.getValue()).build()) 168 .forEach(builder::error); 169 } 170 if (customizer.isIncludeInvalidEnum()) { 171 stream(result.getInvalidEnum()) 172 .map(entry -> 173 ResultErrorBuilder.withIllegalOption(entry.getKey(), entry.getValue()) 174 .detail("enum.values", result.getEnumChoices(entry.getKey())) 175 .build()) 176 .forEach(builder::error); 177 } 178 } 179 } 180 181 // ************************************* 182 // Helpers 183 // ************************************* 184 185 186 protected <T> T setProperties(T instance, Map<String, Object> properties) throws Exception { 187 if (camelContext == null) { 188 throw new IllegalStateException("Camel context is null"); 189 } 190 191 if (!properties.isEmpty()) { 192 final TypeConverter converter = camelContext.getTypeConverter(); 193 194 IntrospectionSupport.setProperties(converter, instance, properties); 195 196 for (Map.Entry<String, Object> entry : properties.entrySet()) { 197 if (entry.getValue() instanceof String) { 198 String value = (String)entry.getValue(); 199 if (EndpointHelper.isReferenceParameter(value)) { 200 IntrospectionSupport.setProperty(camelContext, converter, instance, entry.getKey(), null, value, true); 201 } 202 } 203 } 204 } 205 206 return instance; 207 } 208 209 protected <T> T setProperties(T instance, String prefix, Map<String, Object> properties) throws Exception { 210 return setProperties( 211 instance, 212 IntrospectionSupport.extractProperties(properties, prefix, false) 213 ); 214 } 215 216 protected <T> Optional<T> getOption(Map<String, Object> parameters, String key, Class<T> type) { 217 Object value = parameters.get(key); 218 if (value != null) { 219 return Optional.ofNullable(CamelContextHelper.convertTo(camelContext, type, value)); 220 } 221 222 return Optional.empty(); 223 } 224 225 protected <T> T getOption(Map<String, Object> parameters, String key, Class<T> type, Supplier<T> defaultSupplier) { 226 return getOption(parameters, key, type).orElseGet(defaultSupplier); 227 } 228 229 protected <T> T getMandatoryOption(Map<String, Object> parameters, String key, Class<T> type) throws NoSuchOptionException { 230 return getOption(parameters, key, type).orElseThrow(() -> new NoSuchOptionException(key)); 231 } 232}