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.component.rest;
018
019import java.util.Map;
020import java.util.Set;
021
022import org.apache.camel.Component;
023import org.apache.camel.Consumer;
024import org.apache.camel.NoSuchBeanException;
025import org.apache.camel.Processor;
026import org.apache.camel.Producer;
027import org.apache.camel.impl.DefaultEndpoint;
028import org.apache.camel.spi.Metadata;
029import org.apache.camel.spi.RestConfiguration;
030import org.apache.camel.spi.RestConsumerFactory;
031import org.apache.camel.spi.UriEndpoint;
032import org.apache.camel.spi.UriParam;
033import org.apache.camel.spi.UriPath;
034import org.apache.camel.util.HostUtils;
035import org.apache.camel.util.ObjectHelper;
036
037@UriEndpoint(scheme = "rest", syntax = "rest:method:path:uriTemplate", consumerOnly = true, label = "core,http,rest")
038public class RestEndpoint extends DefaultEndpoint {
039
040    @UriPath(enums = "get,post,put,delete,patch,head,trace,connect,options") @Metadata(required = "true")
041    private String method;
042    @UriPath @Metadata(required = "true")
043    private String path;
044    @UriPath
045    private String uriTemplate;
046    @UriParam
047    private String consumes;
048    @UriParam
049    private String produces;
050    @UriParam
051    private String componentName;
052    @UriParam
053    private String inType;
054    @UriParam
055    private String outType;
056    @UriParam
057    private String routeId;
058    @UriParam
059    private String description;
060
061    private Map<String, Object> parameters;
062
063    public RestEndpoint(String endpointUri, RestComponent component) {
064        super(endpointUri, component);
065    }
066
067    @Override
068    public RestComponent getComponent() {
069        return (RestComponent) super.getComponent();
070    }
071
072    public String getMethod() {
073        return method;
074    }
075
076    /**
077     * HTTP method to use.
078     */
079    public void setMethod(String method) {
080        this.method = method;
081    }
082
083    public String getPath() {
084        return path;
085    }
086
087    /**
088     * The base path
089     */
090    public void setPath(String path) {
091        this.path = path;
092    }
093
094    public String getUriTemplate() {
095        return uriTemplate;
096    }
097
098    /**
099     * The uri template
100     */
101    public void setUriTemplate(String uriTemplate) {
102        this.uriTemplate = uriTemplate;
103    }
104
105    public String getConsumes() {
106        return consumes;
107    }
108
109    /**
110     * Media type such as: 'text/xml', or 'application/json' this REST service accepts.
111     * By default we accept all kinds of types.
112     */
113    public void setConsumes(String consumes) {
114        this.consumes = consumes;
115    }
116
117    public String getProduces() {
118        return produces;
119    }
120
121    /**
122     * Media type such as: 'text/xml', or 'application/json' this REST service returns.
123     */
124    public void setProduces(String produces) {
125        this.produces = produces;
126    }
127
128    public String getComponentName() {
129        return componentName;
130    }
131
132    /**
133     * The Camel Rest component to use for the REST transport, such as restlet, spark-rest.
134     * If no component has been explicit configured, then Camel will lookup if there is a Camel component
135     * that integrates with the Rest DSL, or if a org.apache.camel.spi.RestConsumerFactory is registered in the registry.
136     * If either one is found, then that is being used.
137     */
138    public void setComponentName(String componentName) {
139        this.componentName = componentName;
140    }
141
142    public String getInType() {
143        return inType;
144    }
145
146    /**
147     * To declare the incoming POJO binding type as a FQN class name
148     */
149    public void setInType(String inType) {
150        this.inType = inType;
151    }
152
153    public String getOutType() {
154        return outType;
155    }
156
157    /**
158     * To declare the outgoing POJO binding type as a FQN class name
159     */
160    public void setOutType(String outType) {
161        this.outType = outType;
162    }
163
164    public String getRouteId() {
165        return routeId;
166    }
167
168    /**
169     * Name of the route this REST services creates
170     */
171    public void setRouteId(String routeId) {
172        this.routeId = routeId;
173    }
174
175    public String getDescription() {
176        return description;
177    }
178
179    /**
180     * Human description to document this REST service
181     */
182    public void setDescription(String description) {
183        this.description = description;
184    }
185
186    public Map<String, Object> getParameters() {
187        return parameters;
188    }
189
190    /**
191     * Additional parameters to configure the consumer of the REST transport for this REST service
192     */
193    public void setParameters(Map<String, Object> parameters) {
194        this.parameters = parameters;
195    }
196
197    @Override
198    public Producer createProducer() throws Exception {
199        throw new UnsupportedOperationException("Producer not supported");
200    }
201
202    @Override
203    public Consumer createConsumer(Processor processor) throws Exception {
204        RestConsumerFactory factory = null;
205
206        if (getComponentName() != null) {
207            Object comp = getCamelContext().getRegistry().lookupByName(getComponentName());
208            if (comp != null && comp instanceof RestConsumerFactory) {
209                factory = (RestConsumerFactory) comp;
210            } else {
211                comp = getCamelContext().getComponent(getComponentName());
212                if (comp != null && comp instanceof RestConsumerFactory) {
213                    factory = (RestConsumerFactory) comp;
214                }
215            }
216
217            if (factory == null) {
218                if (comp != null) {
219                    throw new IllegalArgumentException("Component " + getComponentName() + " is not a RestConsumerFactory");
220                } else {
221                    throw new NoSuchBeanException(getComponentName(), RestConsumerFactory.class.getName());
222                }
223            }
224        }
225
226        // try all components
227        if (factory == null) {
228            for (String name : getCamelContext().getComponentNames()) {
229                Component comp = getCamelContext().getComponent(name);
230                if (comp != null && comp instanceof RestConsumerFactory) {
231                    factory = (RestConsumerFactory) comp;
232                    break;
233                }
234            }
235        }
236
237        // lookup in registry
238        if (factory == null) {
239            Set<RestConsumerFactory> factories = getCamelContext().getRegistry().findByType(RestConsumerFactory.class);
240            if (factories != null && factories.size() == 1) {
241                factory = factories.iterator().next();
242            }
243        }
244
245        if (factory != null) {
246            Consumer consumer = factory.createConsumer(getCamelContext(), processor, getMethod(), getPath(), getUriTemplate(), getConsumes(), getProduces(), getParameters());
247            configureConsumer(consumer);
248
249            // if no explicit port/host configured, then use port from rest configuration
250            String scheme = "http";
251            String host = "";
252            int port = 80;
253
254            RestConfiguration config = getCamelContext().getRestConfiguration();
255            if (config.getScheme() != null) {
256                scheme = config.getScheme();
257            }
258            if (config.getHost() != null) {
259                host = config.getHost();
260            }
261            int num = config.getPort();
262            if (num > 0) {
263                port = num;
264            }
265
266            // if no explicit hostname set then resolve the hostname
267            if (ObjectHelper.isEmpty(host)) {
268                if (config.getRestHostNameResolver() == RestConfiguration.RestHostNameResolver.localHostName) {
269                    host = HostUtils.getLocalHostName();
270                } else if (config.getRestHostNameResolver() == RestConfiguration.RestHostNameResolver.localIp) {
271                    host = HostUtils.getLocalIp();
272                }
273            }
274
275
276            // calculate the url to the rest service
277            String path = getPath();
278            if (!path.startsWith("/")) {
279                path = "/" + path;
280            }
281
282            // there may be an optional context path configured to help Camel calculate the correct urls for the REST services
283            // this may be needed when using camel-serlvet where we cannot get the actual context-path or port number of the servlet engine
284            // during init of the servlet
285            String contextPath = config.getContextPath();
286            if (contextPath != null) {
287                if (!contextPath.startsWith("/")) {
288                    path = "/" + contextPath + path;
289                } else {
290                    path = contextPath + path;
291                }
292            }
293
294            String baseUrl = scheme + "://" + host + (port != 80 ? ":" + port : "") + path;
295
296            String url = baseUrl;
297            if (uriTemplate != null) {
298                // make sure to avoid double slashes
299                if (uriTemplate.startsWith("/")) {
300                    url = url + uriTemplate;
301                } else {
302                    url = url + "/" + uriTemplate;
303                }
304            }
305
306            // add to rest registry so we can keep track of them, we will remove from the registry when the consumer is removed
307            // the rest registry will automatic keep track when the consumer is removed,
308            // and un-register the REST service from the registry
309            getCamelContext().getRestRegistry().addRestService(consumer, url, baseUrl, getPath(), getUriTemplate(), getMethod(),
310                    getConsumes(), getProduces(), getInType(), getOutType(), getRouteId(), getDescription());
311            return consumer;
312        } else {
313            throw new IllegalStateException("Cannot find RestConsumerFactory in Registry or as a Component to use");
314        }
315    }
316
317    @Override
318    public boolean isSingleton() {
319        return true;
320    }
321
322    @Override
323    public boolean isLenientProperties() {
324        return true;
325    }
326}