http://git-wip-us.apache.org/repos/asf/camel/blob/c66be7a8/camel-core/src/main/java/org/apache/camel/runtimecatalog/JSonSchemaHelper.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/runtimecatalog/JSonSchemaHelper.java b/camel-core/src/main/java/org/apache/camel/runtimecatalog/JSonSchemaHelper.java new file mode 100644 index 0000000..1e69269 --- /dev/null +++ b/camel-core/src/main/java/org/apache/camel/runtimecatalog/JSonSchemaHelper.java @@ -0,0 +1,424 @@ +/** + * 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.runtimecatalog; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class JSonSchemaHelper { + + // 0 = text, 1 = enum, 2 = boolean, 3 = integer or number + private static final Pattern PATTERN = Pattern.compile("\"(.+?)\"|\\[(.+)\\]|(true|false)|(-?\\d+\\.?\\d*)"); + private static final String QUOT = """; + + private JSonSchemaHelper() { + } + + /** + * Parses the json schema to split it into a list or rows, where each row contains key value pairs with the metadata + * + * @param group the group to parse from such as <tt>component</tt>, <tt>componentProperties</tt>, or <tt>properties</tt>. + * @param json the json + * @return a list of all the rows, where each row is a set of key value pairs with metadata + */ + public static List<Map<String, String>> parseJsonSchema(String group, String json, boolean parseProperties) { + List<Map<String, String>> answer = new ArrayList<Map<String, String>>(); + if (json == null) { + return answer; + } + + boolean found = false; + + // parse line by line + String[] lines = json.split("\n"); + for (String line : lines) { + // we need to find the group first + if (!found) { + String s = line.trim(); + found = s.startsWith("\"" + group + "\":") && s.endsWith("{"); + continue; + } + + // we should stop when we end the group + if (line.equals(" },") || line.equals(" }")) { + break; + } + + // need to safe encode \" so we can parse the line + line = line.replaceAll("\"\\\\\"\"", '"' + QUOT + '"'); + + Map<String, String> row = new LinkedHashMap<String, String>(); + Matcher matcher = PATTERN.matcher(line); + + String key; + if (parseProperties) { + // when parsing properties the first key is given as name, so the first parsed token is the value of the name + key = "name"; + } else { + key = null; + } + while (matcher.find()) { + if (key == null) { + key = matcher.group(1); + } else { + String value = matcher.group(1); + if (value != null) { + // its text based + value = value.trim(); + // decode + value = value.replaceAll(QUOT, "\""); + value = decodeJson(value); + } + if (value == null) { + // not text then its maybe an enum? + value = matcher.group(2); + if (value != null) { + // its an enum so strip out " and trim spaces after comma + value = value.replaceAll("\"", ""); + value = value.replaceAll(", ", ","); + value = value.trim(); + } + } + if (value == null) { + // not text then its maybe a boolean? + value = matcher.group(3); + } + if (value == null) { + // not text then its maybe a integer? + value = matcher.group(4); + } + if (value != null) { + row.put(key, value); + } + // reset + key = null; + } + } + if (!row.isEmpty()) { + answer.add(row); + } + } + + return answer; + } + + private static String decodeJson(String value) { + // json encodes a \ as \\ so we need to decode from \\ back to \ + if ("\\\\".equals(value)) { + value = "\\"; + } + return value; + } + + public static boolean isComponentLenientProperties(List<Map<String, String>> rows) { + for (Map<String, String> row : rows) { + if (row.containsKey("lenientProperties")) { + return "true".equals(row.get("lenientProperties")); + } + } + return false; + } + + public static boolean isComponentConsumerOnly(List<Map<String, String>> rows) { + for (Map<String, String> row : rows) { + if (row.containsKey("consumerOnly")) { + return "true".equals(row.get("consumerOnly")); + } + } + return false; + } + + public static boolean isComponentProducerOnly(List<Map<String, String>> rows) { + for (Map<String, String> row : rows) { + if (row.containsKey("producerOnly")) { + return "true".equals(row.get("producerOnly")); + } + } + return false; + } + + public static boolean isPropertyConsumerOnly(List<Map<String, String>> rows, String name) { + for (Map<String, String> row : rows) { + String labels = null; + boolean found = false; + if (row.containsKey("name")) { + found = name.equals(row.get("name")); + } + if (row.containsKey("label")) { + labels = row.get("label"); + } + if (found) { + return labels != null && labels.contains("consumer"); + } + } + return false; + } + + public static boolean isPropertyProducerOnly(List<Map<String, String>> rows, String name) { + for (Map<String, String> row : rows) { + String labels = null; + boolean found = false; + if (row.containsKey("name")) { + found = name.equals(row.get("name")); + } + if (row.containsKey("label")) { + labels = row.get("label"); + } + if (found) { + return labels != null && labels.contains("producer"); + } + } + return false; + } + + public static boolean isPropertyRequired(List<Map<String, String>> rows, String name) { + for (Map<String, String> row : rows) { + boolean required = false; + boolean found = false; + if (row.containsKey("name")) { + found = name.equals(row.get("name")); + } + if (row.containsKey("required")) { + required = "true".equals(row.get("required")); + } + if (found) { + return required; + } + } + return false; + } + + public static String getPropertyKind(List<Map<String, String>> rows, String name) { + for (Map<String, String> row : rows) { + String kind = null; + boolean found = false; + if (row.containsKey("name")) { + found = name.equals(row.get("name")); + } + if (row.containsKey("kind")) { + kind = row.get("kind"); + } + if (found) { + return kind; + } + } + return null; + } + + public static boolean isPropertyBoolean(List<Map<String, String>> rows, String name) { + for (Map<String, String> row : rows) { + String type = null; + boolean found = false; + if (row.containsKey("name")) { + found = name.equals(row.get("name")); + } + if (row.containsKey("type")) { + type = row.get("type"); + } + if (found) { + return "boolean".equals(type); + } + } + return false; + } + + public static boolean isPropertyInteger(List<Map<String, String>> rows, String name) { + for (Map<String, String> row : rows) { + String type = null; + boolean found = false; + if (row.containsKey("name")) { + found = name.equals(row.get("name")); + } + if (row.containsKey("type")) { + type = row.get("type"); + } + if (found) { + return "integer".equals(type); + } + } + return false; + } + + public static boolean isPropertyNumber(List<Map<String, String>> rows, String name) { + for (Map<String, String> row : rows) { + String type = null; + boolean found = false; + if (row.containsKey("name")) { + found = name.equals(row.get("name")); + } + if (row.containsKey("type")) { + type = row.get("type"); + } + if (found) { + return "number".equals(type); + } + } + return false; + } + + public static boolean isPropertyObject(List<Map<String, String>> rows, String name) { + for (Map<String, String> row : rows) { + String type = null; + boolean found = false; + if (row.containsKey("name")) { + found = name.equals(row.get("name")); + } + if (row.containsKey("type")) { + type = row.get("type"); + } + if (found) { + return "object".equals(type); + } + } + return false; + } + + public static String getPropertyDefaultValue(List<Map<String, String>> rows, String name) { + for (Map<String, String> row : rows) { + String defaultValue = null; + boolean found = false; + if (row.containsKey("name")) { + found = name.equals(row.get("name")); + } + if (row.containsKey("defaultValue")) { + defaultValue = row.get("defaultValue"); + } + if (found) { + return defaultValue; + } + } + return null; + } + + public static String stripOptionalPrefixFromName(List<Map<String, String>> rows, String name) { + for (Map<String, String> row : rows) { + String optionalPrefix = null; + boolean found = false; + if (row.containsKey("optionalPrefix")) { + optionalPrefix = row.get("optionalPrefix"); + } + if (row.containsKey("name")) { + if (optionalPrefix != null && name.startsWith(optionalPrefix)) { + name = name.substring(optionalPrefix.length()); + // try again + return stripOptionalPrefixFromName(rows, name); + } else { + found = name.equals(row.get("name")); + } + } + if (found) { + return name; + } + } + return name; + } + + public static String getPropertyEnum(List<Map<String, String>> rows, String name) { + for (Map<String, String> row : rows) { + String enums = null; + boolean found = false; + if (row.containsKey("name")) { + found = name.equals(row.get("name")); + } + if (row.containsKey("enum")) { + enums = row.get("enum"); + } + if (found) { + return enums; + } + } + return null; + } + + public static String getPropertyPrefix(List<Map<String, String>> rows, String name) { + for (Map<String, String> row : rows) { + String prefix = null; + boolean found = false; + if (row.containsKey("name")) { + found = name.equals(row.get("name")); + } + if (row.containsKey("prefix")) { + prefix = row.get("prefix"); + } + if (found) { + return prefix; + } + } + return null; + } + + public static boolean isPropertyMultiValue(List<Map<String, String>> rows, String name) { + for (Map<String, String> row : rows) { + boolean multiValue = false; + boolean found = false; + if (row.containsKey("name")) { + found = name.equals(row.get("name")); + } + if (row.containsKey("multiValue")) { + multiValue = "true".equals(row.get("multiValue")); + } + if (found) { + return multiValue; + } + } + return false; + } + + public static String getPropertyNameFromNameWithPrefix(List<Map<String, String>> rows, String name) { + for (Map<String, String> row : rows) { + String propertyName = null; + boolean found = false; + if (row.containsKey("name")) { + propertyName = row.get("name"); + } + if (row.containsKey("prefix")) { + String preifx = row.get("prefix"); + found = name.startsWith(preifx); + } + if (found) { + return propertyName; + } + } + return null; + } + + public static Map<String, String> getRow(List<Map<String, String>> rows, String key) { + for (Map<String, String> row : rows) { + if (key.equals(row.get("name"))) { + return row; + } + } + return null; + } + + public static Set<String> getNames(List<Map<String, String>> rows) { + Set<String> answer = new LinkedHashSet<String>(); + for (Map<String, String> row : rows) { + if (row.containsKey("name")) { + answer.add(row.get("name")); + } + } + return answer; + } + +}
http://git-wip-us.apache.org/repos/asf/camel/blob/c66be7a8/camel-core/src/main/java/org/apache/camel/runtimecatalog/JSonSchemaResolver.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/runtimecatalog/JSonSchemaResolver.java b/camel-core/src/main/java/org/apache/camel/runtimecatalog/JSonSchemaResolver.java new file mode 100644 index 0000000..dbd6f45 --- /dev/null +++ b/camel-core/src/main/java/org/apache/camel/runtimecatalog/JSonSchemaResolver.java @@ -0,0 +1,64 @@ +/** + * 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.runtimecatalog; + +/** + * Pluggable resolver to load JSon schema files for components, data formats, languages etc. + */ +public interface JSonSchemaResolver { + + /** + * Returns the component information as JSon format. + * + * @param name the component name + * @return component details in JSon + */ + String getComponentJSonSchema(String name); + + /** + * Returns the data format information as JSon format. + * + * @param name the data format name + * @return data format details in JSon + */ + String getDataFormatJSonSchema(String name); + + /** + * Returns the language information as JSon format. + * + * @param name the language name + * @return language details in JSon + */ + String getLanguageJSonSchema(String name); + + /** + * Returns the other (miscellaneous) information as JSon format. + * + * @param name the other (miscellaneous) name + * @return other (miscellaneous) details in JSon + */ + String getOtherJSonSchema(String name); + + /** + * Returns the model information as JSon format. + * + * @param name the model name + * @return model details in JSon + */ + String getModelJSonSchema(String name); + +} http://git-wip-us.apache.org/repos/asf/camel/blob/c66be7a8/camel-core/src/main/java/org/apache/camel/runtimecatalog/LanguageValidationResult.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/runtimecatalog/LanguageValidationResult.java b/camel-core/src/main/java/org/apache/camel/runtimecatalog/LanguageValidationResult.java new file mode 100644 index 0000000..dbb5525 --- /dev/null +++ b/camel-core/src/main/java/org/apache/camel/runtimecatalog/LanguageValidationResult.java @@ -0,0 +1,65 @@ +/** + * 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.runtimecatalog; + +import java.io.Serializable; + +/** + * Validation result of parsing a language expression or predicate + */ +public class LanguageValidationResult implements Serializable { + private final String text; + private String error; + private String shortError; + private int index; + + public LanguageValidationResult(String text) { + this.text = text; + } + + public String getText() { + return text; + } + + public boolean isSuccess() { + return error == null; + } + + public void setError(String error) { + this.error = error; + } + + public String getError() { + return error; + } + + public String getShortError() { + return shortError; + } + + public void setShortError(String shortError) { + this.shortError = shortError; + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/c66be7a8/camel-core/src/main/java/org/apache/camel/runtimecatalog/RuntimeCamelCatalog.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/runtimecatalog/RuntimeCamelCatalog.java b/camel-core/src/main/java/org/apache/camel/runtimecatalog/RuntimeCamelCatalog.java new file mode 100644 index 0000000..86c4b53 --- /dev/null +++ b/camel-core/src/main/java/org/apache/camel/runtimecatalog/RuntimeCamelCatalog.java @@ -0,0 +1,234 @@ +/** + * 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.runtimecatalog; + +import java.net.URISyntaxException; +import java.util.Map; + +import org.apache.camel.StaticService; + +/** + * Runtime based CamelCatalog which are included in camel-core that can provided limit CamelCatalog capabilities + */ +public interface RuntimeCamelCatalog extends StaticService { + + /** + * Returns the component information as JSon format. + * + * @param name the component name + * @return component details in JSon + */ + String componentJSonSchema(String name); + + /** + * Returns the data format information as JSon format. + * + * @param name the data format name + * @return data format details in JSon + */ + String dataFormatJSonSchema(String name); + + /** + * Returns the language information as JSon format. + * + * @param name the language name + * @return language details in JSon + */ + String languageJSonSchema(String name); + + /** + * Returns the model information as JSon format. + * + * @param name the model name + * @return model details in JSon + */ + String modelJSonSchema(String name); + + /** + * Parses the endpoint uri and constructs a key/value properties of each option + * + * @param uri the endpoint uri + * @return properties as key value pairs of each endpoint option + */ + Map<String, String> endpointProperties(String uri) throws URISyntaxException; + + /** + * Parses the endpoint uri and constructs a key/value properties of only the lenient properties (eg custom options) + * <p/> + * For example using the HTTP components to provide query parameters in the endpoint uri. + * + * @param uri the endpoint uri + * @return properties as key value pairs of each lenient properties + */ + Map<String, String> endpointLenientProperties(String uri) throws URISyntaxException; + + /** + * Validates the pattern whether its a valid time pattern. + * + * @param pattern the pattern such as 5000, 5s, 5sec, 4min, 4m30s, 1h, etc. + * @return <tt>true</tt> if valid, <tt>false</tt> if invalid + */ + boolean validateTimePattern(String pattern); + + /** + * Validates the properties for the given scheme against component and endpoint + * + * @param scheme the endpoint scheme + * @param properties the endpoint properties + * @return validation result + */ + EndpointValidationResult validateProperties(String scheme, Map<String, String> properties); + + /** + * Parses and validates the endpoint uri and constructs a key/value properties of each option. + * + * @param uri the endpoint uri + * @return validation result + */ + EndpointValidationResult validateEndpointProperties(String uri); + + /** + * Parses and validates the endpoint uri and constructs a key/value properties of each option. + * <p/> + * The option ignoreLenientProperties can be used to ignore components that uses lenient properties. + * When this is true, then the uri validation is stricter but would fail on properties that are not part of the component + * but in the uri because of using lenient properties. + * For example using the HTTP components to provide query parameters in the endpoint uri. + * + * @param uri the endpoint uri + * @param ignoreLenientProperties whether to ignore components that uses lenient properties. + * @return validation result + */ + EndpointValidationResult validateEndpointProperties(String uri, boolean ignoreLenientProperties); + + /** + * Parses and validates the endpoint uri and constructs a key/value properties of each option. + * <p/> + * The option ignoreLenientProperties can be used to ignore components that uses lenient properties. + * When this is true, then the uri validation is stricter but would fail on properties that are not part of the component + * but in the uri because of using lenient properties. + * For example using the HTTP components to provide query parameters in the endpoint uri. + * + * @param uri the endpoint uri + * @param ignoreLenientProperties whether to ignore components that uses lenient properties. + * @param consumerOnly whether the endpoint is only used as a consumer + * @param producerOnly whether the endpoint is only used as a producer + * @return validation result + */ + EndpointValidationResult validateEndpointProperties(String uri, boolean ignoreLenientProperties, boolean consumerOnly, boolean producerOnly); + + /** + * Parses and validates the simple expression. + * <p/> + * <b>Important:</b> This requires having <tt>camel-core</tt> on the classpath + * + * @param simple the simple expression + * @return validation result + * @deprecated use {@link #validateSimpleExpression(ClassLoader, String)} + */ + @Deprecated + SimpleValidationResult validateSimpleExpression(String simple); + + /** + * Parses and validates the simple expression. + * <p/> + * <b>Important:</b> This requires having <tt>camel-core</tt> on the classpath + * + * @param classLoader a custom classloader to use for loading the language from the classpath, or <tt>null</tt> for using default classloader + * @param simple the simple expression + * @return validation result + */ + SimpleValidationResult validateSimpleExpression(ClassLoader classLoader, String simple); + + /** + * Parses and validates the simple predicate + * <p/> + * <b>Important:</b> This requires having <tt>camel-core</tt> on the classpath + * + * @param simple the simple predicate + * @return validation result + * @deprecated use {@link #validateSimplePredicate(ClassLoader, String)} + */ + @Deprecated + SimpleValidationResult validateSimplePredicate(String simple); + + /** + * Parses and validates the simple predicate + * <p/> + * <b>Important:</b> This requires having <tt>camel-core</tt> on the classpath + * + * @param classLoader a custom classloader to use for loading the language from the classpath, or <tt>null</tt> for using default classloader + * @param simple the simple predicate + * @return validation result + */ + SimpleValidationResult validateSimplePredicate(ClassLoader classLoader, String simple); + + /** + * Parses and validates the language as a predicate + * <p/> + * <b>Important:</b> This requires having <tt>camel-core</tt> and the language dependencies on the classpath + * + * @param classLoader a custom classloader to use for loading the language from the classpath, or <tt>null</tt> for using default classloader + * @param language the name of the language + * @param text the predicate text + * @return validation result + */ + LanguageValidationResult validateLanguagePredicate(ClassLoader classLoader, String language, String text); + + /** + * Parses and validates the language as an expression + * <p/> + * <b>Important:</b> This requires having <tt>camel-core</tt> and the language dependencies on the classpath + * + * @param classLoader a custom classloader to use for loading the language from the classpath, or <tt>null</tt> for using default classloader + * @param language the name of the language + * @param text the expression text + * @return validation result + */ + LanguageValidationResult validateLanguageExpression(ClassLoader classLoader, String language, String text); + + /** + * Returns the component name from the given endpoint uri + * + * @param uri the endpoint uri + * @return the component name (aka scheme), or <tt>null</tt> if not possible to determine + */ + String endpointComponentName(String uri); + + /** + * Creates an endpoint uri in Java style from the information from the properties + * + * @param scheme the endpoint schema + * @param properties the properties as key value pairs + * @param encode whether to URL encode the returned uri or not + * @return the constructed endpoint uri + * @throws java.net.URISyntaxException is thrown if there is encoding error + */ + String asEndpointUri(String scheme, Map<String, String> properties, boolean encode) throws URISyntaxException; + + /** + * Creates an endpoint uri in XML style (eg escape & as &l;) from the information from the properties + * + * @param scheme the endpoint schema + * @param properties the properties as key value pairs + * @param encode whether to URL encode the returned uri or not + * @return the constructed endpoint uri + * @throws java.net.URISyntaxException is thrown if there is encoding error + */ + String asEndpointUriXml(String scheme, Map<String, String> properties, boolean encode) throws URISyntaxException; + +} http://git-wip-us.apache.org/repos/asf/camel/blob/c66be7a8/camel-core/src/main/java/org/apache/camel/runtimecatalog/SimpleValidationResult.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/runtimecatalog/SimpleValidationResult.java b/camel-core/src/main/java/org/apache/camel/runtimecatalog/SimpleValidationResult.java new file mode 100644 index 0000000..1b8dd0e --- /dev/null +++ b/camel-core/src/main/java/org/apache/camel/runtimecatalog/SimpleValidationResult.java @@ -0,0 +1,32 @@ +/** + * 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.runtimecatalog; + +/** + * To be backwards compatible, but favor using {@link LanguageValidationResult} instead. + */ +public class SimpleValidationResult extends LanguageValidationResult { + + public SimpleValidationResult(String text) { + super(text); + } + + public String getSimple() { + return getText(); + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/c66be7a8/camel-core/src/main/java/org/apache/camel/runtimecatalog/SuggestionStrategy.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/runtimecatalog/SuggestionStrategy.java b/camel-core/src/main/java/org/apache/camel/runtimecatalog/SuggestionStrategy.java new file mode 100644 index 0000000..e29afc6 --- /dev/null +++ b/camel-core/src/main/java/org/apache/camel/runtimecatalog/SuggestionStrategy.java @@ -0,0 +1,34 @@ +/** + * 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.runtimecatalog; + +import java.util.Set; + +/** + * Strategy to provide suggestions for unknown endpoint options + */ +public interface SuggestionStrategy { + + /** + * Provides a list of valid option names for a did you mean function. + * + * @param names valid names + * @param unknownOption unknown option name + * @return a list of suggested names (did you mean) + */ + String[] suggestEndpointOptions(Set<String> names, String unknownOption); +} http://git-wip-us.apache.org/repos/asf/camel/blob/c66be7a8/camel-core/src/main/java/org/apache/camel/runtimecatalog/TimePatternConverter.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/runtimecatalog/TimePatternConverter.java b/camel-core/src/main/java/org/apache/camel/runtimecatalog/TimePatternConverter.java new file mode 100644 index 0000000..ed5585c --- /dev/null +++ b/camel-core/src/main/java/org/apache/camel/runtimecatalog/TimePatternConverter.java @@ -0,0 +1,120 @@ +/** + * 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.runtimecatalog; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * This class is a copy from camel-core so we can use it independent to validate uris with time patterns + */ +public final class TimePatternConverter { + private static final Pattern NUMBERS_ONLY_STRING_PATTERN = Pattern.compile("^[-]?(\\d)+$", Pattern.CASE_INSENSITIVE); + private static final Pattern HOUR_REGEX_PATTERN = Pattern.compile("((\\d)*(\\d))h(our(s)?)?", Pattern.CASE_INSENSITIVE); + private static final Pattern MINUTES_REGEX_PATTERN = Pattern.compile("((\\d)*(\\d))m(in(ute(s)?)?)?", Pattern.CASE_INSENSITIVE); + private static final Pattern SECONDS_REGEX_PATTERN = Pattern.compile("((\\d)*(\\d))s(ec(ond)?(s)?)?", Pattern.CASE_INSENSITIVE); + + /** + * Utility classes should not have a public constructor. + */ + private TimePatternConverter() { + } + + public static long toMilliSeconds(String source) throws IllegalArgumentException { + long milliseconds = 0; + boolean foundFlag = false; + + checkCorrectnessOfPattern(source); + Matcher matcher; + + matcher = createMatcher(NUMBERS_ONLY_STRING_PATTERN, source); + if (matcher.find()) { + // Note: This will also be used for regular numeric strings. + // This String -> long converter will be used for all strings. + milliseconds = Long.valueOf(source); + } else { + matcher = createMatcher(HOUR_REGEX_PATTERN, source); + if (matcher.find()) { + milliseconds = milliseconds + (3600000 * Long.valueOf(matcher.group(1))); + foundFlag = true; + } + + matcher = createMatcher(MINUTES_REGEX_PATTERN, source); + if (matcher.find()) { + long minutes = Long.valueOf(matcher.group(1)); + if ((minutes > 59) && foundFlag) { + throw new IllegalArgumentException("Minutes should contain a valid value between 0 and 59: " + source); + } + foundFlag = true; + milliseconds = milliseconds + (60000 * minutes); + } + + matcher = createMatcher(SECONDS_REGEX_PATTERN, source); + if (matcher.find()) { + long seconds = Long.valueOf(matcher.group(1)); + if ((seconds > 59) && foundFlag) { + throw new IllegalArgumentException("Seconds should contain a valid value between 0 and 59: " + source); + } + foundFlag = true; + milliseconds = milliseconds + (1000 * seconds); + } + + // No pattern matched... initiating fallback check and conversion (if required). + // The source at this point may contain illegal values or special characters + if (!foundFlag) { + milliseconds = Long.valueOf(source); + } + } + + return milliseconds; + } + + private static void checkCorrectnessOfPattern(String source) { + //replace only numbers once + Matcher matcher = createMatcher(NUMBERS_ONLY_STRING_PATTERN, source); + String replaceSource = matcher.replaceFirst(""); + + //replace hour string once + matcher = createMatcher(HOUR_REGEX_PATTERN, replaceSource); + if (matcher.find() && matcher.find()) { + throw new IllegalArgumentException("Hours should not be specified more then once: " + source); + } + replaceSource = matcher.replaceFirst(""); + + //replace minutes once + matcher = createMatcher(MINUTES_REGEX_PATTERN, replaceSource); + if (matcher.find() && matcher.find()) { + throw new IllegalArgumentException("Minutes should not be specified more then once: " + source); + } + replaceSource = matcher.replaceFirst(""); + + //replace seconds once + matcher = createMatcher(SECONDS_REGEX_PATTERN, replaceSource); + if (matcher.find() && matcher.find()) { + throw new IllegalArgumentException("Seconds should not be specified more then once: " + source); + } + replaceSource = matcher.replaceFirst(""); + + if (replaceSource.length() > 0) { + throw new IllegalArgumentException("Illegal characters: " + source); + } + } + + private static Matcher createMatcher(Pattern pattern, String source) { + return pattern.matcher(source); + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/c66be7a8/camel-core/src/main/java/org/apache/camel/runtimecatalog/URISupport.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/runtimecatalog/URISupport.java b/camel-core/src/main/java/org/apache/camel/runtimecatalog/URISupport.java new file mode 100644 index 0000000..8389590 --- /dev/null +++ b/camel-core/src/main/java/org/apache/camel/runtimecatalog/URISupport.java @@ -0,0 +1,392 @@ +/** + * 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.runtimecatalog; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Copied from org.apache.camel.util.URISupport + */ +public final class URISupport { + + public static final String RAW_TOKEN_START = "RAW("; + public static final String RAW_TOKEN_END = ")"; + + private static final String CHARSET = "UTF-8"; + + private URISupport() { + // Helper class + } + + /** + * Normalizes the URI so unsafe characters is encoded + * + * @param uri the input uri + * @return as URI instance + * @throws URISyntaxException is thrown if syntax error in the input uri + */ + public static URI normalizeUri(String uri) throws URISyntaxException { + return new URI(UnsafeUriCharactersEncoder.encode(uri, true)); + } + + public static Map<String, Object> extractProperties(Map<String, Object> properties, String optionPrefix) { + Map<String, Object> rc = new LinkedHashMap<String, Object>(properties.size()); + + for (Iterator<Map.Entry<String, Object>> it = properties.entrySet().iterator(); it.hasNext();) { + Map.Entry<String, Object> entry = it.next(); + String name = entry.getKey(); + if (name.startsWith(optionPrefix)) { + Object value = properties.get(name); + name = name.substring(optionPrefix.length()); + rc.put(name, value); + it.remove(); + } + } + + return rc; + } + + /** + * Strips the query parameters from the uri + * + * @param uri the uri + * @return the uri without the query parameter + */ + public static String stripQuery(String uri) { + int idx = uri.indexOf('?'); + if (idx > -1) { + uri = uri.substring(0, idx); + } + return uri; + } + + /** + * Parses the query parameters of the uri (eg the query part). + * + * @param uri the uri + * @return the parameters, or an empty map if no parameters (eg never null) + * @throws URISyntaxException is thrown if uri has invalid syntax. + */ + public static Map<String, Object> parseParameters(URI uri) throws URISyntaxException { + String query = uri.getQuery(); + if (query == null) { + String schemeSpecificPart = uri.getSchemeSpecificPart(); + int idx = schemeSpecificPart.indexOf('?'); + if (idx < 0) { + // return an empty map + return new LinkedHashMap<String, Object>(0); + } else { + query = schemeSpecificPart.substring(idx + 1); + } + } else { + query = stripPrefix(query, "?"); + } + return parseQuery(query); + } + + /** + * Strips the prefix from the value. + * <p/> + * Returns the value as-is if not starting with the prefix. + * + * @param value the value + * @param prefix the prefix to remove from value + * @return the value without the prefix + */ + public static String stripPrefix(String value, String prefix) { + if (value != null && value.startsWith(prefix)) { + return value.substring(prefix.length()); + } + return value; + } + + /** + * Parses the query part of the uri (eg the parameters). + * <p/> + * The URI parameters will by default be URI encoded. However you can define a parameter + * values with the syntax: <tt>key=RAW(value)</tt> which tells Camel to not encode the value, + * and use the value as is (eg key=value) and the value has <b>not</b> been encoded. + * + * @param uri the uri + * @return the parameters, or an empty map if no parameters (eg never null) + * @throws URISyntaxException is thrown if uri has invalid syntax. + * @see #RAW_TOKEN_START + * @see #RAW_TOKEN_END + */ + public static Map<String, Object> parseQuery(String uri) throws URISyntaxException { + return parseQuery(uri, false); + } + + /** + * Parses the query part of the uri (eg the parameters). + * <p/> + * The URI parameters will by default be URI encoded. However you can define a parameter + * values with the syntax: <tt>key=RAW(value)</tt> which tells Camel to not encode the value, + * and use the value as is (eg key=value) and the value has <b>not</b> been encoded. + * + * @param uri the uri + * @param useRaw whether to force using raw values + * @return the parameters, or an empty map if no parameters (eg never null) + * @throws URISyntaxException is thrown if uri has invalid syntax. + * @see #RAW_TOKEN_START + * @see #RAW_TOKEN_END + */ + public static Map<String, Object> parseQuery(String uri, boolean useRaw) throws URISyntaxException { + // must check for trailing & as the uri.split("&") will ignore those + if (uri != null && uri.endsWith("&")) { + throw new URISyntaxException(uri, "Invalid uri syntax: Trailing & marker found. " + + "Check the uri and remove the trailing & marker."); + } + + if (isEmpty(uri)) { + // return an empty map + return new LinkedHashMap<String, Object>(0); + } + + // need to parse the uri query parameters manually as we cannot rely on splitting by &, + // as & can be used in a parameter value as well. + + try { + // use a linked map so the parameters is in the same order + Map<String, Object> rc = new LinkedHashMap<String, Object>(); + + boolean isKey = true; + boolean isValue = false; + boolean isRaw = false; + StringBuilder key = new StringBuilder(); + StringBuilder value = new StringBuilder(); + + // parse the uri parameters char by char + for (int i = 0; i < uri.length(); i++) { + // current char + char ch = uri.charAt(i); + // look ahead of the next char + char next; + if (i <= uri.length() - 2) { + next = uri.charAt(i + 1); + } else { + next = '\u0000'; + } + + // are we a raw value + isRaw = value.toString().startsWith(RAW_TOKEN_START); + + // if we are in raw mode, then we keep adding until we hit the end marker + if (isRaw) { + if (isKey) { + key.append(ch); + } else if (isValue) { + value.append(ch); + } + + // we only end the raw marker if its )& or at the end of the value + + boolean end = ch == RAW_TOKEN_END.charAt(0) && (next == '&' || next == '\u0000'); + if (end) { + // raw value end, so add that as a parameter, and reset flags + addParameter(key.toString(), value.toString(), rc, useRaw || isRaw); + key.setLength(0); + value.setLength(0); + isKey = true; + isValue = false; + isRaw = false; + // skip to next as we are in raw mode and have already added the value + i++; + } + continue; + } + + // if its a key and there is a = sign then the key ends and we are in value mode + if (isKey && ch == '=') { + isKey = false; + isValue = true; + isRaw = false; + continue; + } + + // the & denote parameter is ended + if (ch == '&') { + // parameter is ended, as we hit & separator + String aKey = key.toString(); + // the key may be a placeholder of options which we then do not know what is + boolean validKey = !aKey.startsWith("{{") && !aKey.endsWith("}}"); + if (validKey) { + addParameter(aKey, value.toString(), rc, useRaw || isRaw); + } + key.setLength(0); + value.setLength(0); + isKey = true; + isValue = false; + isRaw = false; + continue; + } + + // regular char so add it to the key or value + if (isKey) { + key.append(ch); + } else if (isValue) { + value.append(ch); + } + } + + // any left over parameters, then add that + if (key.length() > 0) { + String aKey = key.toString(); + // the key may be a placeholder of options which we then do not know what is + boolean validKey = !aKey.startsWith("{{") && !aKey.endsWith("}}"); + if (validKey) { + addParameter(aKey, value.toString(), rc, useRaw || isRaw); + } + } + + return rc; + + } catch (UnsupportedEncodingException e) { + URISyntaxException se = new URISyntaxException(e.toString(), "Invalid encoding"); + se.initCause(e); + throw se; + } + } + + @SuppressWarnings("unchecked") + private static void addParameter(String name, String value, Map<String, Object> map, boolean isRaw) throws UnsupportedEncodingException { + name = URLDecoder.decode(name, CHARSET); + if (!isRaw) { + // need to replace % with %25 + value = URLDecoder.decode(value.replaceAll("%", "%25"), CHARSET); + } + + // does the key already exist? + if (map.containsKey(name)) { + // yes it does, so make sure we can support multiple values, but using a list + // to hold the multiple values + Object existing = map.get(name); + List<String> list; + if (existing instanceof List) { + list = (List<String>) existing; + } else { + // create a new list to hold the multiple values + list = new ArrayList<String>(); + String s = existing != null ? existing.toString() : null; + if (s != null) { + list.add(s); + } + } + list.add(value); + map.put(name, list); + } else { + map.put(name, value); + } + } + + /** + * Assembles a query from the given map. + * + * @param options the map with the options (eg key/value pairs) + * @param ampersand to use & for Java code, and & for XML + * @return a query string with <tt>key1=value&key2=value2&...</tt>, or an empty string if there is no options. + * @throws URISyntaxException is thrown if uri has invalid syntax. + */ + public static String createQueryString(Map<String, String> options, String ampersand, boolean encode) throws URISyntaxException { + try { + if (options.size() > 0) { + StringBuilder rc = new StringBuilder(); + boolean first = true; + for (Object o : options.keySet()) { + if (first) { + first = false; + } else { + rc.append(ampersand); + } + + String key = (String) o; + Object value = options.get(key); + + // use the value as a String + String s = value != null ? value.toString() : null; + appendQueryStringParameter(key, s, rc, encode); + } + return rc.toString(); + } else { + return ""; + } + } catch (UnsupportedEncodingException e) { + URISyntaxException se = new URISyntaxException(e.toString(), "Invalid encoding"); + se.initCause(e); + throw se; + } + } + + private static void appendQueryStringParameter(String key, String value, StringBuilder rc, boolean encode) throws UnsupportedEncodingException { + if (encode) { + rc.append(URLEncoder.encode(key, CHARSET)); + } else { + rc.append(key); + } + // only append if value is not null + if (value != null) { + rc.append("="); + if (value.startsWith(RAW_TOKEN_START) && value.endsWith(RAW_TOKEN_END)) { + // do not encode RAW parameters + rc.append(value); + } else { + if (encode) { + rc.append(URLEncoder.encode(value, CHARSET)); + } else { + rc.append(value); + } + } + } + } + + /** + * Tests whether the value is <tt>null</tt> or an empty string. + * + * @param value the value, if its a String it will be tested for text length as well + * @return true if empty + */ + public static boolean isEmpty(Object value) { + return !isNotEmpty(value); + } + + /** + * Tests whether the value is <b>not</b> <tt>null</tt> or an empty string. + * + * @param value the value, if its a String it will be tested for text length as well + * @return true if <b>not</b> empty + */ + public static boolean isNotEmpty(Object value) { + if (value == null) { + return false; + } else if (value instanceof String) { + String text = (String) value; + return text.trim().length() > 0; + } else { + return true; + } + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/c66be7a8/camel-core/src/main/java/org/apache/camel/runtimecatalog/UnsafeUriCharactersEncoder.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/runtimecatalog/UnsafeUriCharactersEncoder.java b/camel-core/src/main/java/org/apache/camel/runtimecatalog/UnsafeUriCharactersEncoder.java new file mode 100644 index 0000000..a11c810 --- /dev/null +++ b/camel-core/src/main/java/org/apache/camel/runtimecatalog/UnsafeUriCharactersEncoder.java @@ -0,0 +1,206 @@ +/** + * 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.runtimecatalog; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Encoder for unsafe URI characters. + * <p/> + * A good source for details is <a href="http://en.wikipedia.org/wiki/Url_encode">wikipedia url encode</a> article. + */ +public final class UnsafeUriCharactersEncoder { + private static BitSet unsafeCharactersRfc1738; + private static BitSet unsafeCharactersHttp; + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', + 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f'}; + private static final Pattern RAW_PATTERN = Pattern.compile("RAW\\([^\\)]+\\)"); + + static { + unsafeCharactersRfc1738 = new BitSet(256); + unsafeCharactersRfc1738.set(' '); + unsafeCharactersRfc1738.set('"'); + unsafeCharactersRfc1738.set('<'); + unsafeCharactersRfc1738.set('>'); + unsafeCharactersRfc1738.set('#'); + unsafeCharactersRfc1738.set('%'); + unsafeCharactersRfc1738.set('{'); + unsafeCharactersRfc1738.set('}'); + unsafeCharactersRfc1738.set('|'); + unsafeCharactersRfc1738.set('\\'); + unsafeCharactersRfc1738.set('^'); + unsafeCharactersRfc1738.set('~'); + unsafeCharactersRfc1738.set('['); + unsafeCharactersRfc1738.set(']'); + unsafeCharactersRfc1738.set('`'); + } + + static { + unsafeCharactersHttp = new BitSet(256); + unsafeCharactersHttp.set(' '); + unsafeCharactersHttp.set('"'); + unsafeCharactersHttp.set('<'); + unsafeCharactersHttp.set('>'); + unsafeCharactersHttp.set('#'); + unsafeCharactersHttp.set('%'); + unsafeCharactersHttp.set('{'); + unsafeCharactersHttp.set('}'); + unsafeCharactersHttp.set('|'); + unsafeCharactersHttp.set('\\'); + unsafeCharactersHttp.set('^'); + unsafeCharactersHttp.set('~'); + unsafeCharactersHttp.set('`'); + } + + private UnsafeUriCharactersEncoder() { + // util class + } + + public static String encode(String s) { + return encode(s, unsafeCharactersRfc1738); + } + + public static String encodeHttpURI(String s) { + return encode(s, unsafeCharactersHttp); + } + + public static String encode(String s, BitSet unsafeCharacters) { + return encode(s, unsafeCharacters, false); + } + + public static String encode(String s, boolean checkRaw) { + return encode(s, unsafeCharactersRfc1738, checkRaw); + } + + public static String encodeHttpURI(String s, boolean checkRaw) { + return encode(s, unsafeCharactersHttp, checkRaw); + } + + private static List<Pair> checkRAW(String s) { + Matcher matcher = RAW_PATTERN.matcher(s); + List<Pair> answer = new ArrayList<Pair>(); + // Check all occurrences + while (matcher.find()) { + answer.add(new Pair(matcher.start(), matcher.end())); + } + return answer; + } + + private static boolean isRaw(int index, List<Pair> pairs) { + for (Pair pair : pairs) { + if (index < pair.left) { + return false; + } else { + if (index >= pair.left) { + if (index <= pair.right) { + return true; + } else { + continue; + } + } + } + } + return false; + } + + private static class Pair { + int left; + int right; + + Pair(int left, int right) { + this.left = left; + this.right = right; + } + } + + // Just skip the encode for isRAW part + public static String encode(String s, BitSet unsafeCharacters, boolean checkRaw) { + List<Pair> rawPairs; + if (checkRaw) { + rawPairs = checkRAW(s); + } else { + rawPairs = new ArrayList<Pair>(); + } + + int n = s == null ? 0 : s.length(); + if (n == 0) { + return s; + } + + // First check whether we actually need to encode + char chars[] = s.toCharArray(); + for (int i = 0;;) { + // just deal with the ascii character + if (chars[i] > 0 && chars[i] < 128) { + if (unsafeCharacters.get(chars[i])) { + break; + } + } + if (++i >= chars.length) { + return s; + } + } + + // okay there are some unsafe characters so we do need to encode + // see details at: http://en.wikipedia.org/wiki/Url_encode + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < chars.length; i++) { + char ch = chars[i]; + if (ch > 0 && ch < 128 && unsafeCharacters.get(ch)) { + // special for % sign as it may be a decimal encoded value + if (ch == '%') { + char next = i + 1 < chars.length ? chars[i + 1] : ' '; + char next2 = i + 2 < chars.length ? chars[i + 2] : ' '; + + if (isHexDigit(next) && isHexDigit(next2) && !isRaw(i, rawPairs)) { + // its already encoded (decimal encoded) so just append as is + sb.append(ch); + } else { + // must escape then, as its an unsafe character + appendEscape(sb, (byte) ch); + } + } else { + // must escape then, as its an unsafe character + appendEscape(sb, (byte) ch); + } + } else { + sb.append(ch); + } + } + return sb.toString(); + } + + private static void appendEscape(StringBuilder sb, byte b) { + sb.append('%'); + sb.append(HEX_DIGITS[(b >> 4) & 0x0f]); + sb.append(HEX_DIGITS[(b >> 0) & 0x0f]); + } + + private static boolean isHexDigit(char ch) { + for (char hex : HEX_DIGITS) { + if (hex == ch) { + return true; + } + } + return false; + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/c66be7a8/camel-core/src/main/java/org/apache/camel/runtimecatalog/package.html ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/runtimecatalog/package.html b/camel-core/src/main/java/org/apache/camel/runtimecatalog/package.html new file mode 100644 index 0000000..2f15a04 --- /dev/null +++ b/camel-core/src/main/java/org/apache/camel/runtimecatalog/package.html @@ -0,0 +1,25 @@ +<!-- + 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. +--> +<html> +<head> +</head> +<body> + +Runtime Camel Catalog + +</body> +</html> http://git-wip-us.apache.org/repos/asf/camel/blob/c66be7a8/camel-core/src/main/java/org/apache/camel/util/EndpointHelper.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/util/EndpointHelper.java b/camel-core/src/main/java/org/apache/camel/util/EndpointHelper.java index fd22f55..a32c109 100644 --- a/camel-core/src/main/java/org/apache/camel/util/EndpointHelper.java +++ b/camel-core/src/main/java/org/apache/camel/util/EndpointHelper.java @@ -37,8 +37,8 @@ import org.apache.camel.PollingConsumer; import org.apache.camel.Processor; import org.apache.camel.ResolveEndpointFailedException; import org.apache.camel.Route; -import org.apache.camel.catalog.DefaultRuntimeCamelCatalog; -import org.apache.camel.catalog.RuntimeCamelCatalog; +import org.apache.camel.runtimecatalog.DefaultRuntimeCamelCatalog; +import org.apache.camel.runtimecatalog.RuntimeCamelCatalog; import org.apache.camel.spi.BrowsableEndpoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -523,7 +523,7 @@ public final class EndpointHelper { * @param uri the endpoint uri * @return a map for each option in the uri with the corresponding information from the json * @throws Exception is thrown in case of error - * @deprecated use {@link org.apache.camel.catalog.RuntimeCamelCatalog#endpointProperties(String)} + * @deprecated use {@link org.apache.camel.runtimecatalog.RuntimeCamelCatalog#endpointProperties(String)} */ @Deprecated public static Map<String, Object> endpointProperties(CamelContext camelContext, String uri) throws Exception { http://git-wip-us.apache.org/repos/asf/camel/blob/c66be7a8/camel-core/src/test/java/org/apache/camel/catalog/AbstractCamelCatalogTest.java ---------------------------------------------------------------------- diff --git a/camel-core/src/test/java/org/apache/camel/catalog/AbstractCamelCatalogTest.java b/camel-core/src/test/java/org/apache/camel/catalog/AbstractCamelCatalogTest.java deleted file mode 100644 index ed7e997..0000000 --- a/camel-core/src/test/java/org/apache/camel/catalog/AbstractCamelCatalogTest.java +++ /dev/null @@ -1,104 +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.catalog; - -import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Before; -import org.junit.Test; - -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.mock; -import static org.easymock.EasyMock.replay; -import static org.junit.Assert.assertEquals; - -public class AbstractCamelCatalogTest { - - AbstractCamelCatalog catalog = new AbstractCamelCatalog() { - }; - - JSonSchemaResolver resolver; - - @Before - public void setupMockCatalog() { - resolver = mock(JSonSchemaResolver.class); - - catalog.setJSonSchemaResolver(resolver); - } - - @Test - public void shouldConstructEndpointUris() throws URISyntaxException { - expect(resolver.getComponentJSonSchema("comp")).andReturn("{\n"// - + " \"component\": {\n"// - + " \"syntax\": \"comp:param1:param2\"\n"// - + " }\n"// - + "}"); - - replay(resolver); - - final Map<String, String> properties = new HashMap<>(); - properties.put("param1", "value1"); - properties.put("param2", "value2"); - properties.put("param3", "value3"); - - final String endpointUri = catalog.doAsEndpointUri("comp", properties, "&", false); - - assertEquals("comp:value1:value2?param3=value3", endpointUri); - } - - @Test - public void shouldConstructEndpointUrisWithPropertyPlaceholders() throws URISyntaxException { - expect(resolver.getComponentJSonSchema("comp")).andReturn("{\n"// - + " \"component\": {\n"// - + " \"syntax\": \"comp:param1:param2\"\n"// - + " }\n"// - + "}"); - - replay(resolver); - - final Map<String, String> properties = new HashMap<>(); - properties.put("param1", "{{prop1}}"); - properties.put("param2", "{{prop2}}"); - properties.put("param3", "{{prop3}}"); - - final String endpointUri = catalog.doAsEndpointUri("comp", properties, "&", false); - - assertEquals("comp:{{prop1}}:{{prop2}}?param3={{prop3}}", endpointUri); - } - - @Test - public void shouldConstructEndpointUrisWhenValuesContainTokens() throws URISyntaxException { - expect(resolver.getComponentJSonSchema("comp")).andReturn("{\n"// - + " \"component\": {\n"// - + " \"syntax\": \"comp:param1:param2\"\n"// - + " }\n"// - + "}"); - - replay(resolver); - - final Map<String, String> properties = new HashMap<>(); - properties.put("param1", "{value1}"); - properties.put("param2", "/value2/"); - properties.put("param3", "/value3/{param}"); - - final String endpointUri = catalog.doAsEndpointUri("comp", properties, "&", false); - - assertEquals("comp:{value1}:/value2/?param3=/value3/{param}", endpointUri); - } -} http://git-wip-us.apache.org/repos/asf/camel/blob/c66be7a8/camel-core/src/test/java/org/apache/camel/catalog/RuntimeCamelCatalogTest.java ---------------------------------------------------------------------- diff --git a/camel-core/src/test/java/org/apache/camel/catalog/RuntimeCamelCatalogTest.java b/camel-core/src/test/java/org/apache/camel/catalog/RuntimeCamelCatalogTest.java deleted file mode 100644 index 78e51a2..0000000 --- a/camel-core/src/test/java/org/apache/camel/catalog/RuntimeCamelCatalogTest.java +++ /dev/null @@ -1,393 +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.catalog; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.camel.impl.DefaultCamelContext; -import org.junit.BeforeClass; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -public class RuntimeCamelCatalogTest { - - static RuntimeCamelCatalog catalog; - - private static final Logger LOG = LoggerFactory.getLogger(RuntimeCamelCatalogTest.class); - - @BeforeClass - public static void createCamelCatalog() { - catalog = new DefaultRuntimeCamelCatalog(new DefaultCamelContext()); - } - - @Test - public void testFromCamelContext() throws Exception { - String schema = new DefaultCamelContext().getRuntimeCamelCatalog().modelJSonSchema("choice"); - assertNotNull(schema); - } - - @Test - public void testJsonSchema() throws Exception { - String schema = catalog.modelJSonSchema("aggregate"); - assertNotNull(schema); - - // lets make it possible to find bean/method using both names - schema = catalog.modelJSonSchema("method"); - assertNotNull(schema); - schema = catalog.modelJSonSchema("bean"); - assertNotNull(schema); - } - - @Test - public void testAsEndpointUriMapFile() throws Exception { - Map<String, String> map = new HashMap<String, String>(); - map.put("directoryName", "src/data/inbox"); - map.put("noop", "true"); - map.put("delay", "5000"); - - String uri = catalog.asEndpointUri("file", map, true); - assertEquals("file:src/data/inbox?delay=5000&noop=true", uri); - - String uri2 = catalog.asEndpointUriXml("file", map, true); - assertEquals("file:src/data/inbox?delay=5000&noop=true", uri2); - } - - @Test - public void testAsEndpointUriTimer() throws Exception { - Map<String, String> map = new HashMap<String, String>(); - map.put("timerName", "foo"); - map.put("period", "5000"); - - String uri = catalog.asEndpointUri("timer", map, true); - assertEquals("timer:foo?period=5000", uri); - } - - @Test - public void testAsEndpointUriPropertiesPlaceholders() throws Exception { - Map<String, String> map = new HashMap<String, String>(); - map.put("timerName", "foo"); - map.put("period", "{{howoften}}"); - map.put("repeatCount", "5"); - - String uri = catalog.asEndpointUri("timer", map, true); - assertEquals("timer:foo?period=%7B%7Bhowoften%7D%7D&repeatCount=5", uri); - - uri = catalog.asEndpointUri("timer", map, false); - assertEquals("timer:foo?period={{howoften}}&repeatCount=5", uri); - } - - @Test - public void testAsEndpointUriBeanLookup() throws Exception { - Map<String, String> map = new HashMap<String, String>(); - map.put("resourceUri", "foo.xslt"); - map.put("converter", "#myConverter"); - - String uri = catalog.asEndpointUri("xslt", map, true); - assertEquals("xslt:foo.xslt?converter=%23myConverter", uri); - - uri = catalog.asEndpointUri("xslt", map, false); - assertEquals("xslt:foo.xslt?converter=#myConverter", uri); - } - - @Test - public void testEndpointPropertiesPlaceholders() throws Exception { - Map<String, String> map = catalog.endpointProperties("timer:foo?period={{howoften}}&repeatCount=5"); - assertNotNull(map); - assertEquals(3, map.size()); - - assertEquals("foo", map.get("timerName")); - assertEquals("{{howoften}}", map.get("period")); - assertEquals("5", map.get("repeatCount")); - } - - @Test - public void testAsEndpointUriLog() throws Exception { - Map<String, String> map = new HashMap<String, String>(); - map.put("loggerName", "foo"); - map.put("loggerLevel", "WARN"); - map.put("multiline", "true"); - map.put("showAll", "true"); - map.put("showBody", "false"); - map.put("showBodyType", "false"); - map.put("showExchangePattern", "false"); - map.put("style", "Tab"); - - assertEquals("log:foo?loggerLevel=WARN&multiline=true&showAll=true&style=Tab", catalog.asEndpointUri("log", map, false)); - } - - @Test - public void testAsEndpointUriLogShort() throws Exception { - Map<String, String> map = new HashMap<String, String>(); - map.put("loggerName", "foo"); - map.put("loggerLevel", "DEBUG"); - - assertEquals("log:foo?loggerLevel=DEBUG", catalog.asEndpointUri("log", map, false)); - } - - @Test - public void testAsEndpointUriWithplaceholder() throws Exception { - Map<String, String> map = new HashMap<String, String>(); - map.put("name", "foo"); - map.put("blockWhenFull", "{{block}}"); - assertEquals("seda:foo?blockWhenFull={{block}}", catalog.asEndpointUri("seda", map, false)); - } - - @Test - public void testEndpointPropertiesSedaRequired() throws Exception { - Map<String, String> map = catalog.endpointProperties("seda:foo"); - assertNotNull(map); - assertEquals(1, map.size()); - - assertEquals("foo", map.get("name")); - - map = catalog.endpointProperties("seda:foo?blockWhenFull=true"); - assertNotNull(map); - assertEquals(2, map.size()); - - assertEquals("foo", map.get("name")); - assertEquals("true", map.get("blockWhenFull")); - } - - @Test - public void validateProperties() throws Exception { - // valid - EndpointValidationResult result = catalog.validateEndpointProperties("log:mylog"); - assertTrue(result.isSuccess()); - - // unknown - result = catalog.validateEndpointProperties("log:mylog?level=WARN&foo=bar"); - assertFalse(result.isSuccess()); - assertTrue(result.getUnknown().contains("foo")); - assertEquals(1, result.getNumberOfErrors()); - - // enum - result = catalog.validateEndpointProperties("seda:foo?waitForTaskToComplete=blah"); - assertFalse(result.isSuccess()); - assertEquals("blah", result.getInvalidEnum().get("waitForTaskToComplete")); - assertEquals(1, result.getNumberOfErrors()); - - // reference okay - result = catalog.validateEndpointProperties("seda:foo?queue=#queue"); - assertTrue(result.isSuccess()); - assertEquals(0, result.getNumberOfErrors()); - - // unknown component - result = catalog.validateEndpointProperties("foo:bar?me=you"); - assertFalse(result.isSuccess()); - assertTrue(result.getUnknownComponent().equals("foo")); - assertEquals(1, result.getNumberOfErrors()); - - // invalid boolean but default value - result = catalog.validateEndpointProperties("log:output?showAll=ggg"); - assertFalse(result.isSuccess()); - assertEquals("ggg", result.getInvalidBoolean().get("showAll")); - assertEquals(1, result.getNumberOfErrors()); - - // dataset - result = catalog.validateEndpointProperties("dataset:foo?minRate=50"); - assertTrue(result.isSuccess()); - - // time pattern - result = catalog.validateEndpointProperties("timer://foo?fixedRate=true&delay=0&period=2s"); - assertTrue(result.isSuccess()); - - // reference lookup - result = catalog.validateEndpointProperties("timer://foo?fixedRate=#fixed&delay=#myDelay"); - assertTrue(result.isSuccess()); - - // optional consumer. prefix - result = catalog.validateEndpointProperties("file:inbox?consumer.delay=5000&consumer.greedy=true"); - assertTrue(result.isSuccess()); - - // optional without consumer. prefix - result = catalog.validateEndpointProperties("file:inbox?delay=5000&greedy=true"); - assertTrue(result.isSuccess()); - - // mixed optional without consumer. prefix - result = catalog.validateEndpointProperties("file:inbox?delay=5000&consumer.greedy=true"); - assertTrue(result.isSuccess()); - - // prefix - result = catalog.validateEndpointProperties("file:inbox?delay=5000&scheduler.foo=123&scheduler.bar=456"); - assertTrue(result.isSuccess()); - - // stub - result = catalog.validateEndpointProperties("stub:foo?me=123&you=456"); - assertTrue(result.isSuccess()); - - // lenient on - result = catalog.validateEndpointProperties("dataformat:string:marshal?foo=bar"); - assertTrue(result.isSuccess()); - - // lenient off - result = catalog.validateEndpointProperties("dataformat:string:marshal?foo=bar", true); - assertFalse(result.isSuccess()); - assertTrue(result.getUnknown().contains("foo")); - - // data format - result = catalog.validateEndpointProperties("dataformat:string:marshal?charset=utf-8", true); - assertTrue(result.isSuccess()); - - // incapable to parse - result = catalog.validateEndpointProperties("{{getFtpUrl}}?recursive=true"); - assertFalse(result.isSuccess()); - assertTrue(result.getIncapable() != null); - } - - @Test - public void validatePropertiesSummary() throws Exception { - EndpointValidationResult result = catalog.validateEndpointProperties("yammer:MESSAGES?blah=yada&accessToken=aaa&consumerKey=&useJson=no&initialDelay=five&pollStrategy=myStrategy"); - assertFalse(result.isSuccess()); - String reason = result.summaryErrorMessage(true); - LOG.info(reason); - - result = catalog.validateEndpointProperties("jms:unknown:myqueue"); - assertFalse(result.isSuccess()); - reason = result.summaryErrorMessage(false); - LOG.info(reason); - } - - @Test - public void validateTimePattern() throws Exception { - assertTrue(catalog.validateTimePattern("0")); - assertTrue(catalog.validateTimePattern("500")); - assertTrue(catalog.validateTimePattern("10000")); - assertTrue(catalog.validateTimePattern("5s")); - assertTrue(catalog.validateTimePattern("5sec")); - assertTrue(catalog.validateTimePattern("5secs")); - assertTrue(catalog.validateTimePattern("3m")); - assertTrue(catalog.validateTimePattern("3min")); - assertTrue(catalog.validateTimePattern("3minutes")); - assertTrue(catalog.validateTimePattern("5m15s")); - assertTrue(catalog.validateTimePattern("1h")); - assertTrue(catalog.validateTimePattern("1hour")); - assertTrue(catalog.validateTimePattern("2hours")); - - assertFalse(catalog.validateTimePattern("bla")); - assertFalse(catalog.validateTimePattern("2year")); - assertFalse(catalog.validateTimePattern("60darn")); - } - - @Test - public void testEndpointComponentName() throws Exception { - String name = catalog.endpointComponentName("jms:queue:foo"); - assertEquals("jms", name); - } - - @Test - public void testSimpleExpression() throws Exception { - SimpleValidationResult result = catalog.validateSimpleExpression(null, "${body}"); - assertTrue(result.isSuccess()); - assertEquals("${body}", result.getSimple()); - - result = catalog.validateSimpleExpression(null, "${body"); - assertFalse(result.isSuccess()); - assertEquals("${body", result.getSimple()); - LOG.info(result.getError()); - assertTrue(result.getError().startsWith("expected symbol functionEnd but was eol at location 5")); - assertEquals("expected symbol functionEnd but was eol", result.getShortError()); - assertEquals(5, result.getIndex()); - } - - @Test - public void testSimplePredicate() throws Exception { - SimpleValidationResult result = catalog.validateSimplePredicate(null, "${body} == 'abc'"); - assertTrue(result.isSuccess()); - assertEquals("${body} == 'abc'", result.getSimple()); - - result = catalog.validateSimplePredicate(null, "${body} > ${header.size"); - assertFalse(result.isSuccess()); - assertEquals("${body} > ${header.size", result.getSimple()); - LOG.info(result.getError()); - assertTrue(result.getError().startsWith("expected symbol functionEnd but was eol at location 22")); - assertEquals("expected symbol functionEnd but was eol", result.getShortError()); - assertEquals(22, result.getIndex()); - } - - @Test - public void testSimplePredicatePlaceholder() throws Exception { - SimpleValidationResult result = catalog.validateSimplePredicate(null, "${body} contains '{{danger}}'"); - assertTrue(result.isSuccess()); - assertEquals("${body} contains '{{danger}}'", result.getSimple()); - - result = catalog.validateSimplePredicate(null, "${bdy} contains '{{danger}}'"); - assertFalse(result.isSuccess()); - assertEquals("${bdy} contains '{{danger}}'", result.getSimple()); - LOG.info(result.getError()); - assertTrue(result.getError().startsWith("Unknown function: bdy at location 0")); - assertTrue(result.getError().contains("'{{danger}}'")); - assertEquals("Unknown function: bdy", result.getShortError()); - assertEquals(0, result.getIndex()); - } - - @Test - public void testValidateLanguage() throws Exception { - LanguageValidationResult result = catalog.validateLanguageExpression(null, "simple", "${body}"); - assertTrue(result.isSuccess()); - assertEquals("${body}", result.getText()); - - result = catalog.validateLanguageExpression(null, "header", "foo"); - assertTrue(result.isSuccess()); - assertEquals("foo", result.getText()); - - result = catalog.validateLanguagePredicate(null, "simple", "${body} > 10"); - assertTrue(result.isSuccess()); - assertEquals("${body} > 10", result.getText()); - - result = catalog.validateLanguagePredicate(null, "header", "bar"); - assertTrue(result.isSuccess()); - assertEquals("bar", result.getText()); - - result = catalog.validateLanguagePredicate(null, "foobar", "bar"); - assertFalse(result.isSuccess()); - assertEquals("Unknown language foobar", result.getError()); - } - - @Test - public void testValidateEndpointConsumerOnly() throws Exception { - String uri = "file:inbox?bufferSize=4096&readLock=changed&delete=true"; - EndpointValidationResult result = catalog.validateEndpointProperties(uri, false, true, false); - assertTrue(result.isSuccess()); - - uri = "file:inbox?bufferSize=4096&readLock=changed&delete=true&fileExist=Append"; - result = catalog.validateEndpointProperties(uri, false, true, false); - assertFalse(result.isSuccess()); - - assertEquals("fileExist", result.getNotConsumerOnly().iterator().next()); - } - - @Test - public void testValidateEndpointProducerOnly() throws Exception { - String uri = "file:outbox?bufferSize=4096&fileExist=Append"; - EndpointValidationResult result = catalog.validateEndpointProperties(uri, false, false, true); - assertTrue(result.isSuccess()); - - uri = "file:outbox?bufferSize=4096&fileExist=Append&delete=true"; - result = catalog.validateEndpointProperties(uri, false, false, true); - assertFalse(result.isSuccess()); - - assertEquals("delete", result.getNotProducerOnly().iterator().next()); - } - -}