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 * @param camelContext The Camel context 595 */ 596 public List<RouteDefinition> asRouteDefinition(CamelContext camelContext) { 597 ObjectHelper.notNull(camelContext, "CamelContext"); 598 599 // sanity check this rest definition do not have duplicates 600 validateUniquePaths(); 601 602 List<RouteDefinition> answer = new ArrayList<RouteDefinition>(); 603 if (camelContext.getRestConfigurations().isEmpty()) { 604 camelContext.getRestConfiguration(); 605 } 606 for (RestConfiguration config : camelContext.getRestConfigurations()) { 607 addRouteDefinition(camelContext, answer, config.getComponent()); 608 } 609 return answer; 610 } 611 612 /** 613 * Transforms this REST definition into a list of {@link org.apache.camel.model.RouteDefinition} which 614 * Camel routing engine can add and run. This allows us to define REST services using this 615 * REST DSL and turn those into regular Camel routes. 616 * 617 * @param camelContext The Camel context 618 * @param restConfiguration The rest configuration to use 619 */ 620 public List<RouteDefinition> asRouteDefinition(CamelContext camelContext, RestConfiguration restConfiguration) { 621 ObjectHelper.notNull(camelContext, "CamelContext"); 622 ObjectHelper.notNull(restConfiguration, "RestConfiguration"); 623 624 // sanity check this rest definition do not have duplicates 625 validateUniquePaths(); 626 627 List<RouteDefinition> answer = new ArrayList<RouteDefinition>(); 628 addRouteDefinition(camelContext, answer, restConfiguration.getComponent()); 629 return answer; 630 } 631 632 protected void validateUniquePaths() { 633 Set<String> paths = new HashSet<String>(); 634 for (VerbDefinition verb : verbs) { 635 String path = verb.asVerb(); 636 if (verb.getUri() != null) { 637 path += ":" + verb.getUri(); 638 } 639 if (!paths.add(path)) { 640 throw new IllegalArgumentException("Duplicate verb detected in rest-dsl: " + path); 641 } 642 } 643 } 644 645 /** 646 * Transforms the rest api configuration into a {@link org.apache.camel.model.RouteDefinition} which 647 * Camel routing engine uses to service the rest api docs. 648 */ 649 public static RouteDefinition asRouteApiDefinition(CamelContext camelContext, RestConfiguration configuration) { 650 RouteDefinition answer = new RouteDefinition(); 651 652 // create the from endpoint uri which is using the rest-api component 653 String from = "rest-api:" + configuration.getApiContextPath(); 654 655 // append options 656 Map<String, Object> options = new HashMap<String, Object>(); 657 658 String routeId = configuration.getApiContextRouteId(); 659 if (routeId == null) { 660 routeId = answer.idOrCreate(camelContext.getNodeIdFactory()); 661 } 662 options.put("routeId", routeId); 663 if (configuration.getComponent() != null && !configuration.getComponent().isEmpty()) { 664 options.put("componentName", configuration.getComponent()); 665 } 666 if (configuration.getApiContextIdPattern() != null) { 667 options.put("contextIdPattern", configuration.getApiContextIdPattern()); 668 } 669 670 if (!options.isEmpty()) { 671 String query; 672 try { 673 query = URISupport.createQueryString(options); 674 } catch (URISyntaxException e) { 675 throw ObjectHelper.wrapRuntimeCamelException(e); 676 } 677 from = from + "?" + query; 678 } 679 680 // we use the same uri as the producer (so we have a little route for the rest api) 681 String to = from; 682 answer.fromRest(from); 683 answer.id(routeId); 684 answer.to(to); 685 686 return answer; 687 } 688 689 private void addRouteDefinition(CamelContext camelContext, List<RouteDefinition> answer, String component) { 690 for (VerbDefinition verb : getVerbs()) { 691 // either the verb has a singular to or a embedded route 692 RouteDefinition route = verb.getRoute(); 693 if (route == null) { 694 // it was a singular to, so add a new route and add the singular 695 // to as output to this route 696 route = new RouteDefinition(); 697 ProcessorDefinition def = verb.getTo() != null ? verb.getTo() : verb.getToD(); 698 route.getOutputs().add(def); 699 } 700 701 // add the binding 702 RestBindingDefinition binding = new RestBindingDefinition(); 703 binding.setComponent(component); 704 binding.setType(verb.getType()); 705 binding.setOutType(verb.getOutType()); 706 // verb takes precedence over configuration on rest 707 if (verb.getConsumes() != null) { 708 binding.setConsumes(verb.getConsumes()); 709 } else { 710 binding.setConsumes(getConsumes()); 711 } 712 if (verb.getProduces() != null) { 713 binding.setProduces(verb.getProduces()); 714 } else { 715 binding.setProduces(getProduces()); 716 } 717 if (verb.getBindingMode() != null) { 718 binding.setBindingMode(verb.getBindingMode()); 719 } else { 720 binding.setBindingMode(getBindingMode()); 721 } 722 if (verb.getSkipBindingOnErrorCode() != null) { 723 binding.setSkipBindingOnErrorCode(verb.getSkipBindingOnErrorCode()); 724 } else { 725 binding.setSkipBindingOnErrorCode(getSkipBindingOnErrorCode()); 726 } 727 if (verb.getEnableCORS() != null) { 728 binding.setEnableCORS(verb.getEnableCORS()); 729 } else { 730 binding.setEnableCORS(getEnableCORS()); 731 } 732 // register all the default values for the query parameters 733 for (RestOperationParamDefinition param : verb.getParams()) { 734 if (RestParamType.query == param.getType() && ObjectHelper.isNotEmpty(param.getDefaultValue())) { 735 binding.addDefaultValue(param.getName(), param.getDefaultValue()); 736 } 737 } 738 739 route.getOutputs().add(0, binding); 740 741 // create the from endpoint uri which is using the rest component 742 String from = "rest:" + verb.asVerb() + ":" + buildUri(verb); 743 744 // append options 745 Map<String, Object> options = new HashMap<String, Object>(); 746 // verb takes precedence over configuration on rest 747 if (verb.getConsumes() != null) { 748 options.put("consumes", verb.getConsumes()); 749 } else if (getConsumes() != null) { 750 options.put("consumes", getConsumes()); 751 } 752 if (verb.getProduces() != null) { 753 options.put("produces", verb.getProduces()); 754 } else if (getProduces() != null) { 755 options.put("produces", getProduces()); 756 } 757 758 // append optional type binding information 759 String inType = binding.getType(); 760 if (inType != null) { 761 options.put("inType", inType); 762 } 763 String outType = binding.getOutType(); 764 if (outType != null) { 765 options.put("outType", outType); 766 } 767 // if no route id has been set, then use the verb id as route id 768 if (!route.hasCustomIdAssigned()) { 769 // use id of verb as route id 770 String id = verb.getId(); 771 if (id != null) { 772 route.setId(id); 773 } 774 } 775 String routeId = route.idOrCreate(camelContext.getNodeIdFactory()); 776 verb.setRouteId(routeId); 777 options.put("routeId", routeId); 778 if (component != null && !component.isEmpty()) { 779 options.put("componentName", component); 780 } 781 782 // include optional description, which we favor from 1) to/route description 2) verb description 3) rest description 783 // this allows end users to define general descriptions and override then per to/route or verb 784 String description = verb.getTo() != null ? verb.getTo().getDescriptionText() : route.getDescriptionText(); 785 if (description == null) { 786 description = verb.getDescriptionText(); 787 } 788 if (description == null) { 789 description = getDescriptionText(); 790 } 791 if (description != null) { 792 options.put("description", description); 793 } 794 795 if (!options.isEmpty()) { 796 String query; 797 try { 798 query = URISupport.createQueryString(options); 799 } catch (URISyntaxException e) { 800 throw ObjectHelper.wrapRuntimeCamelException(e); 801 } 802 from = from + "?" + query; 803 } 804 805 String path = getPath(); 806 String s1 = FileUtil.stripTrailingSeparator(path); 807 String s2 = FileUtil.stripLeadingSeparator(verb.getUri()); 808 String allPath; 809 if (s1 != null && s2 != null) { 810 allPath = s1 + "/" + s2; 811 } else if (path != null) { 812 allPath = path; 813 } else { 814 allPath = verb.getUri(); 815 } 816 817 // each {} is a parameter (url templating) 818 String[] arr = allPath.split("\\/"); 819 for (String a : arr) { 820 // need to resolve property placeholders first 821 try { 822 a = camelContext.resolvePropertyPlaceholders(a); 823 } catch (Exception e) { 824 throw ObjectHelper.wrapRuntimeCamelException(e); 825 } 826 if (a.startsWith("{") && a.endsWith("}")) { 827 String key = a.substring(1, a.length() - 1); 828 // merge if exists 829 boolean found = false; 830 for (RestOperationParamDefinition param : verb.getParams()) { 831 // name is mandatory 832 String name = param.getName(); 833 ObjectHelper.notEmpty(name, "parameter name"); 834 // need to resolve property placeholders first 835 try { 836 name = camelContext.resolvePropertyPlaceholders(name); 837 } catch (Exception e) { 838 throw ObjectHelper.wrapRuntimeCamelException(e); 839 } 840 if (name.equalsIgnoreCase(key)) { 841 param.type(RestParamType.path); 842 found = true; 843 break; 844 } 845 } 846 if (!found) { 847 param(verb).name(key).type(RestParamType.path).endParam(); 848 } 849 } 850 } 851 852 if (verb.getType() != null) { 853 String bodyType = verb.getType(); 854 if (bodyType.endsWith("[]")) { 855 bodyType = "List[" + bodyType.substring(0, bodyType.length() - 2) + "]"; 856 } 857 RestOperationParamDefinition param = findParam(verb, RestParamType.body.name()); 858 if (param == null) { 859 // must be body type and set the model class as data type 860 param(verb).name(RestParamType.body.name()).type(RestParamType.body).dataType(bodyType).endParam(); 861 } else { 862 // must be body type and set the model class as data type 863 param.type(RestParamType.body).dataType(bodyType); 864 } 865 } 866 867 // the route should be from this rest endpoint 868 route.fromRest(from); 869 route.id(routeId); 870 route.setRestDefinition(this); 871 answer.add(route); 872 } 873 } 874 875 private String buildUri(VerbDefinition verb) { 876 if (path != null && verb.getUri() != null) { 877 return path + ":" + verb.getUri(); 878 } else if (path != null) { 879 return path; 880 } else if (verb.getUri() != null) { 881 return verb.getUri(); 882 } else { 883 return ""; 884 } 885 } 886 887 private RestOperationParamDefinition findParam(VerbDefinition verb, String name) { 888 for (RestOperationParamDefinition param : verb.getParams()) { 889 if (name.equals(param.getName())) { 890 return param; 891 } 892 } 893 return null; 894 } 895 896}