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
046    private String scheme;
047
048    @XmlAttribute
049    private String host;
050
051    @XmlAttribute
052    private String port;
053
054    @XmlAttribute
055    private String contextPath;
056
057    @XmlAttribute
058    private RestHostNameResolver hostNameResolver;
059
060    @XmlAttribute @Metadata(defaultValue = "auto")
061    private RestBindingMode bindingMode;
062
063    @XmlAttribute
064    private Boolean skipBindingOnErrorCode;
065
066    @XmlAttribute
067    private Boolean enableCORS;
068
069    @XmlAttribute
070    private String jsonDataFormat;
071
072    @XmlAttribute
073    private String xmlDataFormat;
074
075    @XmlElement(name = "componentProperty")
076    private List<RestPropertyDefinition> componentProperties = new ArrayList<RestPropertyDefinition>();
077
078    @XmlElement(name = "endpointProperty")
079    private List<RestPropertyDefinition> endpointProperties = new ArrayList<RestPropertyDefinition>();
080
081    @XmlElement(name = "consumerProperty")
082    private List<RestPropertyDefinition> consumerProperties = new ArrayList<RestPropertyDefinition>();
083
084    @XmlElement(name = "dataFormatProperty")
085    private List<RestPropertyDefinition> dataFormatProperties = new ArrayList<RestPropertyDefinition>();
086
087    @XmlElement(name = "corsHeaders")
088    private List<RestPropertyDefinition> corsHeaders = new ArrayList<RestPropertyDefinition>();
089
090    public String getComponent() {
091        return component;
092    }
093
094    /**
095     * The Camel Rest component to use for the REST transport, such as restlet, spark-rest.
096     * If no component has been explicit configured, then Camel will lookup if there is a Camel component
097     * that integrates with the Rest DSL, or if a org.apache.camel.spi.RestConsumerFactory is registered in the registry.
098     * If either one is found, then that is being used.
099     */
100    public void setComponent(String component) {
101        this.component = component;
102    }
103
104    public String getScheme() {
105        return scheme;
106    }
107
108    /**
109     * The scheme to use for exposing the REST service. Usually http or https is supported.
110     * <p/>
111     * The default value is http
112     */
113    public void setScheme(String scheme) {
114        this.scheme = scheme;
115    }
116
117    public String getHost() {
118        return host;
119    }
120
121    /**
122     * The hostname to use for exposing the REST service.
123     */
124    public void setHost(String host) {
125        this.host = host;
126    }
127
128    public String getPort() {
129        return port;
130    }
131
132    /**
133     * The port number to use for exposing the REST service.
134     * Notice if you use servlet component then the port number configured here does not apply,
135     * as the port number in use is the actual port number the servlet component is using.
136     * eg if using Apache Tomcat its the tomcat http port, if using Apache Karaf its the HTTP service in Karaf
137     * that uses port 8181 by default etc. Though in those situations setting the port number here,
138     * allows tooling and JMX to know the port number, so its recommended to set the port number
139     * to the number that the servlet engine uses.
140     */
141    public void setPort(String port) {
142        this.port = port;
143    }
144
145    public String getContextPath() {
146        return contextPath;
147    }
148
149    /**
150     * Sets a leading context-path the REST services will be using.
151     * This can be used when using components such as SERVLET where the deployed web application is deployed using a context-path.
152     */
153    public void setContextPath(String contextPath) {
154        this.contextPath = contextPath;
155    }
156
157    public RestHostNameResolver getHostNameResolver() {
158        return hostNameResolver;
159    }
160
161    /**
162     * If no hostname has been explicit configured, then this resolver is used to compute the hostname the REST service will be using.
163     */
164    public void setHostNameResolver(RestHostNameResolver hostNameResolver) {
165        this.hostNameResolver = hostNameResolver;
166    }
167
168    public RestBindingMode getBindingMode() {
169        return bindingMode;
170    }
171
172    /**
173     * Sets the binding mode to use.
174     * <p/>
175     * The default value is auto
176     */
177    public void setBindingMode(RestBindingMode bindingMode) {
178        this.bindingMode = bindingMode;
179    }
180
181    public Boolean getSkipBindingOnErrorCode() {
182        return skipBindingOnErrorCode;
183    }
184
185    /**
186     * Whether to skip binding on output if there is a custom HTTP error code header.
187     * This allows to build custom error messages that do not bind to json / xml etc, as success messages otherwise will do.
188     */
189    public void setSkipBindingOnErrorCode(Boolean skipBindingOnErrorCode) {
190        this.skipBindingOnErrorCode = skipBindingOnErrorCode;
191    }
192
193    public Boolean getEnableCORS() {
194        return enableCORS;
195    }
196
197    /**
198     * Whether to enable CORS headers in the HTTP response.
199     * <p/>
200     * The default value is false.
201     */
202    public void setEnableCORS(Boolean enableCORS) {
203        this.enableCORS = enableCORS;
204    }
205
206    public String getJsonDataFormat() {
207        return jsonDataFormat;
208    }
209
210    /**
211     * Name of specific json data format to use.
212     * By default json-jackson will be used.
213     * Important: This option is only for setting a custom name of the data format, not to refer to an existing data format instance.
214     */
215    public void setJsonDataFormat(String jsonDataFormat) {
216        this.jsonDataFormat = jsonDataFormat;
217    }
218
219    public String getXmlDataFormat() {
220        return xmlDataFormat;
221    }
222
223    /**
224     * Name of specific XML data format to use.
225     * By default jaxb will be used.
226     * Important: This option is only for setting a custom name of the data format, not to refer to an existing data format instance.
227     */
228    public void setXmlDataFormat(String xmlDataFormat) {
229        this.xmlDataFormat = xmlDataFormat;
230    }
231
232    public List<RestPropertyDefinition> getComponentProperties() {
233        return componentProperties;
234    }
235
236    /**
237     * Allows to configure as many additional properties for the rest component in use.
238     */
239    public void setComponentProperties(List<RestPropertyDefinition> componentProperties) {
240        this.componentProperties = componentProperties;
241    }
242
243    public List<RestPropertyDefinition> getEndpointProperties() {
244        return endpointProperties;
245    }
246
247    /**
248     * Allows to configure as many additional properties for the rest endpoint in use.
249     */
250    public void setEndpointProperties(List<RestPropertyDefinition> endpointProperties) {
251        this.endpointProperties = endpointProperties;
252    }
253
254    public List<RestPropertyDefinition> getConsumerProperties() {
255        return consumerProperties;
256    }
257
258    /**
259     * Allows to configure as many additional properties for the rest consumer in use.
260     */
261    public void setConsumerProperties(List<RestPropertyDefinition> consumerProperties) {
262        this.consumerProperties = consumerProperties;
263    }
264
265    public List<RestPropertyDefinition> getDataFormatProperties() {
266        return dataFormatProperties;
267    }
268
269    /**
270     * Allows to configure as many additional properties for the data formats in use.
271     * For example set property prettyPrint to true to have json outputted in pretty mode.
272     * The properties can be prefixed to denote the option is only for either JSON or XML and for either the IN or the OUT.
273     * The prefixes are:
274     * <ul>
275     *     <li>json.in.</li>
276     *     <li>json.out.</li>
277     *     <li>xml.in.</li>
278     *     <li>xml.out.</li>
279     * </ul>
280     * For example a key with value "xml.out.mustBeJAXBElement" is only for the XML data format for the outgoing.
281     * A key without a prefix is a common key for all situations.
282     */
283    public void setDataFormatProperties(List<RestPropertyDefinition> dataFormatProperties) {
284        this.dataFormatProperties = dataFormatProperties;
285    }
286
287    public List<RestPropertyDefinition> getCorsHeaders() {
288        return corsHeaders;
289    }
290
291    /**
292     * Allows to configure custom CORS headers.
293     */
294    public void setCorsHeaders(List<RestPropertyDefinition> corsHeaders) {
295        this.corsHeaders = corsHeaders;
296    }
297
298    // Fluent API
299    //-------------------------------------------------------------------------
300
301    /**
302     * To use a specific Camel rest component
303     */
304    public RestConfigurationDefinition component(String componentId) {
305        setComponent(componentId);
306        return this;
307    }
308
309    /**
310     * To use a specific scheme such as http/https
311     */
312    public RestConfigurationDefinition scheme(String scheme) {
313        setScheme(scheme);
314        return this;
315    }
316
317    /**
318     * To define the host to use, such as 0.0.0.0 or localhost
319     */
320    public RestConfigurationDefinition host(String host) {
321        setHost(host);
322        return this;
323    }
324
325    /**
326     * To specify the port number to use for the REST service
327     */
328    public RestConfigurationDefinition port(int port) {
329        setPort("" + port);
330        return this;
331    }
332
333    /**
334     * To specify the port number to use for the REST service
335     */
336    public RestConfigurationDefinition port(String port) {
337        setPort(port);
338        return this;
339    }
340
341    /**
342     * Sets a leading context-path the REST services will be using.
343     * <p/>
344     * This can be used when using components such as <tt>camel-servlet</tt> where the deployed web application
345     * is deployed using a context-path.
346     */
347    public RestConfigurationDefinition contextPath(String contextPath) {
348        setContextPath(contextPath);
349        return this;
350    }
351
352    /**
353     * To specify the hostname resolver
354     */
355    public RestConfigurationDefinition hostNameResolver(RestHostNameResolver hostNameResolver) {
356        setHostNameResolver(hostNameResolver);
357        return this;
358    }
359
360    /**
361     * To specify the binding mode
362     */
363    public RestConfigurationDefinition bindingMode(RestBindingMode bindingMode) {
364        setBindingMode(bindingMode);
365        return this;
366    }
367
368    /**
369     * To specify whether to skip binding output if there is a custom HTTP error code
370     */
371    public RestConfigurationDefinition skipBindingOnErrorCode(boolean skipBindingOnErrorCode) {
372        setSkipBindingOnErrorCode(skipBindingOnErrorCode);
373        return this;
374    }
375
376    /**
377     * To specify whether to enable CORS which means Camel will automatic include CORS in the HTTP headers in the response.
378     */
379    public RestConfigurationDefinition enableCORS(boolean enableCORS) {
380        setEnableCORS(enableCORS);
381        return this;
382    }
383
384    /**
385     * To use a specific json data format
386     * <p/>
387     * <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.
388     *
389     * @param name  name of the data format to {@link org.apache.camel.CamelContext#resolveDataFormat(java.lang.String) resolve}
390     */
391    public RestConfigurationDefinition jsonDataFormat(String name) {
392        setJsonDataFormat(name);
393        return this;
394    }
395
396    /**
397     * To use a specific XML data format
398     * <p/>
399     * <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.
400     *
401     * @param name  name of the data format to {@link org.apache.camel.CamelContext#resolveDataFormat(java.lang.String) resolve}
402     */
403    public RestConfigurationDefinition xmlDataFormat(String name) {
404        setXmlDataFormat(name);
405        return this;
406    }
407
408    /**
409     * For additional configuration options on component level
410     */
411    public RestConfigurationDefinition componentProperty(String key, String value) {
412        RestPropertyDefinition prop = new RestPropertyDefinition();
413        prop.setKey(key);
414        prop.setValue(value);
415        getComponentProperties().add(prop);
416        return this;
417    }
418
419    /**
420     * For additional configuration options on endpoint level
421     */
422    public RestConfigurationDefinition endpointProperty(String key, String value) {
423        RestPropertyDefinition prop = new RestPropertyDefinition();
424        prop.setKey(key);
425        prop.setValue(value);
426        getEndpointProperties().add(prop);
427        return this;
428    }
429
430    /**
431     * For additional configuration options on consumer level
432     */
433    public RestConfigurationDefinition consumerProperty(String key, String value) {
434        RestPropertyDefinition prop = new RestPropertyDefinition();
435        prop.setKey(key);
436        prop.setValue(value);
437        getConsumerProperties().add(prop);
438        return this;
439    }
440
441    /**
442     * For additional configuration options on data format level
443     */
444    public RestConfigurationDefinition dataFormatProperty(String key, String value) {
445        RestPropertyDefinition prop = new RestPropertyDefinition();
446        prop.setKey(key);
447        prop.setValue(value);
448        getDataFormatProperties().add(prop);
449        return this;
450    }
451
452    /**
453     * For configuring CORS headers
454     */
455    public RestConfigurationDefinition corsHeaderProperty(String key, String value) {
456        RestPropertyDefinition prop = new RestPropertyDefinition();
457        prop.setKey(key);
458        prop.setValue(value);
459        getCorsHeaders().add(prop);
460        return this;
461    }
462
463    // Implementation
464    //-------------------------------------------------------------------------
465
466    /**
467     * Creates a {@link org.apache.camel.spi.RestConfiguration} instance based on the definition
468     *
469     * @param context     the camel context
470     * @return the configuration
471     * @throws Exception is thrown if error creating the configuration
472     */
473    public RestConfiguration asRestConfiguration(CamelContext context) throws Exception {
474        RestConfiguration answer = new RestConfiguration();
475        if (component != null) {
476            answer.setComponent(CamelContextHelper.parseText(context, component));
477        }
478        if (scheme != null) {
479            answer.setScheme(CamelContextHelper.parseText(context, scheme));
480        }
481        if (host != null) {
482            answer.setHost(CamelContextHelper.parseText(context, host));
483        }
484        if (port != null) {
485            answer.setPort(CamelContextHelper.parseInteger(context, port));
486        }
487        if (contextPath != null) {
488            answer.setContextPath(CamelContextHelper.parseText(context, contextPath));
489        }
490        if (hostNameResolver != null) {
491            answer.setRestHostNameResolver(hostNameResolver.name());
492        }
493        if (bindingMode != null) {
494            answer.setBindingMode(bindingMode.name());
495        }
496        if (skipBindingOnErrorCode != null) {
497            answer.setSkipBindingOnErrorCode(skipBindingOnErrorCode);
498        }
499        if (enableCORS != null) {
500            answer.setEnableCORS(enableCORS);
501        }
502        if (jsonDataFormat != null) {
503            answer.setJsonDataFormat(jsonDataFormat);
504        }
505        if (xmlDataFormat != null) {
506            answer.setXmlDataFormat(xmlDataFormat);
507        }
508        if (!componentProperties.isEmpty()) {
509            Map<String, Object> props = new HashMap<String, Object>();
510            for (RestPropertyDefinition prop : componentProperties) {
511                String key = prop.getKey();
512                String value = CamelContextHelper.parseText(context, prop.getValue());
513                props.put(key, value);
514            }
515            answer.setComponentProperties(props);
516        }
517        if (!endpointProperties.isEmpty()) {
518            Map<String, Object> props = new HashMap<String, Object>();
519            for (RestPropertyDefinition prop : endpointProperties) {
520                String key = prop.getKey();
521                String value = CamelContextHelper.parseText(context, prop.getValue());
522                props.put(key, value);
523            }
524            answer.setEndpointProperties(props);
525        }
526        if (!consumerProperties.isEmpty()) {
527            Map<String, Object> props = new HashMap<String, Object>();
528            for (RestPropertyDefinition prop : consumerProperties) {
529                String key = prop.getKey();
530                String value = CamelContextHelper.parseText(context, prop.getValue());
531                props.put(key, value);
532            }
533            answer.setConsumerProperties(props);
534        }
535        if (!dataFormatProperties.isEmpty()) {
536            Map<String, Object> props = new HashMap<String, Object>();
537            for (RestPropertyDefinition prop : dataFormatProperties) {
538                String key = prop.getKey();
539                String value = CamelContextHelper.parseText(context, prop.getValue());
540                props.put(key, value);
541            }
542            answer.setDataFormatProperties(props);
543        }
544        if (!corsHeaders.isEmpty()) {
545            Map<String, String> props = new HashMap<String, String>();
546            for (RestPropertyDefinition prop : corsHeaders) {
547                String key = prop.getKey();
548                String value = CamelContextHelper.parseText(context, prop.getValue());
549                props.put(key, value);
550            }
551            answer.setCorsHeaders(props);
552        }
553        return answer;
554    }
555
556}