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