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", title = "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}