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 = "auto") 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 auto 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 */ 561 public RestConfigurationDefinition componentProperty(String key, String value) { 562 RestPropertyDefinition prop = new RestPropertyDefinition(); 563 prop.setKey(key); 564 prop.setValue(value); 565 getComponentProperties().add(prop); 566 return this; 567 } 568 569 /** 570 * For additional configuration options on endpoint level 571 */ 572 public RestConfigurationDefinition endpointProperty(String key, String value) { 573 RestPropertyDefinition prop = new RestPropertyDefinition(); 574 prop.setKey(key); 575 prop.setValue(value); 576 getEndpointProperties().add(prop); 577 return this; 578 } 579 580 /** 581 * For additional configuration options on consumer level 582 */ 583 public RestConfigurationDefinition consumerProperty(String key, String value) { 584 RestPropertyDefinition prop = new RestPropertyDefinition(); 585 prop.setKey(key); 586 prop.setValue(value); 587 getConsumerProperties().add(prop); 588 return this; 589 } 590 591 /** 592 * For additional configuration options on data format level 593 */ 594 public RestConfigurationDefinition dataFormatProperty(String key, String value) { 595 RestPropertyDefinition prop = new RestPropertyDefinition(); 596 prop.setKey(key); 597 prop.setValue(value); 598 getDataFormatProperties().add(prop); 599 return this; 600 } 601 602 /** 603 * For configuring an api property, such as <tt>api.title</tt>, or <tt>api.version</tt>. 604 */ 605 public RestConfigurationDefinition apiProperty(String key, String value) { 606 RestPropertyDefinition prop = new RestPropertyDefinition(); 607 prop.setKey(key); 608 prop.setValue(value); 609 getApiProperties().add(prop); 610 return this; 611 } 612 613 /** 614 * For configuring CORS headers 615 */ 616 public RestConfigurationDefinition corsHeaderProperty(String key, String value) { 617 RestPropertyDefinition prop = new RestPropertyDefinition(); 618 prop.setKey(key); 619 prop.setValue(value); 620 getCorsHeaders().add(prop); 621 return this; 622 } 623 624 // Implementation 625 //------------------------------------------------------------------------- 626 627 /** 628 * Creates a {@link org.apache.camel.spi.RestConfiguration} instance based on the definition 629 * 630 * @param context the camel context 631 * @return the configuration 632 * @throws Exception is thrown if error creating the configuration 633 */ 634 public RestConfiguration asRestConfiguration(CamelContext context) throws Exception { 635 RestConfiguration answer = new RestConfiguration(); 636 if (component != null) { 637 answer.setComponent(CamelContextHelper.parseText(context, component)); 638 } 639 if (apiComponent != null) { 640 answer.setApiComponent(CamelContextHelper.parseText(context, apiComponent)); 641 } 642 if (scheme != null) { 643 answer.setScheme(CamelContextHelper.parseText(context, scheme)); 644 } 645 if (host != null) { 646 answer.setHost(CamelContextHelper.parseText(context, host)); 647 } 648 if (port != null) { 649 answer.setPort(CamelContextHelper.parseInteger(context, port)); 650 } 651 if (apiContextPath != null) { 652 answer.setApiContextPath(CamelContextHelper.parseText(context, apiContextPath)); 653 } 654 if (apiContextRouteId != null) { 655 answer.setApiContextRouteId(CamelContextHelper.parseText(context, apiContextRouteId)); 656 } 657 if (apiContextIdPattern != null) { 658 // special to allow #name# to refer to itself 659 if ("#name#".equals(apiComponent)) { 660 answer.setApiContextIdPattern(context.getName()); 661 } else { 662 answer.setApiContextIdPattern(CamelContextHelper.parseText(context, apiContextIdPattern)); 663 } 664 } 665 if (apiContextListing != null) { 666 answer.setApiContextListing(apiContextListing); 667 } 668 if (contextPath != null) { 669 answer.setContextPath(CamelContextHelper.parseText(context, contextPath)); 670 } 671 if (hostNameResolver != null) { 672 answer.setRestHostNameResolver(hostNameResolver.name()); 673 } 674 if (bindingMode != null) { 675 answer.setBindingMode(bindingMode.name()); 676 } 677 if (skipBindingOnErrorCode != null) { 678 answer.setSkipBindingOnErrorCode(skipBindingOnErrorCode); 679 } 680 if (enableCORS != null) { 681 answer.setEnableCORS(enableCORS); 682 } 683 if (jsonDataFormat != null) { 684 answer.setJsonDataFormat(jsonDataFormat); 685 } 686 if (xmlDataFormat != null) { 687 answer.setXmlDataFormat(xmlDataFormat); 688 } 689 if (!componentProperties.isEmpty()) { 690 Map<String, Object> props = new HashMap<String, Object>(); 691 for (RestPropertyDefinition prop : componentProperties) { 692 String key = prop.getKey(); 693 String value = CamelContextHelper.parseText(context, prop.getValue()); 694 props.put(key, value); 695 } 696 answer.setComponentProperties(props); 697 } 698 if (!endpointProperties.isEmpty()) { 699 Map<String, Object> props = new HashMap<String, Object>(); 700 for (RestPropertyDefinition prop : endpointProperties) { 701 String key = prop.getKey(); 702 String value = CamelContextHelper.parseText(context, prop.getValue()); 703 props.put(key, value); 704 } 705 answer.setEndpointProperties(props); 706 } 707 if (!consumerProperties.isEmpty()) { 708 Map<String, Object> props = new HashMap<String, Object>(); 709 for (RestPropertyDefinition prop : consumerProperties) { 710 String key = prop.getKey(); 711 String value = CamelContextHelper.parseText(context, prop.getValue()); 712 props.put(key, value); 713 } 714 answer.setConsumerProperties(props); 715 } 716 if (!dataFormatProperties.isEmpty()) { 717 Map<String, Object> props = new HashMap<String, Object>(); 718 for (RestPropertyDefinition prop : dataFormatProperties) { 719 String key = prop.getKey(); 720 String value = CamelContextHelper.parseText(context, prop.getValue()); 721 props.put(key, value); 722 } 723 answer.setDataFormatProperties(props); 724 } 725 if (!apiProperties.isEmpty()) { 726 Map<String, Object> props = new HashMap<String, Object>(); 727 for (RestPropertyDefinition prop : apiProperties) { 728 String key = prop.getKey(); 729 String value = CamelContextHelper.parseText(context, prop.getValue()); 730 props.put(key, value); 731 } 732 answer.setApiProperties(props); 733 } 734 if (!corsHeaders.isEmpty()) { 735 Map<String, String> props = new HashMap<String, String>(); 736 for (RestPropertyDefinition prop : corsHeaders) { 737 String key = prop.getKey(); 738 String value = CamelContextHelper.parseText(context, prop.getValue()); 739 props.put(key, value); 740 } 741 answer.setCorsHeaders(props); 742 } 743 return answer; 744 } 745 746}