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.model.rest; 018 019import java.net.URISyntaxException; 020import java.util.ArrayList; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026import javax.xml.bind.annotation.XmlAccessType; 027import javax.xml.bind.annotation.XmlAccessorType; 028import javax.xml.bind.annotation.XmlAttribute; 029import javax.xml.bind.annotation.XmlElementRef; 030import javax.xml.bind.annotation.XmlRootElement; 031 032import org.apache.camel.CamelContext; 033import org.apache.camel.model.OptionalIdentifiedDefinition; 034import org.apache.camel.model.ProcessorDefinition; 035import org.apache.camel.model.RouteDefinition; 036import org.apache.camel.model.ToDefinition; 037import org.apache.camel.model.ToDynamicDefinition; 038import org.apache.camel.spi.Metadata; 039import org.apache.camel.spi.RestConfiguration; 040import org.apache.camel.util.FileUtil; 041import org.apache.camel.util.ObjectHelper; 042import org.apache.camel.util.URISupport; 043 044/** 045 * Defines a rest service using the rest-dsl 046 */ 047@Metadata(label = "rest") 048@XmlRootElement(name = "rest") 049@XmlAccessorType(XmlAccessType.FIELD) 050public class RestDefinition extends OptionalIdentifiedDefinition<RestDefinition> { 051 052 @XmlAttribute 053 private String path; 054 055 @XmlAttribute 056 private String tag; 057 058 @XmlAttribute 059 private String consumes; 060 061 @XmlAttribute 062 private String produces; 063 064 @XmlAttribute @Metadata(defaultValue = "auto") 065 private RestBindingMode bindingMode; 066 067 @XmlAttribute 068 private Boolean skipBindingOnErrorCode; 069 070 @XmlAttribute 071 private Boolean enableCORS; 072 073 @XmlAttribute 074 private Boolean apiDocs; 075 076 @XmlElementRef 077 private List<VerbDefinition> verbs = new ArrayList<VerbDefinition>(); 078 079 @Override 080 public String getLabel() { 081 return "rest"; 082 } 083 084 public String getPath() { 085 return path; 086 } 087 088 /** 089 * Path of the rest service, such as "/foo" 090 */ 091 public void setPath(String path) { 092 this.path = path; 093 } 094 095 public String getTag() { 096 return tag; 097 } 098 099 /** 100 * To configure a special tag for the operations within this rest definition. 101 */ 102 public void setTag(String tag) { 103 this.tag = tag; 104 } 105 106 public String getConsumes() { 107 return consumes; 108 } 109 110 /** 111 * To define the content type what the REST service consumes (accept as input), such as application/xml or application/json. 112 * This option will override what may be configured on a parent level 113 */ 114 public void setConsumes(String consumes) { 115 this.consumes = consumes; 116 } 117 118 public String getProduces() { 119 return produces; 120 } 121 122 /** 123 * To define the content type what the REST service produces (uses for output), such as application/xml or application/json 124 * This option will override what may be configured on a parent level 125 */ 126 public void setProduces(String produces) { 127 this.produces = produces; 128 } 129 130 public RestBindingMode getBindingMode() { 131 return bindingMode; 132 } 133 134 /** 135 * Sets the binding mode to use. 136 * This option will override what may be configured on a parent level 137 * <p/> 138 * The default value is auto 139 */ 140 public void setBindingMode(RestBindingMode bindingMode) { 141 this.bindingMode = bindingMode; 142 } 143 144 public List<VerbDefinition> getVerbs() { 145 return verbs; 146 } 147 148 /** 149 * The HTTP verbs this REST service accepts and uses 150 */ 151 public void setVerbs(List<VerbDefinition> verbs) { 152 this.verbs = verbs; 153 } 154 155 public Boolean getSkipBindingOnErrorCode() { 156 return skipBindingOnErrorCode; 157 } 158 159 /** 160 * Whether to skip binding on output if there is a custom HTTP error code header. 161 * This allows to build custom error messages that do not bind to json / xml etc, as success messages otherwise will do. 162 * This option will override what may be configured on a parent level 163 */ 164 public void setSkipBindingOnErrorCode(Boolean skipBindingOnErrorCode) { 165 this.skipBindingOnErrorCode = skipBindingOnErrorCode; 166 } 167 168 public Boolean getEnableCORS() { 169 return enableCORS; 170 } 171 172 /** 173 * Whether to enable CORS headers in the HTTP response. 174 * This option will override what may be configured on a parent level 175 * <p/> 176 * The default value is false. 177 */ 178 public void setEnableCORS(Boolean enableCORS) { 179 this.enableCORS = enableCORS; 180 } 181 182 public Boolean getApiDocs() { 183 return apiDocs; 184 } 185 186 /** 187 * Whether to include or exclude the VerbDefinition in API documentation. 188 * This option will override what may be configured on a parent level 189 * <p/> 190 * The default value is true. 191 */ 192 public void setApiDocs(Boolean apiDocs) { 193 this.apiDocs = apiDocs; 194 } 195 196 // Fluent API 197 //------------------------------------------------------------------------- 198 199 /** 200 * To set the base path of this REST service 201 */ 202 public RestDefinition path(String path) { 203 setPath(path); 204 return this; 205 } 206 207 /** 208 * To set the tag to use of this REST service 209 */ 210 public RestDefinition tag(String tag) { 211 setTag(tag); 212 return this; 213 } 214 215 public RestDefinition get() { 216 return addVerb("get", null); 217 } 218 219 public RestDefinition get(String uri) { 220 return addVerb("get", uri); 221 } 222 223 public RestDefinition post() { 224 return addVerb("post", null); 225 } 226 227 public RestDefinition post(String uri) { 228 return addVerb("post", uri); 229 } 230 231 public RestDefinition put() { 232 return addVerb("put", null); 233 } 234 235 public RestDefinition put(String uri) { 236 return addVerb("put", uri); 237 } 238 239 public RestDefinition patch() { 240 return addVerb("patch", null); 241 } 242 243 public RestDefinition patch(String uri) { 244 return addVerb("patch", uri); 245 } 246 247 public RestDefinition delete() { 248 return addVerb("delete", null); 249 } 250 251 public RestDefinition delete(String uri) { 252 return addVerb("delete", uri); 253 } 254 255 public RestDefinition head() { 256 return addVerb("head", null); 257 } 258 259 public RestDefinition head(String uri) { 260 return addVerb("head", uri); 261 } 262 263 @Deprecated 264 public RestDefinition options() { 265 return addVerb("options", null); 266 } 267 268 @Deprecated 269 public RestDefinition options(String uri) { 270 return addVerb("options", uri); 271 } 272 273 public RestDefinition verb(String verb) { 274 return addVerb(verb, null); 275 } 276 277 public RestDefinition verb(String verb, String uri) { 278 return addVerb(verb, uri); 279 } 280 281 @Override 282 public RestDefinition id(String id) { 283 if (getVerbs().isEmpty()) { 284 super.id(id); 285 } else { 286 // add on last verb as that is how the Java DSL works 287 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 288 verb.id(id); 289 } 290 291 return this; 292 } 293 294 @Override 295 public RestDefinition description(String text) { 296 if (getVerbs().isEmpty()) { 297 super.description(text); 298 } else { 299 // add on last verb as that is how the Java DSL works 300 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 301 verb.description(text); 302 } 303 304 return this; 305 } 306 307 @Override 308 public RestDefinition description(String id, String text, String lang) { 309 if (getVerbs().isEmpty()) { 310 super.description(id, text, lang); 311 } else { 312 // add on last verb as that is how the Java DSL works 313 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 314 verb.description(id, text, lang); 315 } 316 317 return this; 318 } 319 320 public RestDefinition consumes(String mediaType) { 321 if (getVerbs().isEmpty()) { 322 this.consumes = mediaType; 323 } else { 324 // add on last verb as that is how the Java DSL works 325 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 326 verb.setConsumes(mediaType); 327 } 328 329 return this; 330 } 331 332 public RestOperationParamDefinition param() { 333 if (getVerbs().isEmpty()) { 334 throw new IllegalArgumentException("Must add verb first, such as get/post/delete"); 335 } 336 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 337 return param(verb); 338 } 339 340 public RestDefinition param(RestOperationParamDefinition param) { 341 if (getVerbs().isEmpty()) { 342 throw new IllegalArgumentException("Must add verb first, such as get/post/delete"); 343 } 344 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 345 verb.getParams().add(param); 346 return this; 347 } 348 349 public RestDefinition params(List<RestOperationParamDefinition> params) { 350 if (getVerbs().isEmpty()) { 351 throw new IllegalArgumentException("Must add verb first, such as get/post/delete"); 352 } 353 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 354 verb.getParams().addAll(params); 355 return this; 356 } 357 358 public RestOperationParamDefinition param(VerbDefinition verb) { 359 return new RestOperationParamDefinition(verb); 360 } 361 362 public RestDefinition responseMessage(RestOperationResponseMsgDefinition msg) { 363 if (getVerbs().isEmpty()) { 364 throw new IllegalArgumentException("Must add verb first, such as get/post/delete"); 365 } 366 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 367 verb.getResponseMsgs().add(msg); 368 return this; 369 } 370 371 public RestOperationResponseMsgDefinition responseMessage() { 372 if (getVerbs().isEmpty()) { 373 throw new IllegalArgumentException("Must add verb first, such as get/post/delete"); 374 } 375 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 376 return responseMessage(verb); 377 } 378 379 public RestOperationResponseMsgDefinition responseMessage(VerbDefinition verb) { 380 return new RestOperationResponseMsgDefinition(verb); 381 } 382 383 public RestDefinition responseMessages(List<RestOperationResponseMsgDefinition> msgs) { 384 if (getVerbs().isEmpty()) { 385 throw new IllegalArgumentException("Must add verb first, such as get/post/delete"); 386 } 387 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 388 verb.getResponseMsgs().addAll(msgs); 389 return this; 390 } 391 392 public RestDefinition produces(String mediaType) { 393 if (getVerbs().isEmpty()) { 394 this.produces = mediaType; 395 } else { 396 // add on last verb as that is how the Java DSL works 397 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 398 verb.setProduces(mediaType); 399 } 400 401 return this; 402 } 403 404 public RestDefinition type(Class<?> classType) { 405 // add to last verb 406 if (getVerbs().isEmpty()) { 407 throw new IllegalArgumentException("Must add verb first, such as get/post/delete"); 408 } 409 410 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 411 verb.setType(classType.getCanonicalName()); 412 return this; 413 } 414 415 public RestDefinition typeList(Class<?> classType) { 416 // add to last verb 417 if (getVerbs().isEmpty()) { 418 throw new IllegalArgumentException("Must add verb first, such as get/post/delete"); 419 } 420 421 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 422 // list should end with [] to indicate array 423 verb.setType(classType.getCanonicalName() + "[]"); 424 return this; 425 } 426 427 public RestDefinition outType(Class<?> classType) { 428 // add to last verb 429 if (getVerbs().isEmpty()) { 430 throw new IllegalArgumentException("Must add verb first, such as get/post/delete"); 431 } 432 433 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 434 verb.setOutType(classType.getCanonicalName()); 435 return this; 436 } 437 438 public RestDefinition outTypeList(Class<?> classType) { 439 // add to last verb 440 if (getVerbs().isEmpty()) { 441 throw new IllegalArgumentException("Must add verb first, such as get/post/delete"); 442 } 443 444 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 445 // list should end with [] to indicate array 446 verb.setOutType(classType.getCanonicalName() + "[]"); 447 return this; 448 } 449 450 public RestDefinition bindingMode(RestBindingMode mode) { 451 if (getVerbs().isEmpty()) { 452 this.bindingMode = mode; 453 } else { 454 // add on last verb as that is how the Java DSL works 455 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 456 verb.setBindingMode(mode); 457 } 458 459 return this; 460 } 461 462 public RestDefinition skipBindingOnErrorCode(boolean skipBindingOnErrorCode) { 463 if (getVerbs().isEmpty()) { 464 this.skipBindingOnErrorCode = skipBindingOnErrorCode; 465 } else { 466 // add on last verb as that is how the Java DSL works 467 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 468 verb.setSkipBindingOnErrorCode(skipBindingOnErrorCode); 469 } 470 471 return this; 472 } 473 474 public RestDefinition enableCORS(boolean enableCORS) { 475 if (getVerbs().isEmpty()) { 476 this.enableCORS = enableCORS; 477 } else { 478 // add on last verb as that is how the Java DSL works 479 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 480 verb.setEnableCORS(enableCORS); 481 } 482 483 return this; 484 } 485 486 /** 487 * Include or exclude the current Rest Definition in API documentation. 488 * <p/> 489 * The default value is true. 490 */ 491 public RestDefinition apiDocs(Boolean apiDocs) { 492 if (getVerbs().isEmpty()) { 493 this.apiDocs = apiDocs; 494 } else { 495 // add on last verb as that is how the Java DSL works 496 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 497 verb.setApiDocs(apiDocs); 498 } 499 500 return this; 501 } 502 503 /** 504 * Routes directly to the given static endpoint. 505 * <p/> 506 * If you need additional routing capabilities, then use {@link #route()} instead. 507 * 508 * @param uri the uri of the endpoint 509 * @return this builder 510 */ 511 public RestDefinition to(String uri) { 512 // add to last verb 513 if (getVerbs().isEmpty()) { 514 throw new IllegalArgumentException("Must add verb first, such as get/post/delete"); 515 } 516 517 ToDefinition to = new ToDefinition(uri); 518 519 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 520 verb.setTo(to); 521 return this; 522 } 523 524 /** 525 * Routes directly to the given dynamic endpoint. 526 * <p/> 527 * If you need additional routing capabilities, then use {@link #route()} instead. 528 * 529 * @param uri the uri of the endpoint 530 * @return this builder 531 */ 532 public RestDefinition toD(String uri) { 533 // add to last verb 534 if (getVerbs().isEmpty()) { 535 throw new IllegalArgumentException("Must add verb first, such as get/post/delete"); 536 } 537 538 ToDynamicDefinition to = new ToDynamicDefinition(uri); 539 540 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 541 verb.setToD(to); 542 return this; 543 } 544 545 public RouteDefinition route() { 546 // add to last verb 547 if (getVerbs().isEmpty()) { 548 throw new IllegalArgumentException("Must add verb first, such as get/post/delete"); 549 } 550 551 // link them together so we can navigate using Java DSL 552 RouteDefinition route = new RouteDefinition(); 553 route.setRestDefinition(this); 554 VerbDefinition verb = getVerbs().get(getVerbs().size() - 1); 555 verb.setRoute(route); 556 return route; 557 } 558 559 // Implementation 560 //------------------------------------------------------------------------- 561 562 private RestDefinition addVerb(String verb, String uri) { 563 VerbDefinition answer; 564 565 if ("get".equals(verb)) { 566 answer = new GetVerbDefinition(); 567 } else if ("post".equals(verb)) { 568 answer = new PostVerbDefinition(); 569 } else if ("delete".equals(verb)) { 570 answer = new DeleteVerbDefinition(); 571 } else if ("head".equals(verb)) { 572 answer = new HeadVerbDefinition(); 573 } else if ("put".equals(verb)) { 574 answer = new PutVerbDefinition(); 575 } else if ("patch".equals(verb)) { 576 answer = new PatchVerbDefinition(); 577 } else if ("options".equals(verb)) { 578 answer = new OptionsVerbDefinition(); 579 } else { 580 answer = new VerbDefinition(); 581 answer.setMethod(verb); 582 } 583 getVerbs().add(answer); 584 answer.setRest(this); 585 answer.setUri(uri); 586 return this; 587 } 588 589 /** 590 * Transforms this REST definition into a list of {@link org.apache.camel.model.RouteDefinition} which 591 * Camel routing engine can add and run. This allows us to define REST services using this 592 * REST DSL and turn those into regular Camel routes. 593 */ 594 public List<RouteDefinition> asRouteDefinition(CamelContext camelContext) { 595 // sanity check this rest definition do not have duplicates 596 validateUniquePaths(); 597 598 List<RouteDefinition> answer = new ArrayList<RouteDefinition>(); 599 if (camelContext.getRestConfigurations().isEmpty()) { 600 camelContext.getRestConfiguration(); 601 } 602 for (RestConfiguration config : camelContext.getRestConfigurations()) { 603 addRouteDefinition(camelContext, answer, config.getComponent()); 604 } 605 return answer; 606 } 607 608 protected void validateUniquePaths() { 609 Set<String> paths = new HashSet<String>(); 610 for (VerbDefinition verb : verbs) { 611 String path = verb.asVerb(); 612 if (verb.getUri() != null) { 613 path += ":" + verb.getUri(); 614 } 615 if (!paths.add(path)) { 616 throw new IllegalArgumentException("Duplicate verb detected in rest-dsl: " + path); 617 } 618 } 619 } 620 621 /** 622 * Transforms the rest api configuration into a {@link org.apache.camel.model.RouteDefinition} which 623 * Camel routing engine uses to service the rest api docs. 624 */ 625 public static RouteDefinition asRouteApiDefinition(CamelContext camelContext, RestConfiguration configuration) { 626 RouteDefinition answer = new RouteDefinition(); 627 628 // create the from endpoint uri which is using the rest-api component 629 String from = "rest-api:" + configuration.getApiContextPath(); 630 631 // append options 632 Map<String, Object> options = new HashMap<String, Object>(); 633 634 String routeId = configuration.getApiContextRouteId(); 635 if (routeId == null) { 636 routeId = answer.idOrCreate(camelContext.getNodeIdFactory()); 637 } 638 options.put("routeId", routeId); 639 if (configuration.getComponent() != null && !configuration.getComponent().isEmpty()) { 640 options.put("componentName", configuration.getComponent()); 641 } 642 if (configuration.getApiContextIdPattern() != null) { 643 options.put("contextIdPattern", configuration.getApiContextIdPattern()); 644 } 645 646 if (!options.isEmpty()) { 647 String query; 648 try { 649 query = URISupport.createQueryString(options); 650 } catch (URISyntaxException e) { 651 throw ObjectHelper.wrapRuntimeCamelException(e); 652 } 653 from = from + "?" + query; 654 } 655 656 // we use the same uri as the producer (so we have a little route for the rest api) 657 String to = from; 658 answer.fromRest(from); 659 answer.id(routeId); 660 answer.to(to); 661 662 return answer; 663 } 664 665 private void addRouteDefinition(CamelContext camelContext, List<RouteDefinition> answer, String component) { 666 for (VerbDefinition verb : getVerbs()) { 667 // either the verb has a singular to or a embedded route 668 RouteDefinition route = verb.getRoute(); 669 if (route == null) { 670 // it was a singular to, so add a new route and add the singular 671 // to as output to this route 672 route = new RouteDefinition(); 673 ProcessorDefinition def = verb.getTo() != null ? verb.getTo() : verb.getToD(); 674 route.getOutputs().add(def); 675 } 676 677 // add the binding 678 RestBindingDefinition binding = new RestBindingDefinition(); 679 binding.setComponent(component); 680 binding.setType(verb.getType()); 681 binding.setOutType(verb.getOutType()); 682 // verb takes precedence over configuration on rest 683 if (verb.getConsumes() != null) { 684 binding.setConsumes(verb.getConsumes()); 685 } else { 686 binding.setConsumes(getConsumes()); 687 } 688 if (verb.getProduces() != null) { 689 binding.setProduces(verb.getProduces()); 690 } else { 691 binding.setProduces(getProduces()); 692 } 693 if (verb.getBindingMode() != null) { 694 binding.setBindingMode(verb.getBindingMode()); 695 } else { 696 binding.setBindingMode(getBindingMode()); 697 } 698 if (verb.getSkipBindingOnErrorCode() != null) { 699 binding.setSkipBindingOnErrorCode(verb.getSkipBindingOnErrorCode()); 700 } else { 701 binding.setSkipBindingOnErrorCode(getSkipBindingOnErrorCode()); 702 } 703 if (verb.getEnableCORS() != null) { 704 binding.setEnableCORS(verb.getEnableCORS()); 705 } else { 706 binding.setEnableCORS(getEnableCORS()); 707 } 708 // register all the default values for the query parameters 709 for (RestOperationParamDefinition param : verb.getParams()) { 710 if (RestParamType.query == param.getType() && param.getDefaultValue() != null) { 711 binding.addDefaultValue(param.getName(), param.getDefaultValue()); 712 } 713 } 714 715 route.getOutputs().add(0, binding); 716 717 // create the from endpoint uri which is using the rest component 718 String from = "rest:" + verb.asVerb() + ":" + buildUri(verb); 719 720 // append options 721 Map<String, Object> options = new HashMap<String, Object>(); 722 // verb takes precedence over configuration on rest 723 if (verb.getConsumes() != null) { 724 options.put("consumes", verb.getConsumes()); 725 } else if (getConsumes() != null) { 726 options.put("consumes", getConsumes()); 727 } 728 if (verb.getProduces() != null) { 729 options.put("produces", verb.getProduces()); 730 } else if (getProduces() != null) { 731 options.put("produces", getProduces()); 732 } 733 734 // append optional type binding information 735 String inType = binding.getType(); 736 if (inType != null) { 737 options.put("inType", inType); 738 } 739 String outType = binding.getOutType(); 740 if (outType != null) { 741 options.put("outType", outType); 742 } 743 // if no route id has been set, then use the verb id as route id 744 if (!route.hasCustomIdAssigned()) { 745 // use id of verb as route id 746 String id = verb.getId(); 747 if (id != null) { 748 route.setId(id); 749 } 750 } 751 String routeId = route.idOrCreate(camelContext.getNodeIdFactory()); 752 verb.setRouteId(routeId); 753 options.put("routeId", routeId); 754 if (component != null && !component.isEmpty()) { 755 options.put("componentName", component); 756 } 757 758 // include optional description, which we favor from 1) to/route description 2) verb description 3) rest description 759 // this allows end users to define general descriptions and override then per to/route or verb 760 String description = verb.getTo() != null ? verb.getTo().getDescriptionText() : route.getDescriptionText(); 761 if (description == null) { 762 description = verb.getDescriptionText(); 763 } 764 if (description == null) { 765 description = getDescriptionText(); 766 } 767 if (description != null) { 768 options.put("description", description); 769 } 770 771 if (!options.isEmpty()) { 772 String query; 773 try { 774 query = URISupport.createQueryString(options); 775 } catch (URISyntaxException e) { 776 throw ObjectHelper.wrapRuntimeCamelException(e); 777 } 778 from = from + "?" + query; 779 } 780 781 String path = getPath(); 782 String s1 = FileUtil.stripTrailingSeparator(path); 783 String s2 = FileUtil.stripLeadingSeparator(verb.getUri()); 784 String allPath; 785 if (s1 != null && s2 != null) { 786 allPath = s1 + "/" + s2; 787 } else if (path != null) { 788 allPath = path; 789 } else { 790 allPath = verb.getUri(); 791 } 792 793 // each {} is a parameter (url templating) 794 String[] arr = allPath.split("\\/"); 795 for (String a : arr) { 796 // need to resolve property placeholders first 797 try { 798 a = camelContext.resolvePropertyPlaceholders(a); 799 } catch (Exception e) { 800 throw ObjectHelper.wrapRuntimeCamelException(e); 801 } 802 if (a.startsWith("{") && a.endsWith("}")) { 803 String key = a.substring(1, a.length() - 1); 804 // merge if exists 805 boolean found = false; 806 for (RestOperationParamDefinition param : verb.getParams()) { 807 // name is mandatory 808 String name = param.getName(); 809 ObjectHelper.notEmpty(name, "parameter name"); 810 // need to resolve property placeholders first 811 try { 812 name = camelContext.resolvePropertyPlaceholders(name); 813 } catch (Exception e) { 814 throw ObjectHelper.wrapRuntimeCamelException(e); 815 } 816 if (name.equalsIgnoreCase(key)) { 817 param.type(RestParamType.path); 818 found = true; 819 break; 820 } 821 } 822 if (!found) { 823 param(verb).name(key).type(RestParamType.path).endParam(); 824 } 825 } 826 } 827 828 if (verb.getType() != null) { 829 String bodyType = verb.getType(); 830 if (bodyType.endsWith("[]")) { 831 bodyType = "List[" + bodyType.substring(0, bodyType.length() - 2) + "]"; 832 } 833 RestOperationParamDefinition param = findParam(verb, RestParamType.body.name()); 834 if (param == null) { 835 // must be body type and set the model class as data type 836 param(verb).name(RestParamType.body.name()).type(RestParamType.body).dataType(bodyType).endParam(); 837 } else { 838 // must be body type and set the model class as data type 839 param.type(RestParamType.body).dataType(bodyType); 840 } 841 } 842 843 // the route should be from this rest endpoint 844 route.fromRest(from); 845 route.id(routeId); 846 route.setRestDefinition(this); 847 answer.add(route); 848 } 849 } 850 851 private String buildUri(VerbDefinition verb) { 852 if (path != null && verb.getUri() != null) { 853 return path + ":" + verb.getUri(); 854 } else if (path != null) { 855 return path; 856 } else if (verb.getUri() != null) { 857 return verb.getUri(); 858 } else { 859 return ""; 860 } 861 } 862 863 private RestOperationParamDefinition findParam(VerbDefinition verb, String name) { 864 for (RestOperationParamDefinition param : verb.getParams()) { 865 if (name.equals(param.getName())) { 866 return param; 867 } 868 } 869 return null; 870 } 871 872}