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