001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.camel.util; 018 019import java.net.URI; 020import java.net.URISyntaxException; 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.Iterator; 024import java.util.LinkedHashMap; 025import java.util.List; 026import java.util.Map; 027import java.util.concurrent.atomic.AtomicLong; 028import java.util.regex.Matcher; 029import java.util.regex.Pattern; 030import java.util.regex.PatternSyntaxException; 031 032import org.apache.camel.CamelContext; 033import org.apache.camel.DelegateEndpoint; 034import org.apache.camel.Endpoint; 035import org.apache.camel.Exchange; 036import org.apache.camel.ExchangePattern; 037import org.apache.camel.Message; 038import org.apache.camel.PollingConsumer; 039import org.apache.camel.Processor; 040import org.apache.camel.ResolveEndpointFailedException; 041import org.apache.camel.Route; 042import org.apache.camel.spi.BrowsableEndpoint; 043import org.slf4j.Logger; 044import org.slf4j.LoggerFactory; 045 046import static org.apache.camel.util.JsonSchemaHelper.getPropertyDefaultValue; 047import static org.apache.camel.util.JsonSchemaHelper.getPropertyPrefix; 048import static org.apache.camel.util.JsonSchemaHelper.isPropertyMultiValue; 049import static org.apache.camel.util.JsonSchemaHelper.isPropertyRequired; 050import static org.apache.camel.util.ObjectHelper.after; 051 052/** 053 * Some helper methods for working with {@link Endpoint} instances 054 */ 055public final class EndpointHelper { 056 057 private static final Logger LOG = LoggerFactory.getLogger(EndpointHelper.class); 058 private static final AtomicLong ENDPOINT_COUNTER = new AtomicLong(0); 059 private static final Pattern SYNTAX_PATTERN = Pattern.compile("(\\w+)"); 060 061 private EndpointHelper() { 062 //Utility Class 063 } 064 065 /** 066 * Creates a {@link PollingConsumer} and polls all pending messages on the endpoint 067 * and invokes the given {@link Processor} to process each {@link Exchange} and then closes 068 * down the consumer and throws any exceptions thrown. 069 */ 070 public static void pollEndpoint(Endpoint endpoint, Processor processor, long timeout) throws Exception { 071 PollingConsumer consumer = endpoint.createPollingConsumer(); 072 try { 073 ServiceHelper.startService(consumer); 074 075 while (true) { 076 Exchange exchange = consumer.receive(timeout); 077 if (exchange == null) { 078 break; 079 } else { 080 processor.process(exchange); 081 } 082 } 083 } finally { 084 try { 085 ServiceHelper.stopAndShutdownService(consumer); 086 } catch (Exception e) { 087 LOG.warn("Failed to stop PollingConsumer: " + consumer + ". This example is ignored.", e); 088 } 089 } 090 } 091 092 /** 093 * Creates a {@link PollingConsumer} and polls all pending messages on the 094 * endpoint and invokes the given {@link Processor} to process each 095 * {@link Exchange} and then closes down the consumer and throws any 096 * exceptions thrown. 097 */ 098 public static void pollEndpoint(Endpoint endpoint, Processor processor) throws Exception { 099 pollEndpoint(endpoint, processor, 1000L); 100 } 101 102 /** 103 * Matches the endpoint with the given pattern. 104 * <p/> 105 * The endpoint will first resolve property placeholders using {@link CamelContext#resolvePropertyPlaceholders(String)}. 106 * <p/> 107 * The match rules are applied in this order: 108 * <ul> 109 * <li>exact match, returns true</li> 110 * <li>wildcard match (pattern ends with a * and the uri starts with the pattern), returns true</li> 111 * <li>regular expression match, returns true</li> 112 * <li>otherwise returns false</li> 113 * </ul> 114 * 115 * @param context the Camel context, if <tt>null</tt> then property placeholder resolution is skipped. 116 * @param uri the endpoint uri 117 * @param pattern a pattern to match 118 * @return <tt>true</tt> if match, <tt>false</tt> otherwise. 119 */ 120 public static boolean matchEndpoint(CamelContext context, String uri, String pattern) { 121 if (context != null) { 122 try { 123 uri = context.resolvePropertyPlaceholders(uri); 124 } catch (Exception e) { 125 throw new ResolveEndpointFailedException(uri, e); 126 } 127 } 128 129 // normalize uri so we can do endpoint hits with minor mistakes and parameters is not in the same order 130 try { 131 uri = URISupport.normalizeUri(uri); 132 } catch (Exception e) { 133 throw new ResolveEndpointFailedException(uri, e); 134 } 135 136 // we need to test with and without scheme separators (//) 137 if (uri.contains("://")) { 138 // try without :// also 139 String scheme = ObjectHelper.before(uri, "://"); 140 String path = after(uri, "://"); 141 if (matchPattern(scheme + ":" + path, pattern)) { 142 return true; 143 } 144 } else { 145 // try with :// also 146 String scheme = ObjectHelper.before(uri, ":"); 147 String path = after(uri, ":"); 148 if (matchPattern(scheme + "://" + path, pattern)) { 149 return true; 150 } 151 } 152 153 // and fallback to test with the uri as is 154 return matchPattern(uri, pattern); 155 } 156 157 /** 158 * Matches the endpoint with the given pattern. 159 * 160 * @see #matchEndpoint(org.apache.camel.CamelContext, String, String) 161 * @deprecated use {@link #matchEndpoint(org.apache.camel.CamelContext, String, String)} instead. 162 */ 163 @Deprecated 164 public static boolean matchEndpoint(String uri, String pattern) { 165 return matchEndpoint(null, uri, pattern); 166 } 167 168 /** 169 * Matches the name with the given pattern. 170 * <p/> 171 * The match rules are applied in this order: 172 * <ul> 173 * <li>exact match, returns true</li> 174 * <li>wildcard match (pattern ends with a * and the name starts with the pattern), returns true</li> 175 * <li>regular expression match, returns true</li> 176 * <li>otherwise returns false</li> 177 * </ul> 178 * 179 * @param name the name 180 * @param pattern a pattern to match 181 * @return <tt>true</tt> if match, <tt>false</tt> otherwise. 182 */ 183 public static boolean matchPattern(String name, String pattern) { 184 if (name == null || pattern == null) { 185 return false; 186 } 187 188 if (name.equals(pattern)) { 189 // exact match 190 return true; 191 } 192 193 if (matchWildcard(name, pattern)) { 194 return true; 195 } 196 197 if (matchRegex(name, pattern)) { 198 return true; 199 } 200 201 // no match 202 return false; 203 } 204 205 /** 206 * Matches the name with the given pattern. 207 * <p/> 208 * The match rules are applied in this order: 209 * <ul> 210 * <li>wildcard match (pattern ends with a * and the name starts with the pattern), returns true</li> 211 * <li>otherwise returns false</li> 212 * </ul> 213 * 214 * @param name the name 215 * @param pattern a pattern to match 216 * @return <tt>true</tt> if match, <tt>false</tt> otherwise. 217 */ 218 private static boolean matchWildcard(String name, String pattern) { 219 // we have wildcard support in that hence you can match with: file* to match any file endpoints 220 if (pattern.endsWith("*") && name.startsWith(pattern.substring(0, pattern.length() - 1))) { 221 return true; 222 } 223 return false; 224 } 225 226 /** 227 * Matches the name with the given pattern. 228 * <p/> 229 * The match rules are applied in this order: 230 * <ul> 231 * <li>regular expression match, returns true</li> 232 * <li>otherwise returns false</li> 233 * </ul> 234 * 235 * @param name the name 236 * @param pattern a pattern to match 237 * @return <tt>true</tt> if match, <tt>false</tt> otherwise. 238 */ 239 private static boolean matchRegex(String name, String pattern) { 240 // match by regular expression 241 try { 242 if (name.matches(pattern)) { 243 return true; 244 } 245 } catch (PatternSyntaxException e) { 246 // ignore 247 } 248 return false; 249 } 250 251 /** 252 * Sets the regular properties on the given bean 253 * 254 * @param context the camel context 255 * @param bean the bean 256 * @param parameters parameters 257 * @throws Exception is thrown if setting property fails 258 */ 259 public static void setProperties(CamelContext context, Object bean, Map<String, Object> parameters) throws Exception { 260 IntrospectionSupport.setProperties(context.getTypeConverter(), bean, parameters); 261 } 262 263 /** 264 * Sets the reference properties on the given bean 265 * <p/> 266 * This is convention over configuration, setting all reference parameters (using {@link #isReferenceParameter(String)} 267 * by looking it up in registry and setting it on the bean if possible. 268 * 269 * @param context the camel context 270 * @param bean the bean 271 * @param parameters parameters 272 * @throws Exception is thrown if setting property fails 273 */ 274 public static void setReferenceProperties(CamelContext context, Object bean, Map<String, Object> parameters) throws Exception { 275 Iterator<Map.Entry<String, Object>> it = parameters.entrySet().iterator(); 276 while (it.hasNext()) { 277 Map.Entry<String, Object> entry = it.next(); 278 String name = entry.getKey(); 279 Object v = entry.getValue(); 280 String value = v != null ? v.toString() : null; 281 if (value != null && isReferenceParameter(value)) { 282 boolean hit = IntrospectionSupport.setProperty(context, context.getTypeConverter(), bean, name, null, value, true); 283 if (hit) { 284 // must remove as its a valid option and we could configure it 285 it.remove(); 286 } 287 } 288 } 289 } 290 291 /** 292 * Is the given parameter a reference parameter (starting with a # char) 293 * 294 * @param parameter the parameter 295 * @return <tt>true</tt> if its a reference parameter 296 */ 297 public static boolean isReferenceParameter(String parameter) { 298 return parameter != null && parameter.trim().startsWith("#"); 299 } 300 301 /** 302 * Resolves a reference parameter by making a lookup in the registry. 303 * 304 * @param <T> type of object to lookup. 305 * @param context Camel context to use for lookup. 306 * @param value reference parameter value. 307 * @param type type of object to lookup. 308 * @return lookup result. 309 * @throws IllegalArgumentException if referenced object was not found in registry. 310 */ 311 public static <T> T resolveReferenceParameter(CamelContext context, String value, Class<T> type) { 312 return resolveReferenceParameter(context, value, type, true); 313 } 314 315 /** 316 * Resolves a reference parameter by making a lookup in the registry. 317 * 318 * @param <T> type of object to lookup. 319 * @param context Camel context to use for lookup. 320 * @param value reference parameter value. 321 * @param type type of object to lookup. 322 * @return lookup result (or <code>null</code> only if 323 * <code>mandatory</code> is <code>false</code>). 324 * @throws IllegalArgumentException if object was not found in registry and 325 * <code>mandatory</code> is <code>true</code>. 326 */ 327 public static <T> T resolveReferenceParameter(CamelContext context, String value, Class<T> type, boolean mandatory) { 328 String valueNoHash = StringHelper.replaceAll(value, "#", ""); 329 if (mandatory) { 330 return CamelContextHelper.mandatoryLookup(context, valueNoHash, type); 331 } else { 332 return CamelContextHelper.lookup(context, valueNoHash, type); 333 } 334 } 335 336 /** 337 * Resolves a reference list parameter by making lookups in the registry. 338 * The parameter value must be one of the following: 339 * <ul> 340 * <li>a comma-separated list of references to beans of type T</li> 341 * <li>a single reference to a bean type T</li> 342 * <li>a single reference to a bean of type java.util.List</li> 343 * </ul> 344 * 345 * @param context Camel context to use for lookup. 346 * @param value reference parameter value. 347 * @param elementType result list element type. 348 * @return list of lookup results, will always return a list. 349 * @throws IllegalArgumentException if any referenced object was not found in registry. 350 */ 351 @SuppressWarnings({"unchecked", "rawtypes"}) 352 public static <T> List<T> resolveReferenceListParameter(CamelContext context, String value, Class<T> elementType) { 353 if (value == null) { 354 return new ArrayList<T>(); 355 } 356 List<String> elements = Arrays.asList(value.split(",")); 357 if (elements.size() == 1) { 358 Object bean = resolveReferenceParameter(context, elements.get(0).trim(), Object.class); 359 if (bean instanceof List) { 360 // The bean is a list 361 return (List) bean; 362 } else { 363 // The bean is a list element 364 return Arrays.asList(elementType.cast(bean)); 365 } 366 } else { // more than one list element 367 List<T> result = new ArrayList<T>(elements.size()); 368 for (String element : elements) { 369 result.add(resolveReferenceParameter(context, element.trim(), elementType)); 370 } 371 return result; 372 } 373 } 374 375 /** 376 * Resolves a parameter, by doing a reference lookup if the parameter is a reference, and converting 377 * the parameter to the given type. 378 * 379 * @param <T> type of object to convert the parameter value as. 380 * @param context Camel context to use for lookup. 381 * @param value parameter or reference parameter value. 382 * @param type type of object to lookup. 383 * @return lookup result if it was a reference parameter, or the value converted to the given type 384 * @throws IllegalArgumentException if referenced object was not found in registry. 385 */ 386 public static <T> T resolveParameter(CamelContext context, String value, Class<T> type) { 387 T result; 388 if (EndpointHelper.isReferenceParameter(value)) { 389 result = EndpointHelper.resolveReferenceParameter(context, value, type); 390 } else { 391 result = context.getTypeConverter().convertTo(type, value); 392 } 393 return result; 394 } 395 396 /** 397 * @deprecated use {@link #resolveParameter(org.apache.camel.CamelContext, String, Class)} 398 */ 399 @Deprecated 400 public static <T> T resloveStringParameter(CamelContext context, String value, Class<T> type) { 401 return resolveParameter(context, value, type); 402 } 403 404 /** 405 * Gets the route id for the given endpoint in which there is a consumer listening. 406 * 407 * @param endpoint the endpoint 408 * @return the route id, or <tt>null</tt> if none found 409 */ 410 public static String getRouteIdFromEndpoint(Endpoint endpoint) { 411 if (endpoint == null || endpoint.getCamelContext() == null) { 412 return null; 413 } 414 415 List<Route> routes = endpoint.getCamelContext().getRoutes(); 416 for (Route route : routes) { 417 if (route.getEndpoint().equals(endpoint) 418 || route.getEndpoint().getEndpointKey().equals(endpoint.getEndpointKey())) { 419 return route.getId(); 420 } 421 } 422 return null; 423 } 424 425 /** 426 * A helper method for Endpoint implementations to create new Ids for Endpoints which also implement 427 * {@link org.apache.camel.spi.HasId} 428 */ 429 public static String createEndpointId() { 430 return "endpoint" + ENDPOINT_COUNTER.incrementAndGet(); 431 } 432 433 /** 434 * Lookup the id the given endpoint has been enlisted with in the {@link org.apache.camel.spi.Registry}. 435 * 436 * @param endpoint the endpoint 437 * @return the endpoint id, or <tt>null</tt> if not found 438 */ 439 public static String lookupEndpointRegistryId(Endpoint endpoint) { 440 if (endpoint == null || endpoint.getCamelContext() == null) { 441 return null; 442 } 443 444 // it may be a delegate endpoint, which we need to match as well 445 Endpoint delegate = null; 446 if (endpoint instanceof DelegateEndpoint) { 447 delegate = ((DelegateEndpoint) endpoint).getEndpoint(); 448 } 449 450 Map<String, Endpoint> map = endpoint.getCamelContext().getRegistry().findByTypeWithName(Endpoint.class); 451 for (Map.Entry<String, Endpoint> entry : map.entrySet()) { 452 if (entry.getValue().equals(endpoint) || entry.getValue().equals(delegate)) { 453 return entry.getKey(); 454 } 455 } 456 457 // not found 458 return null; 459 } 460 461 /** 462 * Browses the {@link BrowsableEndpoint} within the given range, and returns the messages as a XML payload. 463 * 464 * @param endpoint the browsable endpoint 465 * @param fromIndex from range 466 * @param toIndex to range 467 * @param includeBody whether to include the message body in the XML payload 468 * @return XML payload with the messages 469 * @throws IllegalArgumentException if the from and to range is invalid 470 * @see MessageHelper#dumpAsXml(org.apache.camel.Message) 471 */ 472 public static String browseRangeMessagesAsXml(BrowsableEndpoint endpoint, Integer fromIndex, Integer toIndex, Boolean includeBody) { 473 if (fromIndex == null) { 474 fromIndex = 0; 475 } 476 if (toIndex == null) { 477 toIndex = Integer.MAX_VALUE; 478 } 479 if (fromIndex > toIndex) { 480 throw new IllegalArgumentException("From index cannot be larger than to index, was: " + fromIndex + " > " + toIndex); 481 } 482 483 List<Exchange> exchanges = endpoint.getExchanges(); 484 if (exchanges.size() == 0) { 485 return null; 486 } 487 488 StringBuilder sb = new StringBuilder(); 489 sb.append("<messages>"); 490 for (int i = fromIndex; i < exchanges.size() && i <= toIndex; i++) { 491 Exchange exchange = exchanges.get(i); 492 Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn(); 493 String xml = MessageHelper.dumpAsXml(msg, includeBody); 494 sb.append("\n").append(xml); 495 } 496 sb.append("\n</messages>"); 497 return sb.toString(); 498 } 499 500 /** 501 * Attempts to resolve if the url has an <tt>exchangePattern</tt> option configured 502 * 503 * @param url the url 504 * @return the exchange pattern, or <tt>null</tt> if the url has no <tt>exchangePattern</tt> configured. 505 * @throws URISyntaxException is thrown if uri is invalid 506 */ 507 public static ExchangePattern resolveExchangePatternFromUrl(String url) throws URISyntaxException { 508 int idx = url.indexOf("?"); 509 if (idx > 0) { 510 url = url.substring(idx + 1); 511 } 512 Map<String, Object> parameters = URISupport.parseQuery(url, true); 513 String pattern = (String) parameters.get("exchangePattern"); 514 if (pattern != null) { 515 return ExchangePattern.asEnum(pattern); 516 } 517 return null; 518 } 519 520 /** 521 * Parses the endpoint uri and builds a map of documentation information for each option which is extracted 522 * from the component json documentation 523 * 524 * @param camelContext the Camel context 525 * @param uri the endpoint uri 526 * @return a map for each option in the uri with the corresponding information from the json 527 * @throws Exception is thrown in case of error 528 */ 529 // CHECKSTYLE:OFF 530 public static Map<String, Object> endpointProperties(CamelContext camelContext, String uri) throws Exception { 531 // NOTICE: This logic is similar to org.apache.camel.util.EndpointHelper#endpointProperties 532 // as the catalog also offers similar functionality (without having camel-core on classpath) 533 534 // need to normalize uri first 535 536 // parse the uri 537 URI u = normalizeUri(uri); 538 String scheme = u.getScheme(); 539 540 String json = camelContext.getComponentParameterJsonSchema(u.getScheme()); 541 if (json == null) { 542 throw new IllegalArgumentException("Cannot find endpoint with scheme " + scheme); 543 } 544 545 // grab the syntax 546 String syntax = null; 547 String alternativeSyntax = null; 548 List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema("component", json, false); 549 for (Map<String, String> row : rows) { 550 if (row.containsKey("syntax")) { 551 syntax = row.get("syntax"); 552 } 553 if (row.containsKey("alternativeSyntax")) { 554 alternativeSyntax = row.get("alternativeSyntax"); 555 } 556 } 557 if (syntax == null) { 558 throw new IllegalArgumentException("Endpoint with scheme " + scheme + " has no syntax defined in the json schema"); 559 } 560 561 // only if we support alternative syntax, and the uri contains the username and password in the authority 562 // part of the uri, then we would need some special logic to capture that information and strip those 563 // details from the uri, so we can continue parsing the uri using the normal syntax 564 Map<String, String> userInfoOptions = new LinkedHashMap<String, String>(); 565 if (alternativeSyntax != null && alternativeSyntax.contains("@")) { 566 // clip the scheme from the syntax 567 alternativeSyntax = after(alternativeSyntax, ":"); 568 // trim so only userinfo 569 int idx = alternativeSyntax.indexOf("@"); 570 String fields = alternativeSyntax.substring(0, idx); 571 String[] names = fields.split(":"); 572 573 // grab authority part and grab username and/or password 574 String authority = u.getAuthority(); 575 if (authority != null && authority.contains("@")) { 576 String username = null; 577 String password = null; 578 579 // grab unserinfo part before @ 580 String userInfo = authority.substring(0, authority.indexOf("@")); 581 String[] parts = userInfo.split(":"); 582 if (parts.length == 2) { 583 username = parts[0]; 584 password = parts[1]; 585 } else { 586 // only username 587 username = userInfo; 588 } 589 590 // remember the username and/or password which we add later to the options 591 if (names.length == 2) { 592 userInfoOptions.put(names[0], username); 593 if (password != null) { 594 // password is optional 595 userInfoOptions.put(names[1], password); 596 } 597 } 598 } 599 } 600 601 // clip the scheme from the syntax 602 syntax = after(syntax, ":"); 603 // clip the scheme from the uri 604 uri = after(uri, ":"); 605 String uriPath = stripQuery(uri); 606 607 // strip user info from uri path 608 if (!userInfoOptions.isEmpty()) { 609 int idx = uriPath.indexOf('@'); 610 if (idx > -1) { 611 uriPath = uriPath.substring(idx + 1); 612 } 613 } 614 615 // strip double slash in the start 616 if (uriPath != null && uriPath.startsWith("//")) { 617 uriPath = uriPath.substring(2); 618 } 619 620 // parse the syntax and find the names of each option 621 Matcher matcher = SYNTAX_PATTERN.matcher(syntax); 622 List<String> word = new ArrayList<String>(); 623 while (matcher.find()) { 624 String s = matcher.group(1); 625 if (!scheme.equals(s)) { 626 word.add(s); 627 } 628 } 629 // parse the syntax and find each token between each option 630 String[] tokens = SYNTAX_PATTERN.split(syntax); 631 632 // find the position where each option start/end 633 List<String> word2 = new ArrayList<String>(); 634 int prev = 0; 635 int prevPath = 0; 636 637 // special for activemq/jms where the enum for destinationType causes a token issue as it includes a colon 638 // for 'temp:queue' and 'temp:topic' values 639 if ("activemq".equals(scheme) || "jms".equals("scheme")) { 640 if (uriPath.startsWith("temp:")) { 641 prevPath = 5; 642 } 643 } 644 645 for (String token : tokens) { 646 if (token.isEmpty()) { 647 continue; 648 } 649 650 // special for some tokens where :// can be used also, eg http://foo 651 int idx = -1; 652 int len = 0; 653 if (":".equals(token)) { 654 idx = uriPath.indexOf("://", prevPath); 655 len = 3; 656 } 657 if (idx == -1) { 658 idx = uriPath.indexOf(token, prevPath); 659 len = token.length(); 660 } 661 662 if (idx > 0) { 663 String option = uriPath.substring(prev, idx); 664 word2.add(option); 665 prev = idx + len; 666 prevPath = prev; 667 } 668 } 669 // special for last or if we did not add anyone 670 if (prev > 0 || word2.isEmpty()) { 671 String option = uriPath.substring(prev); 672 word2.add(option); 673 } 674 675 rows = JsonSchemaHelper.parseJsonSchema("properties", json, true); 676 677 boolean defaultValueAdded = false; 678 679 // now parse the uri to know which part isw what 680 Map<String, String> options = new LinkedHashMap<String, String>(); 681 682 // include the username and password from the userinfo section 683 if (!userInfoOptions.isEmpty()) { 684 options.putAll(userInfoOptions); 685 } 686 687 // word contains the syntax path elements 688 Iterator<String> it = word2.iterator(); 689 for (int i = 0; i < word.size(); i++) { 690 String key = word.get(i); 691 692 boolean allOptions = word.size() == word2.size(); 693 boolean required = isPropertyRequired(rows, key); 694 String defaultValue = getPropertyDefaultValue(rows, key); 695 696 // we have all options so no problem 697 if (allOptions) { 698 String value = it.next(); 699 options.put(key, value); 700 } else { 701 // we have a little problem as we do not not have all options 702 if (!required) { 703 String value = null; 704 705 boolean last = i == word.size() - 1; 706 if (last) { 707 // if its the last value then use it instead of the default value 708 value = it.hasNext() ? it.next() : null; 709 if (value != null) { 710 options.put(key, value); 711 } else { 712 value = defaultValue; 713 } 714 } 715 if (value != null) { 716 options.put(key, value); 717 defaultValueAdded = true; 718 } 719 } else { 720 String value = it.hasNext() ? it.next() : null; 721 if (value != null) { 722 options.put(key, value); 723 } 724 } 725 } 726 } 727 728 Map<String, Object> answer = new LinkedHashMap<String, Object>(); 729 730 // remove all options which are using default values and are not required 731 for (Map.Entry<String, String> entry : options.entrySet()) { 732 String key = entry.getKey(); 733 String value = entry.getValue(); 734 735 if (defaultValueAdded) { 736 boolean required = isPropertyRequired(rows, key); 737 String defaultValue = getPropertyDefaultValue(rows, key); 738 739 if (!required && defaultValue != null) { 740 if (defaultValue.equals(value)) { 741 continue; 742 } 743 } 744 } 745 746 // we should keep this in the answer 747 answer.put(key, value); 748 } 749 750 // now parse the uri parameters 751 Map<String, Object> parameters = URISupport.parseParameters(u); 752 753 // and covert the values to String so its JMX friendly 754 while (!parameters.isEmpty()) { 755 Map.Entry<String, Object> entry = parameters.entrySet().iterator().next(); 756 String key = entry.getKey(); 757 String value = entry.getValue() != null ? entry.getValue().toString() : ""; 758 759 boolean multiValued = isPropertyMultiValue(rows, key); 760 if (multiValued) { 761 String prefix = getPropertyPrefix(rows, key); 762 // extra all the multi valued options 763 Map<String, Object> values = URISupport.extractProperties(parameters, prefix); 764 // build a string with the extra multi valued options with the prefix and & as separator 765 CollectionStringBuffer csb = new CollectionStringBuffer("&"); 766 for (Map.Entry<String, Object> multi : values.entrySet()) { 767 String line = prefix + multi.getKey() + "=" + (multi.getValue() != null ? multi.getValue().toString() : ""); 768 csb.append(line); 769 } 770 // append the extra multi-values to the existing (which contains the first multi value) 771 if (!csb.isEmpty()) { 772 value = value + "&" + csb.toString(); 773 } 774 } 775 776 answer.put(key, value); 777 // remove the parameter as we run in a while loop until no more parameters 778 parameters.remove(key); 779 } 780 781 return answer; 782 } 783 // CHECKSTYLE:ON 784 785 /** 786 * Normalizes the URI so unsafe characters is encoded 787 * 788 * @param uri the input uri 789 * @return as URI instance 790 * @throws URISyntaxException is thrown if syntax error in the input uri 791 */ 792 private static URI normalizeUri(String uri) throws URISyntaxException { 793 return new URI(UnsafeUriCharactersEncoder.encode(uri, true)); 794 } 795 796 /** 797 * Strips the query parameters from the uri 798 * 799 * @param uri the uri 800 * @return the uri without the query parameter 801 */ 802 private static String stripQuery(String uri) { 803 int idx = uri.indexOf('?'); 804 if (idx > -1) { 805 uri = uri.substring(0, idx); 806 } 807 return uri; 808 } 809 810}