http://git-wip-us.apache.org/repos/asf/camel/blob/b23fbb46/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/DefaultRuntimeProvider.java
----------------------------------------------------------------------
diff --git 
a/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/DefaultRuntimeProvider.java
 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/DefaultRuntimeProvider.java
new file mode 100644
index 0000000..5e3ec9b
--- /dev/null
+++ 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/DefaultRuntimeProvider.java
@@ -0,0 +1,160 @@
+/**
+ * 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.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultRuntimeProvider implements RuntimeProvider {
+
+    private static final String COMPONENT_DIR = 
"org/apache/camel/catalog/components";
+    private static final String DATAFORMAT_DIR = 
"org/apache/camel/catalog/dataformats";
+    private static final String LANGUAGE_DIR = 
"org/apache/camel/catalog/languages";
+    private static final String OTHER_DIR = "org/apache/camel/catalog/others";
+    private static final String COMPONENTS_CATALOG = 
"org/apache/camel/catalog/components.properties";
+    private static final String DATA_FORMATS_CATALOG = 
"org/apache/camel/catalog/dataformats.properties";
+    private static final String LANGUAGE_CATALOG = 
"org/apache/camel/catalog/languages.properties";
+    private static final String OTHER_CATALOG = 
"org/apache/camel/catalog/others.properties";
+
+    private CamelCatalog camelCatalog;
+
+    public DefaultRuntimeProvider() {
+    }
+
+    public DefaultRuntimeProvider(CamelCatalog camelCatalog) {
+        this.camelCatalog = camelCatalog;
+    }
+
+    @Override
+    public CamelCatalog getCamelCatalog() {
+        return camelCatalog;
+    }
+
+    @Override
+    public void setCamelCatalog(CamelCatalog camelCatalog) {
+        this.camelCatalog = camelCatalog;
+    }
+
+    @Override
+    public String getProviderName() {
+        return "default";
+    }
+
+    @Override
+    public String getProviderGroupId() {
+        return "org.apache.camel";
+    }
+
+    @Override
+    public String getProviderArtifactId() {
+        return "camel-catalog";
+    }
+
+    @Override
+    public String getComponentJSonSchemaDirectory() {
+        return COMPONENT_DIR;
+    }
+
+    @Override
+    public String getDataFormatJSonSchemaDirectory() {
+        return DATAFORMAT_DIR;
+    }
+
+    @Override
+    public String getLanguageJSonSchemaDirectory() {
+        return LANGUAGE_DIR;
+    }
+
+    @Override
+    public String getOtherJSonSchemaDirectory() {
+        return OTHER_DIR;
+    }
+
+    protected String getComponentsCatalog() {
+        return COMPONENTS_CATALOG;
+    }
+
+    protected String getDataFormatsCatalog() {
+        return DATA_FORMATS_CATALOG;
+    }
+
+    protected String getLanguageCatalog() {
+        return LANGUAGE_CATALOG;
+    }
+
+    protected String getOtherCatalog() {
+        return OTHER_CATALOG;
+    }
+
+    @Override
+    public List<String> findComponentNames() {
+        List<String> names = new ArrayList<String>();
+        InputStream is = 
getCamelCatalog().getVersionManager().getResourceAsStream(getComponentsCatalog());
+        if (is != null) {
+            try {
+                CatalogHelper.loadLines(is, names);
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+        return names;
+    }
+
+    @Override
+    public List<String> findDataFormatNames() {
+        List<String> names = new ArrayList<String>();
+        InputStream is = 
getCamelCatalog().getVersionManager().getResourceAsStream(getDataFormatsCatalog());
+        if (is != null) {
+            try {
+                CatalogHelper.loadLines(is, names);
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+        return names;
+    }
+
+    @Override
+    public List<String> findLanguageNames() {
+        List<String> names = new ArrayList<String>();
+        InputStream is = 
getCamelCatalog().getVersionManager().getResourceAsStream(getLanguageCatalog());
+        if (is != null) {
+            try {
+                CatalogHelper.loadLines(is, names);
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+        return names;
+    }
+
+    @Override
+    public List<String> findOtherNames() {
+        List<String> names = new ArrayList<String>();
+        InputStream is = 
getCamelCatalog().getVersionManager().getResourceAsStream(getOtherCatalog());
+        if (is != null) {
+            try {
+                CatalogHelper.loadLines(is, names);
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+        return names;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/b23fbb46/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/DefaultVersionManager.java
----------------------------------------------------------------------
diff --git 
a/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/DefaultVersionManager.java
 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/DefaultVersionManager.java
new file mode 100644
index 0000000..c30ddbc
--- /dev/null
+++ 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/DefaultVersionManager.java
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.catalog;
+
+import java.io.InputStream;
+
+public class DefaultVersionManager implements VersionManager {
+
+    private final CamelCatalog camelCatalog;
+
+    public DefaultVersionManager(CamelCatalog camelCatalog) {
+        this.camelCatalog = camelCatalog;
+    }
+
+    @Override
+    public String getLoadedVersion() {
+        return camelCatalog.getCatalogVersion();
+    }
+
+    @Override
+    public boolean loadVersion(String version) {
+        return getLoadedVersion().equals(version);
+    }
+
+    @Override
+    public String getRuntimeProviderLoadedVersion() {
+        // not supported
+        return null;
+    }
+
+    @Override
+    public boolean loadRuntimeProviderVersion(String groupId, String 
artifactId, String version) {
+        // not supported
+        return false;
+    }
+
+    @Override
+    public InputStream getResourceAsStream(String name) {
+        return 
DefaultCamelCatalog.class.getClassLoader().getResourceAsStream(name);
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/b23fbb46/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/EndpointValidationResult.java
----------------------------------------------------------------------
diff --git 
a/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/EndpointValidationResult.java
 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/EndpointValidationResult.java
new file mode 100644
index 0000000..11e2c5e
--- /dev/null
+++ 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/EndpointValidationResult.java
@@ -0,0 +1,426 @@
+/**
+ * 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.io.Serializable;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.camel.catalog.URISupport.isEmpty;
+
+/**
+ * Details result of validating endpoint uri.
+ */
+public class EndpointValidationResult implements Serializable {
+
+    private final String uri;
+    private int errors;
+
+    // general
+    private String syntaxError;
+    private String unknownComponent;
+    private String incapable;
+
+    // options
+    private Set<String> unknown;
+    private Map<String, String[]> unknownSuggestions;
+    private Set<String> lenient;
+    private Set<String> notConsumerOnly;
+    private Set<String> notProducerOnly;
+    private Set<String> required;
+    private Map<String, String> invalidEnum;
+    private Map<String, String[]> invalidEnumChoices;
+    private Map<String, String[]> invalidEnumSuggestions;
+    private Map<String, String> invalidReference;
+    private Map<String, String> invalidBoolean;
+    private Map<String, String> invalidInteger;
+    private Map<String, String> invalidNumber;
+    private Map<String, String> defaultValues;
+
+    public EndpointValidationResult(String uri) {
+        this.uri = uri;
+    }
+
+    public String getUri() {
+        return uri;
+    }
+
+    public int getNumberOfErrors() {
+        return errors;
+    }
+
+    public boolean isSuccess() {
+        boolean ok = syntaxError == null && unknownComponent == null && 
incapable == null
+                && unknown == null && required == null;
+        if (ok) {
+            ok = notConsumerOnly == null && notProducerOnly == null;
+        }
+        if (ok) {
+            ok = invalidEnum == null && invalidEnumChoices == null && 
invalidReference == null
+                && invalidBoolean == null && invalidInteger == null && 
invalidNumber == null;
+        }
+        return ok;
+    }
+
+    public void addSyntaxError(String syntaxError) {
+        this.syntaxError = syntaxError;
+        errors++;
+    }
+
+    public void addIncapable(String uri) {
+        this.incapable = uri;
+        errors++;
+    }
+
+    public void addUnknownComponent(String name) {
+        this.unknownComponent = name;
+        errors++;
+    }
+
+    public void addUnknown(String name) {
+        if (unknown == null) {
+            unknown = new LinkedHashSet<String>();
+        }
+        if (!unknown.contains(name)) {
+            unknown.add(name);
+            errors++;
+        }
+    }
+
+    public void addUnknownSuggestions(String name, String[] suggestions) {
+        if (unknownSuggestions == null) {
+            unknownSuggestions = new LinkedHashMap<String, String[]>();
+        }
+        unknownSuggestions.put(name, suggestions);
+    }
+
+    public void addLenient(String name) {
+        if (lenient == null) {
+            lenient = new LinkedHashSet<String>();
+        }
+        if (!lenient.contains(name)) {
+            lenient.add(name);
+        }
+    }
+
+    public void addRequired(String name) {
+        if (required == null) {
+            required = new LinkedHashSet<String>();
+        }
+        if (!required.contains(name)) {
+            required.add(name);
+            errors++;
+        }
+    }
+
+    public void addInvalidEnum(String name, String value) {
+        if (invalidEnum == null) {
+            invalidEnum = new LinkedHashMap<String, String>();
+        }
+        if (!invalidEnum.containsKey(name)) {
+            invalidEnum.put(name, value);
+            errors++;
+        }
+    }
+
+    public void addInvalidEnumChoices(String name, String[] choices) {
+        if (invalidEnumChoices == null) {
+            invalidEnumChoices = new LinkedHashMap<String, String[]>();
+        }
+        invalidEnumChoices.put(name, choices);
+    }
+
+    public void addInvalidEnumSuggestions(String name, String[] suggestions) {
+        if (invalidEnumSuggestions == null) {
+            invalidEnumSuggestions = new LinkedHashMap<String, String[]>();
+        }
+        invalidEnumSuggestions.put(name, suggestions);
+    }
+
+    public void addInvalidReference(String name, String value) {
+        if (invalidReference == null) {
+            invalidReference = new LinkedHashMap<String, String>();
+        }
+        if (!invalidReference.containsKey(name)) {
+            invalidReference.put(name, value);
+            errors++;
+        }
+    }
+
+    public void addInvalidBoolean(String name, String value) {
+        if (invalidBoolean == null) {
+            invalidBoolean = new LinkedHashMap<String, String>();
+        }
+        if (!invalidBoolean.containsKey(name)) {
+            invalidBoolean.put(name, value);
+            errors++;
+        }
+    }
+
+    public void addInvalidInteger(String name, String value) {
+        if (invalidInteger == null) {
+            invalidInteger = new LinkedHashMap<String, String>();
+        }
+        if (!invalidInteger.containsKey(name)) {
+            invalidInteger.put(name, value);
+            errors++;
+        }
+    }
+
+    public void addInvalidNumber(String name, String value) {
+        if (invalidNumber == null) {
+            invalidNumber = new LinkedHashMap<String, String>();
+        }
+        if (!invalidNumber.containsKey(name)) {
+            invalidNumber.put(name, value);
+            errors++;
+        }
+    }
+
+    public void addDefaultValue(String name, String value)  {
+        if (defaultValues == null) {
+            defaultValues = new LinkedHashMap<String, String>();
+        }
+        defaultValues.put(name, value);
+    }
+
+    public void addNotConsumerOnly(String name) {
+        if (notConsumerOnly == null) {
+            notConsumerOnly = new LinkedHashSet<String>();
+        }
+        if (!notConsumerOnly.contains(name)) {
+            notConsumerOnly.add(name);
+            errors++;
+        }
+    }
+
+    public void addNotProducerOnly(String name) {
+        if (notProducerOnly == null) {
+            notProducerOnly = new LinkedHashSet<String>();
+        }
+        if (!notProducerOnly.contains(name)) {
+            notProducerOnly.add(name);
+            errors++;
+        }
+    }
+
+    public String getSyntaxError() {
+        return syntaxError;
+    }
+
+    public String getIncapable() {
+        return incapable;
+    }
+
+    public Set<String> getUnknown() {
+        return unknown;
+    }
+
+    public Set<String> getLenient() {
+        return lenient;
+    }
+
+    public Map<String, String[]> getUnknownSuggestions() {
+        return unknownSuggestions;
+    }
+
+    public String getUnknownComponent() {
+        return unknownComponent;
+    }
+
+    public Set<String> getRequired() {
+        return required;
+    }
+
+    public Map<String, String> getInvalidEnum() {
+        return invalidEnum;
+    }
+
+    public Map<String, String[]> getInvalidEnumChoices() {
+        return invalidEnumChoices;
+    }
+
+    public Map<String, String> getInvalidReference() {
+        return invalidReference;
+    }
+
+    public Map<String, String> getInvalidBoolean() {
+        return invalidBoolean;
+    }
+
+    public Map<String, String> getInvalidInteger() {
+        return invalidInteger;
+    }
+
+    public Map<String, String> getInvalidNumber() {
+        return invalidNumber;
+    }
+
+    public Map<String, String> getDefaultValues() {
+        return defaultValues;
+    }
+
+    public Set<String> getNotConsumerOnly() {
+        return notConsumerOnly;
+    }
+
+    public Set<String> getNotProducerOnly() {
+        return notProducerOnly;
+    }
+
+    /**
+     * A human readable summary of the validation errors.
+     *
+     * @param includeHeader whether to include a header
+     * @return the summary, or <tt>null</tt> if no validation errors
+     */
+    public String summaryErrorMessage(boolean includeHeader) {
+        if (isSuccess()) {
+            return null;
+        }
+
+        if (incapable != null) {
+            return "\tIncapable of parsing uri: " + incapable;
+        } else if (syntaxError != null) {
+            return "\tSyntax error: " + syntaxError;
+        } else if (unknownComponent != null) {
+            return "\tUnknown component: " + unknownComponent;
+        }
+
+        // for each invalid option build a reason message
+        Map<String, String> options = new LinkedHashMap<String, String>();
+        if (unknown != null) {
+            for (String name : unknown) {
+                if (unknownSuggestions != null && 
unknownSuggestions.containsKey(name)) {
+                    String[] suggestions = unknownSuggestions.get(name);
+                    if (suggestions != null && suggestions.length > 0) {
+                        String str = Arrays.asList(suggestions).toString();
+                        options.put(name, "Unknown option. Did you mean: " + 
str);
+                    } else {
+                        options.put(name, "Unknown option");
+                    }
+                } else {
+                    options.put(name, "Unknown option");
+                }
+            }
+        }
+        if (notConsumerOnly != null) {
+            for (String name : notConsumerOnly) {
+                options.put(name, "Option not applicable in consumer only 
mode");
+            }
+        }
+        if (notProducerOnly != null) {
+            for (String name : notProducerOnly) {
+                options.put(name, "Option not applicable in producer only 
mode");
+            }
+        }
+        if (required != null) {
+            for (String name : required) {
+                options.put(name, "Missing required option");
+            }
+        }
+        if (invalidEnum != null) {
+            for (Map.Entry<String, String> entry : invalidEnum.entrySet()) {
+                String name = entry.getKey();
+                String[] choices = invalidEnumChoices.get(name);
+                String defaultValue = defaultValues != null ? 
defaultValues.get(entry.getKey()) : null;
+                String str = Arrays.asList(choices).toString();
+                String msg = "Invalid enum value: " + entry.getValue() + ". 
Possible values: " + str;
+                if (invalidEnumSuggestions != null) {
+                    String[] suggestions = invalidEnumSuggestions.get(name);
+                    if (suggestions != null && suggestions.length > 0) {
+                        str = Arrays.asList(suggestions).toString();
+                        msg += ". Did you mean: " + str;
+                    }
+                }
+                if (defaultValue != null) {
+                    msg += ". Default value: " + defaultValue;
+                }
+
+                options.put(entry.getKey(), msg);
+            }
+        }
+        if (invalidReference != null) {
+            for (Map.Entry<String, String> entry : 
invalidReference.entrySet()) {
+                boolean empty = isEmpty(entry.getValue());
+                if (empty) {
+                    options.put(entry.getKey(), "Empty reference value");
+                } else if (!entry.getValue().startsWith("#")) {
+                    options.put(entry.getKey(), "Invalid reference value: " + 
entry.getValue() + " must start with #");
+                } else {
+                    options.put(entry.getKey(), "Invalid reference value: " + 
entry.getValue());
+                }
+            }
+        }
+        if (invalidBoolean != null) {
+            for (Map.Entry<String, String> entry : invalidBoolean.entrySet()) {
+                boolean empty = isEmpty(entry.getValue());
+                if (empty) {
+                    options.put(entry.getKey(), "Empty boolean value");
+                } else {
+                    options.put(entry.getKey(), "Invalid boolean value: " + 
entry.getValue());
+                }
+            }
+        }
+        if (invalidInteger != null) {
+            for (Map.Entry<String, String> entry : invalidInteger.entrySet()) {
+                boolean empty = isEmpty(entry.getValue());
+                if (empty) {
+                    options.put(entry.getKey(), "Empty integer value");
+                } else {
+                    options.put(entry.getKey(), "Invalid integer value: " + 
entry.getValue());
+                }
+            }
+        }
+        if (invalidNumber != null) {
+            for (Map.Entry<String, String> entry : invalidNumber.entrySet()) {
+                boolean empty = isEmpty(entry.getValue());
+                if (empty) {
+                    options.put(entry.getKey(), "Empty number value");
+                } else {
+                    options.put(entry.getKey(), "Invalid number value: " + 
entry.getValue());
+                }
+            }
+        }
+
+        // build a table with the error summary nicely formatted
+        // lets use 24 as min length
+        int maxLen = 24;
+        for (String key : options.keySet()) {
+            maxLen = Math.max(maxLen, key.length());
+        }
+        String format = "%" + maxLen + "s    %s";
+
+        // build the human error summary
+        StringBuilder sb = new StringBuilder();
+        if (includeHeader) {
+            sb.append("Endpoint validator error\n");
+            
sb.append("---------------------------------------------------------------------------------------------------------------------------------------\n");
+            sb.append("\n");
+        }
+        sb.append("\t").append(uri).append("\n");
+        for (Map.Entry<String, String> option : options.entrySet()) {
+            String out = String.format(format, option.getKey(), 
option.getValue());
+            sb.append("\n\t").append(out);
+        }
+
+        return sb.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/b23fbb46/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/JSonSchemaHelper.java
----------------------------------------------------------------------
diff --git 
a/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/JSonSchemaHelper.java
 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/JSonSchemaHelper.java
new file mode 100644
index 0000000..318cbf4
--- /dev/null
+++ 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/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.catalog;
+
+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 = "&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/b23fbb46/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/LanguageValidationResult.java
----------------------------------------------------------------------
diff --git 
a/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/LanguageValidationResult.java
 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/LanguageValidationResult.java
new file mode 100644
index 0000000..4a977eb
--- /dev/null
+++ 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/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.catalog;
+
+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/b23fbb46/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/RuntimeProvider.java
----------------------------------------------------------------------
diff --git 
a/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/RuntimeProvider.java
 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/RuntimeProvider.java
new file mode 100644
index 0000000..fa4c3d6
--- /dev/null
+++ 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/RuntimeProvider.java
@@ -0,0 +1,93 @@
+/**
+ * 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.List;
+
+/**
+ * A pluggable strategy for chosen runtime to run Camel such as default, 
karaf, spring-boot, etc.
+ * This allows third party runtimes to provide their own provider, that can 
amend the catalog
+ * to match the runtime. For example spring-boot or karaf does not support all 
the default Camel components.
+ */
+public interface RuntimeProvider {
+
+    /**
+     * Gets the {@link CamelCatalog}
+     */
+    CamelCatalog getCamelCatalog();
+
+    /**
+     * Sets the {@link CamelCatalog} to use
+     */
+    void setCamelCatalog(CamelCatalog camelCatalog);
+
+    /**
+     * Name of provider such as <tt>default</tt>, <tt>karaf</tt>, 
<tt>spring-boot</tt>
+     */
+    String getProviderName();
+
+    /**
+     * Maven group id of the runtime provider JAR dependency.
+     */
+    String getProviderGroupId();
+
+    /**
+     * Maven artifact id of the runtime provider JAR dependency.
+     */
+    String getProviderArtifactId();
+
+    /**
+     * Gets the directory where the component json files are stored in the 
catalog JAR file
+     */
+    String getComponentJSonSchemaDirectory();
+
+    /**
+     * Gets the directory where the data format json files are stored in the 
catalog JAR file
+     */
+    String getDataFormatJSonSchemaDirectory();
+
+    /**
+     * Gets the directory where the language json files are stored in the 
catalog JAR file
+     */
+    String getLanguageJSonSchemaDirectory();
+
+    /**
+     * Gets the directory where the other (miscellaneous) json files are 
stored in the catalog JAR file
+     */
+    String getOtherJSonSchemaDirectory();
+
+    /**
+     * Find all the component names from the Camel catalog supported by the 
provider
+     */
+    List<String> findComponentNames();
+
+    /**
+     * Find all the data format names from the Camel catalog supported by the 
provider
+     */
+    List<String> findDataFormatNames();
+
+    /**
+     * Find all the language names from the Camel catalog supported by the 
provider
+     */
+    List<String> findLanguageNames();
+
+    /**
+     * Find all the other (miscellaneous) names from the Camel catalog 
supported by the provider
+     */
+    List<String> findOtherNames();
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/b23fbb46/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/SimpleValidationResult.java
----------------------------------------------------------------------
diff --git 
a/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/SimpleValidationResult.java
 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/SimpleValidationResult.java
new file mode 100644
index 0000000..5c86f23
--- /dev/null
+++ 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/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.catalog;
+
+/**
+ * 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/b23fbb46/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/SuggestionStrategy.java
----------------------------------------------------------------------
diff --git 
a/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/SuggestionStrategy.java
 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/SuggestionStrategy.java
new file mode 100644
index 0000000..057d372
--- /dev/null
+++ 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/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.catalog;
+
+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/b23fbb46/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/TimePatternConverter.java
----------------------------------------------------------------------
diff --git 
a/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/TimePatternConverter.java
 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/TimePatternConverter.java
new file mode 100644
index 0000000..4ed843a
--- /dev/null
+++ 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/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.catalog;
+
+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/b23fbb46/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/URISupport.java
----------------------------------------------------------------------
diff --git 
a/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/URISupport.java
 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/URISupport.java
new file mode 100644
index 0000000..ea1ecd5
--- /dev/null
+++ 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/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.catalog;
+
+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 &amp; 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/b23fbb46/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/UnsafeUriCharactersEncoder.java
----------------------------------------------------------------------
diff --git 
a/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/UnsafeUriCharactersEncoder.java
 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/UnsafeUriCharactersEncoder.java
new file mode 100644
index 0000000..563bac2
--- /dev/null
+++ 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/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.catalog;
+
+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/b23fbb46/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/VersionHelper.java
----------------------------------------------------------------------
diff --git 
a/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/VersionHelper.java
 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/VersionHelper.java
new file mode 100644
index 0000000..e1c69bc
--- /dev/null
+++ 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/VersionHelper.java
@@ -0,0 +1,73 @@
+/**
+ * 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.io.InputStream;
+import java.util.Properties;
+
+/**
+ * To get the version of this catalog.
+ */
+public class VersionHelper {
+
+    private static volatile String version;
+
+    public synchronized String getVersion() {
+        if (version != null) {
+            return version;
+        }
+        InputStream is = null;
+        // try to load from maven properties first
+        try {
+            Properties p = new Properties();
+            is = 
getClass().getResourceAsStream("/META-INF/maven/org.apache.camel/camel-catalog/pom.properties");
+            if (is != null) {
+                p.load(is);
+                version = p.getProperty("version", "");
+            }
+        } catch (Exception e) {
+            // ignore
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (Exception e) {
+                    // ignore
+                }
+            }
+        }
+
+        // fallback to using Java API
+        if (version == null) {
+            Package aPackage = getClass().getPackage();
+            if (aPackage != null) {
+                version = aPackage.getImplementationVersion();
+                if (version == null) {
+                    version = aPackage.getSpecificationVersion();
+                }
+            }
+        }
+
+        if (version == null) {
+            // we could not compute the version so use a blank
+            version = "";
+        }
+
+        return version;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/b23fbb46/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/VersionManager.java
----------------------------------------------------------------------
diff --git 
a/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/VersionManager.java
 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/VersionManager.java
new file mode 100644
index 0000000..6f8aff4
--- /dev/null
+++ 
b/platforms/camel-catalog/src/main/java/org/apache/camel/catalog/VersionManager.java
@@ -0,0 +1,70 @@
+/**
+ * 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.io.InputStream;
+
+/**
+ * Strategy to manage and load Camel versions the catalog uses.
+ */
+public interface VersionManager {
+
+    /**
+     * Gets the current loaded Camel version used by the catalog.
+     */
+    String getLoadedVersion();
+
+    /**
+     * Attempt to load the Camel version to be used by the catalog.
+     * <p/>
+     * Loading the camel-catalog JAR of the given version of choice may 
require internet access
+     * to download the JAR from Maven central. You can pre download the JAR 
and install in a local
+     * Maven repository to avoid internet access for offline environments.
+     *
+     * @param version  the Camel version such as <tt>2.17.1</tt>
+     * @return <tt>true</tt> if the version was loaded, <tt>false</tt> if not.
+     */
+    boolean loadVersion(String version);
+
+    /**
+     * Gets the current loaded runtime provider version used by the catalog.
+     */
+    String getRuntimeProviderLoadedVersion();
+
+    /**
+     * Attempt to load the runtime provider version to be used by the catalog.
+     * <p/>
+     * Loading the runtime provider JAR of the given version of choice may 
require internet access
+     * to download the JAR from Maven central. You can pre download the JAR 
and install in a local
+     * Maven repository to avoid internet access for offline environments.
+     *
+     * @param groupId  the runtime provider Maven groupId
+     * @param artifactId  the runtime provider Maven artifactId
+     * @param version  the runtime provider Maven version
+     * @return <tt>true</tt> if the version was loaded, <tt>false</tt> if not.
+     */
+    boolean loadRuntimeProviderVersion(String groupId, String artifactId, 
String version);
+
+    /**
+     * Returns an input stream for reading the specified resource from the 
loaded Catalog version.
+     *
+     * @param name the resource name
+     * @return the stream if found, or <tt>null</tt> if not found.
+     */
+    InputStream getResourceAsStream(String name);
+
+}

Reply via email to