This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch sandbox/camel-3.x in repository https://gitbox.apache.org/repos/asf/camel.git
commit 18e3d696e027271f40e5e66b35f4fe072128081f Merge: 37a077a a287357 Author: Guillaume Nodet <gno...@gmail.com> AuthorDate: Thu Oct 25 15:18:06 2018 +0200 Merge remote-tracking branch 'origin/master' into camel-3.x .../src/main/docs/eips/content-filter-eip.adoc | 2 +- .../src/main/docs/eips/dead-letter-channel.adoc | 50 +-- .../src/main/docs/eips/message-translator.adoc | 2 +- camel-core/src/main/docs/eips/pipeline-eip.adoc | 97 ----- .../src/main/docs/eips/pipes-and-filters.adoc | 4 +- .../requestReply-eip.adoc} | 2 +- .../camel/impl/validator/ProcessorValidator.java | 9 +- .../impl/validator/ValidatorXmlSchemaTest.java | 70 +++ .../resources/org/apache/camel/impl/validate.xsd | 29 ++ .../java/org/apache/camel/util/URISupport.java | 2 +- .../camel/converter/crypto/PGPDataFormatUtil.java | 8 + ...va => SpringPGPDataFormatNoPassPhraseTest.java} | 6 +- .../converter/crypto/SpringPGPDataFormatTest.java | 5 - .../crypto/SpringPGPDataFormatNoPassPhraseTest.xml | 43 ++ .../camel/component/crypto/camel-private.pgp | Bin 0 -> 2547 bytes .../apache/camel/component/crypto/camel-public.pgp | Bin 0 -> 1245 bytes .../config_maps/KubernetesConfigMapsProducer.java | 7 +- .../producer/KubernetesConfigMapsProducerTest.java | 25 +- .../src/main/docs/mybatis-component.adoc | 2 +- .../camel/component/printer/PrinterProducer.java | 2 +- .../camel/component/printer/PrinterPrintTest.java | 30 ++ docs/user-manual/en/SUMMARY.md | 22 +- docs/user-manual/en/architecture.adoc | 2 +- docs/user-manual/en/backlog-tracer.adoc | 115 +++++ docs/user-manual/en/bean-integration.adoc | 2 +- docs/user-manual/en/binding.adoc | 90 ++++ docs/user-manual/en/data-format.adoc | 4 +- docs/user-manual/en/defaulterrorhandler.adoc | 6 +- docs/user-manual/en/download-archives.adoc | 127 ++++++ docs/user-manual/en/download.adoc | 127 ++++++ docs/user-manual/en/error-handling-in-camel.adoc | 12 +- docs/user-manual/en/exception-clause.adoc | 20 +- docs/user-manual/en/expression.adoc | 4 +- docs/user-manual/en/faq.adoc | 201 +++------ ...loader-issue-of-servicemix-camel-component.adoc | 43 ++ .../exception-beandefinitionstoreexception.adoc | 50 +++ ...ption-javaxnamingnoinitialcontextexception.adoc | 36 ++ ...tion-orgapachecamelnosuchendpointexception.adoc | 25 ++ .../faq/exception-orgxmlsaxsaxparseexception.adoc | 26 ++ ...an-i-create-a-custom-component-or-endpoint.adoc | 13 + ...on-ip-address-from-the-camel-cxf-consumer-.adoc | 17 + .../faq/how-can-i-stop-a-route-from-a-route.adoc | 70 +++ ...ults-with-stacktraces-when-using-camel-cxf.adoc | 6 + .../en/faq/how-do-i-configure-endpoints.adoc | 36 +- ...-endpoints-without-the-value-being-encoded.adoc | 16 + ...size-for-producercache-or-producertemplate.adoc | 36 ++ ...ximum-endpoint-cache-size-for-camelcontext.adoc | 42 ++ .../en/faq/how-do-i-debug-my-route.adoc | 15 + docs/user-manual/en/faq/how-do-i-disable-jmx.adoc | 30 ++ .../en/faq/how-do-i-enable-debug-logging.adoc | 33 ++ ...reams-when-debug-logging-messages-in-camel.adoc | 55 +++ ...en-consuming-for-example-from-a-ftp-server.adoc | 31 ++ ...how-do-i-import-rests-from-other-xml-files.adoc | 87 ++++ ...ow-do-i-import-routes-from-other-xml-files.adoc | 44 ++ .../faq/how-do-i-invoke-camel-routes-from-jbi.adoc | 34 ++ .../en/faq/how-do-i-let-jetty-match-wildcards.adoc | 31 ++ ...ow-do-i-make-my-jms-endpoint-transactional.adoc | 31 ++ .../en/faq/how-do-i-name-my-routes.adoc | 20 + .../en/faq/how-do-i-restart-camelcontext.adoc | 9 + ...wn-exception-during-processing-an-exchange.adoc | 54 +++ .../how-do-i-retry-failed-messages-forever.adoc | 19 + ...om-a-certain-point-back-or-an-entire-route.adoc | 92 ++++ ...-contexttestsupport-class-in-my-unit-tests.adoc | 44 ++ .../how-do-i-run-activemq-and-camel-in-jboss.adoc | 21 + ...chars-when-debug-logging-messages-in-camel.adoc | 54 +++ ...do-i-set-the-mep-when-interacting-with-jbi.adoc | 34 ++ ...ify-time-period-in-a-human-friendly-syntax.adoc | 55 +++ ...h-method-to-use-when-using-beans-in-routes.adoc | 9 + .../en/faq/how-do-i-use-a-big-uber-jar.adoc | 11 + .../faq/how-do-i-use-camel-inside-servicemix.adoc | 7 + .../en/faq/how-do-i-use-java-14-logging.adoc | 20 + docs/user-manual/en/faq/how-do-i-use-log4j.adoc | 47 ++ .../how-do-i-use-uris-with-parameters-in-xml.adoc | 52 +++ ...om-processor-which-sends-multiple-messages.adoc | 51 +++ ...direct-event-seda-and-vm-endpoints-compare.adoc | 14 + ...-do-the-timer-and-quartz-endpoints-compare.adoc | 8 + ...how-does-camel-look-up-beans-and-endpoints.adoc | 23 + .../en/faq/how-does-camel-work-with-activemq.adoc | 2 +- ...hould-i-invoke-my-pojos-or-spring-services.adoc | 22 + ...kage-applications-using-camel-and-activemq.adoc | 22 + ...the-camel-cxf-endpoint-from-osgi-platform-.adoc | 67 +++ ...-avoid-sending-some-or-all-message-headers.adoc | 75 ++++ ...e-a-static-camel-converter-method-in-scala.adoc | 19 + ...http-protocol-headers-in-the-camel-message.adoc | 44 ++ ...end-the-same-message-to-multiple-endpoints.adoc | 34 ++ ...-without-touching-the-spring-configuration.adoc | 88 ++++ .../en/faq/how-to-use-a-dynamic-uri-in-to.adoc | 80 ++++ ...-extra-camel-componets-in-servicemix-camel.adoc | 73 ++++ ...-1x-context-xml-from-apache-camel-web-site.adoc | 42 ++ .../Message-flow-in-Route.png | Bin 0 -> 13415 bytes .../flow.png | Bin 0 -> 6152 bytes docs/user-manual/en/faq/is-there-an-ide.adoc | 14 + ...when-adding-and-removing-routes-at-runtime.adoc | 10 + ...-activemq-broker-or-in-another-application.adoc | 36 ++ .../en/faq/using-camel-core-testsjar.adoc | 10 + .../using-getin-or-getout-methods-on-exchange.adoc | 131 ++++++ ...se-when-or-otherwise-in-a-java-camel-route.adoc | 98 +++++ .../en/faq/why-cant-i-use-sign-in-my-password.adoc | 18 + ...o-many-noclassdeffoundexception-on-startup.adoc | 17 + ...my-message-lose-its-headers-during-routing.adoc | 4 + ...use-too-many-threads-with-producertemplate.adoc | 29 ++ ...-does-ftp-component-not-download-any-files.adoc | 8 + .../why-does-maven-not-download-dependencies.adoc | 10 + ...-file-consumer-use-the-camel-error-handler.adoc | 51 +++ ...jms-route-only-consume-one-message-at-once.adoc | 21 + ...ge-with-error-handler-not-work-as-expected.adoc | 16 + .../en/faq/why-is-my-message-body-empty.adoc | 19 + ...is-my-processor-not-showing-up-in-jconsole.adoc | 35 ++ ...-the-exception-null-when-i-use-onexception.adoc | 37 ++ .../en/faq/why-use-multiple-camelcontext.adoc | 16 + docs/user-manual/en/getting-started.adoc | 4 +- docs/user-manual/en/graceful-shutdown.adoc | 471 +++++++++++++++++++++ .../download.data/camel-box-v1.0-150x200.png | Bin 0 -> 93934 bytes docs/user-manual/en/jndi.adoc | 22 + docs/user-manual/en/languages.adoc | 2 +- docs/user-manual/en/predicate.adoc | 2 +- docs/user-manual/en/processor.adoc | 4 +- .../en/release-notes/camel-2170-release.adoc | 28 +- .../en/release-notes/camel-2180-release.adoc | 44 +- .../en/release-notes/camel-2190-release.adoc | 14 +- .../en/release-notes/camel-2200-release.adoc | 14 +- .../en/release-notes/camel-2210-release.adoc | 14 +- docs/user-manual/en/route-policy.adoc | 227 ++++++++++ docs/user-manual/en/scripting-languages.adoc | 2 +- docs/user-manual/en/spring-testing.adoc | 2 +- docs/user-manual/en/spring.adoc | 4 +- docs/user-manual/en/uuidgenerator.adoc | 80 ++++ docs/user-manual/en/vtd-xml.adoc | 51 --- .../resources/fabric8/hazelcast-deployment.yaml | 2 +- parent/pom.xml | 16 +- .../camel/generator/swagger/PathVisitor.java | 10 +- .../swagger/RestDslDefinitionGenerator.java | 2 +- .../swagger/RestDslSourceCodeGenerator.java | 2 +- .../generator/swagger/RestDslXmlGenerator.java | 3 +- .../generator/swagger/RestDslGeneratorTest.java | 2 + .../generator/swagger/RestDslXmlGeneratorTest.java | 2 +- .../src/test/resources/MyRestRoute.txt | 2 +- .../src/test/resources/MyRestRouteFilter.txt | 2 +- .../src/test/resources/SwaggerPetstore.txt | 2 +- .../resources/SwaggerPetstoreWithRestComponent.txt | 2 +- .../SwaggerPetstoreWithRestComponentXml.txt | 2 +- .../src/test/resources/SwaggerPetstoreXml.txt | 2 +- 142 files changed, 4282 insertions(+), 480 deletions(-) diff --cc camel-util/src/main/java/org/apache/camel/util/URISupport.java index 88fd074,0000000..539e416 mode 100644,000000..100644 --- a/camel-util/src/main/java/org/apache/camel/util/URISupport.java +++ b/camel-util/src/main/java/org/apache/camel/util/URISupport.java @@@ -1,691 -1,0 +1,691 @@@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.util; + +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; +import java.util.regex.Pattern; + +/** + * URI utilities. + */ +public final class URISupport { + + public static final String RAW_TOKEN_START = "RAW("; + public static final String RAW_TOKEN_END = ")"; + + // Match any key-value pair in the URI query string whose key contains + // "passphrase" or "password" or secret key (case-insensitive). + // First capture group is the key, second is the value. + private static final Pattern SECRETS = Pattern.compile("([?&][^=]*(?:passphrase|password|secretKey)[^=]*)=(RAW\\(.*\\)|[^&]*)", + Pattern.CASE_INSENSITIVE); + + // Match the user password in the URI as second capture group + // (applies to URI with authority component and userinfo token in the form "user:password"). + private static final Pattern USERINFO_PASSWORD = Pattern.compile("(.*://.*:)(.*)(@)"); + + // Match the user password in the URI path as second capture group + // (applies to URI path with authority component and userinfo token in the form "user:password"). + private static final Pattern PATH_USERINFO_PASSWORD = Pattern.compile("(.*:)(.*)(@)"); + + private static final String CHARSET = "UTF-8"; + + private URISupport() { + // Helper class + } + + /** + * Removes detected sensitive information (such as passwords) from the URI and returns the result. + * + * @param uri The uri to sanitize. + * @see #SECRETS and #USERINFO_PASSWORD for the matched pattern + * + * @return Returns null if the uri is null, otherwise the URI with the passphrase, password or secretKey sanitized. + */ + public static String sanitizeUri(String uri) { + // use xxxxx as replacement as that works well with JMX also + String sanitized = uri; + if (uri != null) { + sanitized = SECRETS.matcher(sanitized).replaceAll("$1=xxxxxx"); + sanitized = USERINFO_PASSWORD.matcher(sanitized).replaceFirst("$1xxxxxx$3"); + } + return sanitized; + } + + /** + * Removes detected sensitive information (such as passwords) from the + * <em>path part</em> of an URI (that is, the part without the query + * parameters or component prefix) and returns the result. + * + * @param path the URI path to sanitize + * @return null if the path is null, otherwise the sanitized path + */ + public static String sanitizePath(String path) { + String sanitized = path; + if (path != null) { + sanitized = PATH_USERINFO_PASSWORD.matcher(sanitized).replaceFirst("$1xxxxxx$3"); + } + return sanitized; + } + + /** + * Extracts the scheme specific path from the URI that is used as the remainder option when creating endpoints. + * + * @param u the URI + * @param useRaw whether to force using raw values + * @return the remainder path + */ + public static String extractRemainderPath(URI u, boolean useRaw) { + String path = useRaw ? u.getRawSchemeSpecificPart() : u.getSchemeSpecificPart(); + + // lets trim off any query arguments + if (path.startsWith("//")) { + path = path.substring(2); + } + int idx = path.indexOf('?'); + if (idx > -1) { + path = path.substring(0, idx); + } + + return path; + } + + /** + * 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 { + return parseQuery(uri, useRaw, 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 + * @param lenient whether to parse lenient and ignore trailing & markers which has no key or value which can happen when using HTTP components + * @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, boolean lenient) throws URISyntaxException { + // must check for trailing & as the uri.split("&") will ignore those + if (!lenient) { + if (uri != null && uri.endsWith("&")) { + throw new URISyntaxException(uri, "Invalid uri syntax: Trailing & marker found. " + + "Check the uri and remove the trailing & marker."); + } + } + + if (uri == null || ObjectHelper.isEmpty(uri)) { + // return an empty map + return new LinkedHashMap<>(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<>(); + + 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 + addParameter(key.toString(), 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) { + addParameter(key.toString(), value.toString(), rc, useRaw || isRaw); + } + + return rc; + + } catch (UnsupportedEncodingException e) { + URISyntaxException se = new URISyntaxException(e.toString(), "Invalid encoding"); + se.initCause(e); + throw se; + } + } + + 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 + String s = StringHelper.replaceAll(value, "%", "%25"); + value = URLDecoder.decode(s, 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 = CastUtils.cast((List<?>) existing); + } else { + // create a new list to hold the multiple values + list = new ArrayList<>(); + 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); + } + } + + /** + * 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<>(0); + } else { + query = schemeSpecificPart.substring(idx + 1); + } + } else { + query = stripPrefix(query, "?"); + } + return parseQuery(query); + } + + /** + * Traverses the given parameters, and resolve any parameter values which uses the RAW token + * syntax: <tt>key=RAW(value)</tt>. This method will then remove the RAW tokens, and replace + * the content of the value, with just the value. + * + * @param parameters the uri parameters + * @see #parseQuery(String) + * @see #RAW_TOKEN_START + * @see #RAW_TOKEN_END + */ + @SuppressWarnings("unchecked") + public static void resolveRawParameterValues(Map<String, Object> parameters) { + for (Map.Entry<String, Object> entry : parameters.entrySet()) { + if (entry.getValue() != null) { + // if the value is a list then we need to iterate + Object value = entry.getValue(); + if (value instanceof List) { + List list = (List) value; + for (int i = 0; i < list.size(); i++) { + Object obj = list.get(i); + if (obj != null) { + String str = obj.toString(); + if (str.startsWith(RAW_TOKEN_START) && str.endsWith(RAW_TOKEN_END)) { + str = str.substring(4, str.length() - 1); + // update the string in the list + list.set(i, str); + } + } + } + } else { + String str = entry.getValue().toString(); + if (str.startsWith(RAW_TOKEN_START) && str.endsWith(RAW_TOKEN_END)) { + str = str.substring(4, str.length() - 1); + entry.setValue(str); + } + } + } + } + } + + /** + * Creates a URI with the given query + * + * @param uri the uri + * @param query the query to append to the uri + * @return uri with the query appended + * @throws URISyntaxException is thrown if uri has invalid syntax. + */ + public static URI createURIWithQuery(URI uri, String query) throws URISyntaxException { + ObjectHelper.notNull(uri, "uri"); + + // assemble string as new uri and replace parameters with the query instead + String s = uri.toString(); + String before = StringHelper.before(s, "?"); + if (before == null) { + before = StringHelper.before(s, "#"); + } + if (before != null) { + s = before; + } + if (query != null) { + s = s + "?" + query; + } + if ((!s.contains("#")) && (uri.getFragment() != null)) { + s = s + "#" + uri.getFragment(); + } + + return new URI(s); + } + + /** + * 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 || prefix == null) { + return value; + } + + if (value.startsWith(prefix)) { + return value.substring(prefix.length()); + } + + return value; + } + + /** + * Strips the suffix from the value. + * <p/> + * Returns the value as-is if not ending with the prefix. + * + * @param value the value + * @param suffix the suffix to remove from value + * @return the value without the suffix + */ + public static String stripSuffix(final String value, final String suffix) { + if (value == null || suffix == null) { + return value; + } + + if (value.endsWith(suffix)) { + return value.substring(0, value.length() - suffix.length()); + } + + return value; + } + + /** + * Assembles a query from the given map. + * + * @param options the map with the options (eg key/value pairs) + * @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. + */ + @SuppressWarnings("unchecked") + public static String createQueryString(Map<String, Object> options) 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("&"); + } + + String key = (String) o; + Object value = options.get(key); + + // the value may be a list since the same key has multiple values + if (value instanceof List) { + List<String> list = (List<String>) value; + for (Iterator<String> it = list.iterator(); it.hasNext();) { + String s = it.next(); + appendQueryStringParameter(key, s, rc); + // append & separator if there is more in the list to append + if (it.hasNext()) { + rc.append("&"); + } + } + } else { + // use the value as a String + String s = value != null ? value.toString() : null; + appendQueryStringParameter(key, s, rc); + } + } + 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) throws UnsupportedEncodingException { + rc.append(URLEncoder.encode(key, CHARSET)); + // 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 unless it has % + // need to replace % with %25 to avoid losing "%" when decoding + String s = StringHelper.replaceAll(value, "%", "%25"); + rc.append(s); + } else { + rc.append(URLEncoder.encode(value, CHARSET)); + } + } + } + + /** + * Creates a URI from the original URI and the remaining parameters + * <p/> + * Used by various Camel components + */ + public static URI createRemainingURI(URI originalURI, Map<String, Object> params) throws URISyntaxException { + String s = createQueryString(params); + if (s.length() == 0) { + s = null; + } + return createURIWithQuery(originalURI, s); + } + + /** + * Appends the given parameters to the given URI. + * <p/> + * It keeps the original parameters and if a new parameter is already defined in + * {@code originalURI}, it will be replaced by its value in {@code newParameters}. + * + * @param originalURI the original URI + * @param newParameters the parameters to add + * @return the URI with all the parameters + * @throws URISyntaxException is thrown if the uri syntax is invalid + * @throws UnsupportedEncodingException is thrown if encoding error + */ + public static String appendParametersToURI(String originalURI, Map<String, Object> newParameters) throws URISyntaxException, UnsupportedEncodingException { + URI uri = new URI(normalizeUri(originalURI)); + Map<String, Object> parameters = parseParameters(uri); + parameters.putAll(newParameters); + return createRemainingURI(uri, parameters).toString(); + } + + /** + * Normalizes the uri by reordering the parameters so they are sorted and thus + * we can use the uris for endpoint matching. + * <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 normalized uri + * @throws URISyntaxException in thrown if the uri syntax is invalid + * @throws UnsupportedEncodingException is thrown if encoding error + * @see #RAW_TOKEN_START + * @see #RAW_TOKEN_END + */ + public static String normalizeUri(String uri) throws URISyntaxException, UnsupportedEncodingException { + + URI u = new URI(UnsafeUriCharactersEncoder.encode(uri, true)); + String path = u.getSchemeSpecificPart(); + String scheme = u.getScheme(); + + // not possible to normalize + if (scheme == null || path == null) { + return uri; + } + + // lets trim off any query arguments + if (path.startsWith("//")) { + path = path.substring(2); + } + int idx = path.indexOf('?'); + // when the path has ? + if (idx != -1) { + path = path.substring(0, idx); + } + + if (u.getScheme().startsWith("http")) { + path = UnsafeUriCharactersEncoder.encodeHttpURI(path); + } else { + path = UnsafeUriCharactersEncoder.encode(path); + } + + // okay if we have user info in the path and they use @ in username or password, + // then we need to encode them (but leave the last @ sign before the hostname) + // this is needed as Camel end users may not encode their user info properly, but expect + // this to work out of the box with Camel, and hence we need to fix it for them + String userInfoPath = path; + if (userInfoPath.contains("/")) { + userInfoPath = userInfoPath.substring(0, userInfoPath.indexOf("/")); + } + if (StringHelper.countChar(userInfoPath, '@') > 1) { + int max = userInfoPath.lastIndexOf('@'); + String before = userInfoPath.substring(0, max); + // after must be from original path + String after = path.substring(max); + + // replace the @ with %40 + before = StringHelper.replaceAll(before, "@", "%40"); + path = before + after; + } + + // in case there are parameters we should reorder them + Map<String, Object> parameters = URISupport.parseParameters(u); + if (parameters.isEmpty()) { + // no parameters then just return + return buildUri(scheme, path, null); + } else { + // reorder parameters a..z + List<String> keys = new ArrayList<>(parameters.keySet()); + keys.sort(null); + + Map<String, Object> sorted = new LinkedHashMap<>(parameters.size()); + for (String key : keys) { + sorted.put(key, parameters.get(key)); + } + + // build uri object with sorted parameters + String query = URISupport.createQueryString(sorted); + return buildUri(scheme, path, query); + } + } + + private static String buildUri(String scheme, String path, String query) { + // must include :// to do a correct URI all components can work with + return scheme + "://" + path + (query != null ? "?" + query : ""); + } + + public static Map<String, Object> extractProperties(Map<String, Object> properties, String optionPrefix) { + Map<String, Object> rc = new LinkedHashMap<>(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; + } + + public static String pathAndQueryOf(final URI uri) { + final String path = uri.getPath(); + + String pathAndQuery = path; + if (ObjectHelper.isEmpty(path)) { + pathAndQuery = "/"; + } + + final String query = uri.getQuery(); + if (ObjectHelper.isNotEmpty(query)) { + pathAndQuery += "?" + query; + } + + return pathAndQuery; + } + + public static String joinPaths(final String... paths) { + if (paths == null || paths.length == 0) { + return ""; + } + + final StringBuilder joined = new StringBuilder(); + + boolean addedLast = false; - for (int i = paths.length -1 ; i >= 0 ; i--) { ++ for (int i = paths.length - 1; i >= 0; i--) { + String path = paths[i]; + if (ObjectHelper.isNotEmpty(path)) { + if (addedLast) { + path = stripSuffix(path, "/"); + } + + addedLast = true; + + if (path.charAt(0) == '/') { + joined.insert(0, path); + } else { + if (i > 0) { + joined.insert(0, '/').insert(1, path); + } else { + joined.insert(0, path); + } + } + } + } + + return joined.toString(); + } +} diff --cc components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/config_maps/KubernetesConfigMapsProducer.java index c5ee87c,0bad3f3..0c29a56 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/config_maps/KubernetesConfigMapsProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/config_maps/KubernetesConfigMapsProducer.java @@@ -105,11 -109,16 +105,16 @@@ public class KubernetesConfigMapsProduc protected void doGetConfigMap(Exchange exchange, String operation) throws Exception { ConfigMap configMap = null; String cfMapName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_CONFIGMAP_NAME, String.class); + String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); if (ObjectHelper.isEmpty(cfMapName)) { - LOG.error("Get a specific ConfigMap require specify a ConfigMap name"); + log.error("Get a specific ConfigMap require specify a ConfigMap name"); throw new IllegalArgumentException("Get a specific ConfigMap require specify a ConfigMap name"); } - configMap = getEndpoint().getKubernetesClient().configMaps().withName(cfMapName).get(); + if (namespaceName != null) { + configMap = getEndpoint().getKubernetesClient().configMaps().inNamespace(namespaceName).withName(cfMapName).get(); + } else { + configMap = getEndpoint().getKubernetesClient().configMaps().withName(cfMapName).get(); + } MessageHelper.copyHeaders(exchange.getIn(), exchange.getOut(), true); exchange.getOut().setBody(configMap);