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