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.util.ArrayList; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023import javax.xml.bind.annotation.XmlAccessType; 024import javax.xml.bind.annotation.XmlAccessorType; 025import javax.xml.bind.annotation.XmlAttribute; 026import javax.xml.bind.annotation.XmlElement; 027import javax.xml.bind.annotation.XmlRootElement; 028 029import org.apache.camel.CamelContext; 030import org.apache.camel.spi.Metadata; 031import org.apache.camel.spi.RestConfiguration; 032import org.apache.camel.util.CamelContextHelper; 033 034/** 035 * To configure rest 036 */ 037@Metadata(label = "rest") 038@XmlRootElement(name = "restConfiguration") 039@XmlAccessorType(XmlAccessType.FIELD) 040public class RestConfigurationDefinition { 041 042 @XmlAttribute 043 private String component; 044 045 @XmlAttribute @Metadata(defaultValue = "swagger") 046 private String apiComponent; 047 048 @XmlAttribute 049 private String scheme; 050 051 @XmlAttribute 052 private String host; 053 054 @XmlAttribute 055 private String port; 056 057 @XmlAttribute 058 private String contextPath; 059 060 @XmlAttribute 061 private String apiContextPath; 062 063 @XmlAttribute 064 private String apiContextRouteId; 065 066 @XmlAttribute 067 private String apiContextIdPattern; 068 069 @XmlAttribute 070 private Boolean apiContextListing; 071 072 @XmlAttribute 073 private RestHostNameResolver hostNameResolver; 074 075 @XmlAttribute @Metadata(defaultValue = "off") 076 private RestBindingMode bindingMode; 077 078 @XmlAttribute 079 private Boolean skipBindingOnErrorCode; 080 081 @XmlAttribute 082 private Boolean enableCORS; 083 084 @XmlAttribute 085 private String jsonDataFormat; 086 087 @XmlAttribute 088 private String xmlDataFormat; 089 090 @XmlElement(name = "componentProperty") 091 private List<RestPropertyDefinition> componentProperties = new ArrayList<RestPropertyDefinition>(); 092 093 @XmlElement(name = "endpointProperty") 094 private List<RestPropertyDefinition> endpointProperties = new ArrayList<RestPropertyDefinition>(); 095 096 @XmlElement(name = "consumerProperty") 097 private List<RestPropertyDefinition> consumerProperties = new ArrayList<RestPropertyDefinition>(); 098 099 @XmlElement(name = "dataFormatProperty") 100 private List<RestPropertyDefinition> dataFormatProperties = new ArrayList<RestPropertyDefinition>(); 101 102 @XmlElement(name = "apiProperty") 103 private List<RestPropertyDefinition> apiProperties = new ArrayList<RestPropertyDefinition>(); 104 105 @XmlElement(name = "corsHeaders") 106 private List<RestPropertyDefinition> corsHeaders = new ArrayList<RestPropertyDefinition>(); 107 108 public String getComponent() { 109 return component; 110 } 111 112 /** 113 * The Camel Rest component to use for the REST transport, such as restlet, spark-rest. 114 * If no component has been explicit configured, then Camel will lookup if there is a Camel component 115 * that integrates with the Rest DSL, or if a org.apache.camel.spi.RestConsumerFactory is registered in the registry. 116 * If either one is found, then that is being used. 117 */ 118 public void setComponent(String component) { 119 this.component = component; 120 } 121 122 public String getApiComponent() { 123 return apiComponent; 124 } 125 126 /** 127 * The name of the Camel component to use as the REST API (such as swagger) 128 */ 129 public void setApiComponent(String apiComponent) { 130 this.apiComponent = apiComponent; 131 } 132 133 public String getScheme() { 134 return scheme; 135 } 136 137 /** 138 * The scheme to use for exposing the REST service. Usually http or https is supported. 139 * <p/> 140 * The default value is http 141 */ 142 public void setScheme(String scheme) { 143 this.scheme = scheme; 144 } 145 146 public String getHost() { 147 return host; 148 } 149 150 /** 151 * The hostname to use for exposing the REST service. 152 */ 153 public void setHost(String host) { 154 this.host = host; 155 } 156 157 public String getPort() { 158 return port; 159 } 160 161 /** 162 * The port number to use for exposing the REST service. 163 * Notice if you use servlet component then the port number configured here does not apply, 164 * as the port number in use is the actual port number the servlet component is using. 165 * eg if using Apache Tomcat its the tomcat http port, if using Apache Karaf its the HTTP service in Karaf 166 * that uses port 8181 by default etc. Though in those situations setting the port number here, 167 * allows tooling and JMX to know the port number, so its recommended to set the port number 168 * to the number that the servlet engine uses. 169 */ 170 public void setPort(String port) { 171 this.port = port; 172 } 173 174 public String getContextPath() { 175 return contextPath; 176 } 177 178 /** 179 * Sets a leading context-path the REST services will be using. 180 * <p/> 181 * This can be used when using components such as <tt>camel-servlet</tt> where the deployed web application 182 * is deployed using a context-path. Or for components such as <tt>camel-jetty</tt> or <tt>camel-netty4-http</tt> 183 * that includes a HTTP server. 184 */ 185 public void setContextPath(String contextPath) { 186 this.contextPath = contextPath; 187 } 188 189 public String getApiContextPath() { 190 return apiContextPath; 191 } 192 193 /** 194 * Sets a leading API context-path the REST API services will be using. 195 * <p/> 196 * This can be used when using components such as <tt>camel-servlet</tt> where the deployed web application 197 * is deployed using a context-path. 198 * 199 * @param contextPath the API context path 200 */ 201 public void setApiContextPath(String contextPath) { 202 this.apiContextPath = contextPath; 203 } 204 205 public String getApiContextRouteId() { 206 return apiContextRouteId; 207 } 208 209 /** 210 * Sets the route id to use for the route that services the REST API. 211 * <p/> 212 * The route will by default use an auto assigned route id. 213 * 214 * @param apiContextRouteId the route id 215 */ 216 public void setApiContextRouteId(String apiContextRouteId) { 217 this.apiContextRouteId = apiContextRouteId; 218 } 219 220 public String getApiContextIdPattern() { 221 return apiContextIdPattern; 222 } 223 224 /** 225 * Sets an CamelContext id pattern to only allow Rest APIs from rest services within CamelContext's which name matches the pattern. 226 * <p/> 227 * The pattern <tt>#name#</tt> refers to the CamelContext name, to match on the current CamelContext only. 228 * For any other value, the pattern uses the rules from {@link org.apache.camel.util.EndpointHelper#matchPattern(String, String)} 229 * 230 * @param apiContextIdPattern the pattern 231 */ 232 public void setApiContextIdPattern(String apiContextIdPattern) { 233 this.apiContextIdPattern = apiContextIdPattern; 234 } 235 236 public Boolean getApiContextListing() { 237 return apiContextListing; 238 } 239 240 /** 241 * Sets whether listing of all available CamelContext's with REST services in the JVM is enabled. If enabled it allows to discover 242 * these contexts, if <tt>false</tt> then only the current CamelContext is in use. 243 */ 244 public void setApiContextListing(Boolean apiContextListing) { 245 this.apiContextListing = apiContextListing; 246 } 247 248 public RestHostNameResolver getHostNameResolver() { 249 return hostNameResolver; 250 } 251 252 /** 253 * If no hostname has been explicit configured, then this resolver is used to compute the hostname the REST service will be using. 254 */ 255 public void setHostNameResolver(RestHostNameResolver hostNameResolver) { 256 this.hostNameResolver = hostNameResolver; 257 } 258 259 public RestBindingMode getBindingMode() { 260 return bindingMode; 261 } 262 263 /** 264 * Sets the binding mode to use. 265 * <p/> 266 * The default value is off 267 */ 268 public void setBindingMode(RestBindingMode bindingMode) { 269 this.bindingMode = bindingMode; 270 } 271 272 public Boolean getSkipBindingOnErrorCode() { 273 return skipBindingOnErrorCode; 274 } 275 276 /** 277 * Whether to skip binding on output if there is a custom HTTP error code header. 278 * This allows to build custom error messages that do not bind to json / xml etc, as success messages otherwise will do. 279 */ 280 public void setSkipBindingOnErrorCode(Boolean skipBindingOnErrorCode) { 281 this.skipBindingOnErrorCode = skipBindingOnErrorCode; 282 } 283 284 public Boolean getEnableCORS() { 285 return enableCORS; 286 } 287 288 /** 289 * Whether to enable CORS headers in the HTTP response. 290 * <p/> 291 * The default value is false. 292 */ 293 public void setEnableCORS(Boolean enableCORS) { 294 this.enableCORS = enableCORS; 295 } 296 297 public String getJsonDataFormat() { 298 return jsonDataFormat; 299 } 300 301 /** 302 * Name of specific json data format to use. 303 * By default json-jackson will be used. 304 * Important: This option is only for setting a custom name of the data format, not to refer to an existing data format instance. 305 */ 306 public void setJsonDataFormat(String jsonDataFormat) { 307 this.jsonDataFormat = jsonDataFormat; 308 } 309 310 public String getXmlDataFormat() { 311 return xmlDataFormat; 312 } 313 314 /** 315 * Name of specific XML data format to use. 316 * By default jaxb will be used. 317 * Important: This option is only for setting a custom name of the data format, not to refer to an existing data format instance. 318 */ 319 public void setXmlDataFormat(String xmlDataFormat) { 320 this.xmlDataFormat = xmlDataFormat; 321 } 322 323 public List<RestPropertyDefinition> getComponentProperties() { 324 return componentProperties; 325 } 326 327 /** 328 * Allows to configure as many additional properties for the rest component in use. 329 */ 330 public void setComponentProperties(List<RestPropertyDefinition> componentProperties) { 331 this.componentProperties = componentProperties; 332 } 333 334 public List<RestPropertyDefinition> getEndpointProperties() { 335 return endpointProperties; 336 } 337 338 /** 339 * Allows to configure as many additional properties for the rest endpoint in use. 340 */ 341 public void setEndpointProperties(List<RestPropertyDefinition> endpointProperties) { 342 this.endpointProperties = endpointProperties; 343 } 344 345 public List<RestPropertyDefinition> getConsumerProperties() { 346 return consumerProperties; 347 } 348 349 /** 350 * Allows to configure as many additional properties for the rest consumer in use. 351 */ 352 public void setConsumerProperties(List<RestPropertyDefinition> consumerProperties) { 353 this.consumerProperties = consumerProperties; 354 } 355 356 public List<RestPropertyDefinition> getDataFormatProperties() { 357 return dataFormatProperties; 358 } 359 360 /** 361 * Allows to configure as many additional properties for the data formats in use. 362 * For example set property prettyPrint to true to have json outputted in pretty mode. 363 * The properties can be prefixed to denote the option is only for either JSON or XML and for either the IN or the OUT. 364 * The prefixes are: 365 * <ul> 366 * <li>json.in.</li> 367 * <li>json.out.</li> 368 * <li>xml.in.</li> 369 * <li>xml.out.</li> 370 * </ul> 371 * For example a key with value "xml.out.mustBeJAXBElement" is only for the XML data format for the outgoing. 372 * A key without a prefix is a common key for all situations. 373 */ 374 public void setDataFormatProperties(List<RestPropertyDefinition> dataFormatProperties) { 375 this.dataFormatProperties = dataFormatProperties; 376 } 377 378 public List<RestPropertyDefinition> getApiProperties() { 379 return apiProperties; 380 } 381 382 /** 383 * Allows to configure as many additional properties for the api documentation (swagger). 384 * For example set property api.title to my cool stuff 385 */ 386 public void setApiProperties(List<RestPropertyDefinition> apiProperties) { 387 this.apiProperties = apiProperties; 388 } 389 390 public List<RestPropertyDefinition> getCorsHeaders() { 391 return corsHeaders; 392 } 393 394 /** 395 * Allows to configure custom CORS headers. 396 */ 397 public void setCorsHeaders(List<RestPropertyDefinition> corsHeaders) { 398 this.corsHeaders = corsHeaders; 399 } 400 401 // Fluent API 402 //------------------------------------------------------------------------- 403 404 /** 405 * To use a specific Camel rest component 406 */ 407 public RestConfigurationDefinition component(String componentId) { 408 setComponent(componentId); 409 return this; 410 } 411 412 /** 413 * To use a specific Camel rest API component 414 */ 415 public RestConfigurationDefinition apiComponent(String componentId) { 416 setApiComponent(componentId); 417 return this; 418 } 419 420 /** 421 * To use a specific scheme such as http/https 422 */ 423 public RestConfigurationDefinition scheme(String scheme) { 424 setScheme(scheme); 425 return this; 426 } 427 428 /** 429 * To define the host to use, such as 0.0.0.0 or localhost 430 */ 431 public RestConfigurationDefinition host(String host) { 432 setHost(host); 433 return this; 434 } 435 436 /** 437 * To specify the port number to use for the REST service 438 */ 439 public RestConfigurationDefinition port(int port) { 440 setPort("" + port); 441 return this; 442 } 443 444 /** 445 * To specify the port number to use for the REST service 446 */ 447 public RestConfigurationDefinition port(String port) { 448 setPort(port); 449 return this; 450 } 451 452 /** 453 * Sets a leading context-path the REST services will be using. 454 * <p/> 455 * This can be used when using components such as <tt>camel-servlet</tt> where the deployed web application 456 * is deployed using a context-path. Or for components such as <tt>camel-jetty</tt> or <tt>camel-netty4-http</tt> 457 * that includes a HTTP server. 458 */ 459 public RestConfigurationDefinition apiContextPath(String contextPath) { 460 setApiContextPath(contextPath); 461 return this; 462 } 463 464 /** 465 * Sets the route id to use for the route that services the REST API. 466 */ 467 public RestConfigurationDefinition apiContextRouteId(String routeId) { 468 setApiContextRouteId(routeId); 469 return this; 470 } 471 472 /** 473 * Sets an CamelContext id pattern to only allow Rest APIs from rest services within CamelContext's which name matches the pattern. 474 * <p/> 475 * The pattern uses the rules from {@link org.apache.camel.util.EndpointHelper#matchPattern(String, String)} 476 */ 477 public RestConfigurationDefinition apiContextIdPattern(String pattern) { 478 setApiContextIdPattern(pattern); 479 return this; 480 } 481 482 /** 483 * Sets whether listing of all available CamelContext's with REST services in the JVM is enabled. If enabled it allows to discover 484 * these contexts, if <tt>false</tt> then only the current CamelContext is in use. 485 */ 486 public RestConfigurationDefinition apiContextListing(boolean listing) { 487 setApiContextListing(listing); 488 return this; 489 } 490 491 /** 492 * Sets a leading context-path the REST services will be using. 493 * <p/> 494 * This can be used when using components such as <tt>camel-servlet</tt> where the deployed web application 495 * is deployed using a context-path. 496 */ 497 public RestConfigurationDefinition contextPath(String contextPath) { 498 setContextPath(contextPath); 499 return this; 500 } 501 502 /** 503 * To specify the hostname resolver 504 */ 505 public RestConfigurationDefinition hostNameResolver(RestHostNameResolver hostNameResolver) { 506 setHostNameResolver(hostNameResolver); 507 return this; 508 } 509 510 /** 511 * To specify the binding mode 512 */ 513 public RestConfigurationDefinition bindingMode(RestBindingMode bindingMode) { 514 setBindingMode(bindingMode); 515 return this; 516 } 517 518 /** 519 * To specify whether to skip binding output if there is a custom HTTP error code 520 */ 521 public RestConfigurationDefinition skipBindingOnErrorCode(boolean skipBindingOnErrorCode) { 522 setSkipBindingOnErrorCode(skipBindingOnErrorCode); 523 return this; 524 } 525 526 /** 527 * To specify whether to enable CORS which means Camel will automatic include CORS in the HTTP headers in the response. 528 */ 529 public RestConfigurationDefinition enableCORS(boolean enableCORS) { 530 setEnableCORS(enableCORS); 531 return this; 532 } 533 534 /** 535 * To use a specific json data format 536 * <p/> 537 * <b>Important:</b> This option is only for setting a custom name of the data format, not to refer to an existing data format instance. 538 * 539 * @param name name of the data format to {@link org.apache.camel.CamelContext#resolveDataFormat(java.lang.String) resolve} 540 */ 541 public RestConfigurationDefinition jsonDataFormat(String name) { 542 setJsonDataFormat(name); 543 return this; 544 } 545 546 /** 547 * To use a specific XML data format 548 * <p/> 549 * <b>Important:</b> This option is only for setting a custom name of the data format, not to refer to an existing data format instance. 550 * 551 * @param name name of the data format to {@link org.apache.camel.CamelContext#resolveDataFormat(java.lang.String) resolve} 552 */ 553 public RestConfigurationDefinition xmlDataFormat(String name) { 554 setXmlDataFormat(name); 555 return this; 556 } 557 558 /** 559 * For additional configuration options on component level 560 * <p/> 561 * The value can use <tt>#</tt> to refer to a bean to lookup in the registry. 562 */ 563 public RestConfigurationDefinition componentProperty(String key, String value) { 564 RestPropertyDefinition prop = new RestPropertyDefinition(); 565 prop.setKey(key); 566 prop.setValue(value); 567 getComponentProperties().add(prop); 568 return this; 569 } 570 571 /** 572 * For additional configuration options on endpoint level 573 * <p/> 574 * The value can use <tt>#</tt> to refer to a bean to lookup in the registry. 575 */ 576 public RestConfigurationDefinition endpointProperty(String key, String value) { 577 RestPropertyDefinition prop = new RestPropertyDefinition(); 578 prop.setKey(key); 579 prop.setValue(value); 580 getEndpointProperties().add(prop); 581 return this; 582 } 583 584 /** 585 * For additional configuration options on consumer level 586 * <p/> 587 * The value can use <tt>#</tt> to refer to a bean to lookup in the registry. 588 */ 589 public RestConfigurationDefinition consumerProperty(String key, String value) { 590 RestPropertyDefinition prop = new RestPropertyDefinition(); 591 prop.setKey(key); 592 prop.setValue(value); 593 getConsumerProperties().add(prop); 594 return this; 595 } 596 597 /** 598 * For additional configuration options on data format level 599 * <p/> 600 * The value can use <tt>#</tt> to refer to a bean to lookup in the registry. 601 */ 602 public RestConfigurationDefinition dataFormatProperty(String key, String value) { 603 RestPropertyDefinition prop = new RestPropertyDefinition(); 604 prop.setKey(key); 605 prop.setValue(value); 606 getDataFormatProperties().add(prop); 607 return this; 608 } 609 610 /** 611 * For configuring an api property, such as <tt>api.title</tt>, or <tt>api.version</tt>. 612 */ 613 public RestConfigurationDefinition apiProperty(String key, String value) { 614 RestPropertyDefinition prop = new RestPropertyDefinition(); 615 prop.setKey(key); 616 prop.setValue(value); 617 getApiProperties().add(prop); 618 return this; 619 } 620 621 /** 622 * For configuring CORS headers 623 */ 624 public RestConfigurationDefinition corsHeaderProperty(String key, String value) { 625 RestPropertyDefinition prop = new RestPropertyDefinition(); 626 prop.setKey(key); 627 prop.setValue(value); 628 getCorsHeaders().add(prop); 629 return this; 630 } 631 632 // Implementation 633 //------------------------------------------------------------------------- 634 635 /** 636 * Creates a {@link org.apache.camel.spi.RestConfiguration} instance based on the definition 637 * 638 * @param context the camel context 639 * @return the configuration 640 * @throws Exception is thrown if error creating the configuration 641 */ 642 public RestConfiguration asRestConfiguration(CamelContext context) throws Exception { 643 RestConfiguration answer = new RestConfiguration(); 644 if (component != null) { 645 answer.setComponent(CamelContextHelper.parseText(context, component)); 646 } 647 if (apiComponent != null) { 648 answer.setApiComponent(CamelContextHelper.parseText(context, apiComponent)); 649 } 650 if (scheme != null) { 651 answer.setScheme(CamelContextHelper.parseText(context, scheme)); 652 } 653 if (host != null) { 654 answer.setHost(CamelContextHelper.parseText(context, host)); 655 } 656 if (port != null) { 657 answer.setPort(CamelContextHelper.parseInteger(context, port)); 658 } 659 if (apiContextPath != null) { 660 answer.setApiContextPath(CamelContextHelper.parseText(context, apiContextPath)); 661 } 662 if (apiContextRouteId != null) { 663 answer.setApiContextRouteId(CamelContextHelper.parseText(context, apiContextRouteId)); 664 } 665 if (apiContextIdPattern != null) { 666 // special to allow #name# to refer to itself 667 if ("#name#".equals(apiComponent)) { 668 answer.setApiContextIdPattern(context.getName()); 669 } else { 670 answer.setApiContextIdPattern(CamelContextHelper.parseText(context, apiContextIdPattern)); 671 } 672 } 673 if (apiContextListing != null) { 674 answer.setApiContextListing(apiContextListing); 675 } 676 if (contextPath != null) { 677 answer.setContextPath(CamelContextHelper.parseText(context, contextPath)); 678 } 679 if (hostNameResolver != null) { 680 answer.setRestHostNameResolver(hostNameResolver.name()); 681 } 682 if (bindingMode != null) { 683 answer.setBindingMode(bindingMode.name()); 684 } 685 if (skipBindingOnErrorCode != null) { 686 answer.setSkipBindingOnErrorCode(skipBindingOnErrorCode); 687 } 688 if (enableCORS != null) { 689 answer.setEnableCORS(enableCORS); 690 } 691 if (jsonDataFormat != null) { 692 answer.setJsonDataFormat(jsonDataFormat); 693 } 694 if (xmlDataFormat != null) { 695 answer.setXmlDataFormat(xmlDataFormat); 696 } 697 if (!componentProperties.isEmpty()) { 698 Map<String, Object> props = new HashMap<String, Object>(); 699 for (RestPropertyDefinition prop : componentProperties) { 700 String key = prop.getKey(); 701 String value = CamelContextHelper.parseText(context, prop.getValue()); 702 props.put(key, value); 703 } 704 answer.setComponentProperties(props); 705 } 706 if (!endpointProperties.isEmpty()) { 707 Map<String, Object> props = new HashMap<String, Object>(); 708 for (RestPropertyDefinition prop : endpointProperties) { 709 String key = prop.getKey(); 710 String value = CamelContextHelper.parseText(context, prop.getValue()); 711 props.put(key, value); 712 } 713 answer.setEndpointProperties(props); 714 } 715 if (!consumerProperties.isEmpty()) { 716 Map<String, Object> props = new HashMap<String, Object>(); 717 for (RestPropertyDefinition prop : consumerProperties) { 718 String key = prop.getKey(); 719 String value = CamelContextHelper.parseText(context, prop.getValue()); 720 props.put(key, value); 721 } 722 answer.setConsumerProperties(props); 723 } 724 if (!dataFormatProperties.isEmpty()) { 725 Map<String, Object> props = new HashMap<String, Object>(); 726 for (RestPropertyDefinition prop : dataFormatProperties) { 727 String key = prop.getKey(); 728 String value = CamelContextHelper.parseText(context, prop.getValue()); 729 props.put(key, value); 730 } 731 answer.setDataFormatProperties(props); 732 } 733 if (!apiProperties.isEmpty()) { 734 Map<String, Object> props = new HashMap<String, Object>(); 735 for (RestPropertyDefinition prop : apiProperties) { 736 String key = prop.getKey(); 737 String value = CamelContextHelper.parseText(context, prop.getValue()); 738 props.put(key, value); 739 } 740 answer.setApiProperties(props); 741 } 742 if (!corsHeaders.isEmpty()) { 743 Map<String, String> props = new HashMap<String, String>(); 744 for (RestPropertyDefinition prop : corsHeaders) { 745 String key = prop.getKey(); 746 String value = CamelContextHelper.parseText(context, prop.getValue()); 747 props.put(key, value); 748 } 749 answer.setCorsHeaders(props); 750 } 751 return answer; 752 } 753 754}