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