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.NoFactoryAvailableException; 026import org.apache.camel.NoSuchBeanException; 027import org.apache.camel.Processor; 028import org.apache.camel.Producer; 029import org.apache.camel.impl.DefaultEndpoint; 030import org.apache.camel.spi.FactoryFinder; 031import org.apache.camel.spi.Metadata; 032import org.apache.camel.spi.RestApiConsumerFactory; 033import org.apache.camel.spi.RestApiProcessorFactory; 034import org.apache.camel.spi.RestConfiguration; 035import org.apache.camel.spi.UriEndpoint; 036import org.apache.camel.spi.UriParam; 037import org.apache.camel.spi.UriPath; 038import org.apache.camel.util.HostUtils; 039import org.apache.camel.util.ObjectHelper; 040 041/** 042 * The rest-api component is used for providing Swagger API of the REST services which has been defined using the rest-dsl in Camel. 043 */ 044@UriEndpoint(firstVersion = "2.16.0", scheme = "rest-api", title = "REST API", syntax = "rest-api:path/contextIdPattern", 045 consumerOnly = true, label = "core,rest", lenientProperties = true) 046public class RestApiEndpoint extends DefaultEndpoint { 047 048 public static final String DEFAULT_API_COMPONENT_NAME = "openapi"; 049 public static final String RESOURCE_PATH = "META-INF/services/org/apache/camel/restapi/"; 050 051 @UriPath 052 @Metadata(required = "true") 053 private String path; 054 @UriPath 055 private String contextIdPattern; 056 @UriParam 057 private String componentName; 058 @UriParam 059 private String apiComponentName; 060 061 private Map<String, Object> parameters; 062 063 public RestApiEndpoint(String endpointUri, RestApiComponent component) { 064 super(endpointUri, component); 065 setExchangePattern(ExchangePattern.InOut); 066 } 067 068 @Override 069 public RestApiComponent getComponent() { 070 return (RestApiComponent) super.getComponent(); 071 } 072 073 public String getPath() { 074 return path; 075 } 076 077 /** 078 * The base path 079 */ 080 public void setPath(String path) { 081 this.path = path; 082 } 083 084 public String getContextIdPattern() { 085 return contextIdPattern; 086 } 087 088 /** 089 * Optional CamelContext id pattern to only allow Rest APIs from rest services within CamelContext's which name matches the pattern. 090 */ 091 public void setContextIdPattern(String contextIdPattern) { 092 this.contextIdPattern = contextIdPattern; 093 } 094 095 public String getComponentName() { 096 return componentName; 097 } 098 099 /** 100 * The Camel Rest component to use for the REST transport, such as restlet, spark-rest. 101 * If no component has been explicit configured, then Camel will lookup if there is a Camel component 102 * that integrates with the Rest DSL, or if a org.apache.camel.spi.RestConsumerFactory is registered in the registry. 103 * If either one is found, then that is being used. 104 */ 105 public void setComponentName(String componentName) { 106 this.componentName = componentName; 107 } 108 109 public String getApiComponentName() { 110 return apiComponentName; 111 } 112 113 /** 114 * The Camel Rest API component to use for generating the API of the REST services, such as OpenApi. 115 */ 116 public void setApiComponentName(String apiComponentName) { 117 this.apiComponentName = apiComponentName; 118 } 119 120 public Map<String, Object> getParameters() { 121 return parameters; 122 } 123 124 /** 125 * Additional parameters to configure the consumer of the REST transport for this REST service 126 */ 127 public void setParameters(Map<String, Object> parameters) { 128 this.parameters = parameters; 129 } 130 131 @Override 132 public Producer createProducer() throws Exception { 133 RestApiProcessorFactory factory = null; 134 135 RestConfiguration config = getCamelContext().getRestConfiguration(componentName, true); 136 137 // lookup in registry 138 Set<RestApiProcessorFactory> factories = getCamelContext().getRegistry().findByType(RestApiProcessorFactory.class); 139 if (factories != null && factories.size() == 1) { 140 factory = factories.iterator().next(); 141 } 142 143 // lookup on classpath using factory finder to automatic find it (just add camel-openapi-java to classpath etc) 144 if (factory == null) { 145 String name = apiComponentName != null ? apiComponentName : config.getApiComponent(); 146 if (name == null) { 147 name = DEFAULT_API_COMPONENT_NAME; 148 } 149 try { 150 FactoryFinder finder = getCamelContext().getFactoryFinder(RESOURCE_PATH); 151 Object instance = finder.newInstance(name); 152 if (instance instanceof RestApiProcessorFactory) { 153 factory = (RestApiProcessorFactory) instance; 154 } 155 } catch (NoFactoryAvailableException e) { 156 // ignore 157 } 158 } 159 160 if (factory == null) { 161 String name = apiComponentName != null ? apiComponentName : config.getApiComponent(); 162 if (name == null) { 163 name = "swagger"; //use swagger as fallback 164 } 165 try { 166 FactoryFinder finder = getCamelContext().getFactoryFinder(RESOURCE_PATH); 167 Object instance = finder.newInstance(name); 168 if (instance instanceof RestApiProcessorFactory) { 169 factory = (RestApiProcessorFactory) instance; 170 } 171 } catch (NoFactoryAvailableException e) { 172 // ignore 173 } 174 } 175 176 177 if (factory != null) { 178 179 // if no explicit port/host configured, then use port from rest configuration 180 String host = ""; 181 int port = 80; 182 183 if (config.getApiHost() != null) { 184 host = config.getApiHost(); 185 } else if (config.getHost() != null) { 186 host = config.getHost(); 187 } 188 int num = config.getPort(); 189 if (num > 0) { 190 port = num; 191 } 192 193 // if no explicit hostname set then resolve the hostname 194 if (ObjectHelper.isEmpty(host)) { 195 if (config.getHostNameResolver() == RestConfiguration.RestHostNameResolver.allLocalIp) { 196 host = "0.0.0.0"; 197 } else if (config.getHostNameResolver() == RestConfiguration.RestHostNameResolver.localHostName) { 198 host = HostUtils.getLocalHostName(); 199 } else if (config.getHostNameResolver() == RestConfiguration.RestHostNameResolver.localIp) { 200 host = HostUtils.getLocalIp(); 201 } 202 203 // no host was configured so calculate a host to use 204 // there should be no schema in the host (but only port) 205 String targetHost = host + (port != 80 ? ":" + port : ""); 206 getParameters().put("host", targetHost); 207 } 208 209 // the base path should start with a leading slash 210 String path = getPath(); 211 if (path != null && !path.startsWith("/")) { 212 path = "/" + path; 213 } 214 215 // whether listing of the context id's is enabled or not 216 boolean contextIdListing = config.isApiContextListing(); 217 218 Processor processor = factory.createApiProcessor(getCamelContext(), path, getContextIdPattern(), contextIdListing, config, getParameters()); 219 return new RestApiProducer(this, processor); 220 } else { 221 throw new IllegalStateException("Cannot find RestApiProcessorFactory in Registry or classpath (such as the camel-openapi-java component)"); 222 } 223 } 224 225 @Override 226 public Consumer createConsumer(Processor processor) throws Exception { 227 RestApiConsumerFactory factory = null; 228 String cname = null; 229 230 // we use the rest component as the HTTP consumer to service the API 231 // the API then uses the api component (eg usually camel-openapi-java) to build the API 232 if (getComponentName() != null) { 233 Object comp = getCamelContext().getRegistry().lookupByName(getComponentName()); 234 if (comp instanceof RestApiConsumerFactory) { 235 factory = (RestApiConsumerFactory) comp; 236 } else { 237 comp = getCamelContext().getComponent(getComponentName()); 238 if (comp instanceof RestApiConsumerFactory) { 239 factory = (RestApiConsumerFactory) comp; 240 } 241 } 242 243 if (factory == null) { 244 if (comp != null) { 245 throw new IllegalArgumentException("Component " + getComponentName() + " is not a RestApiConsumerFactory"); 246 } else { 247 throw new NoSuchBeanException(getComponentName(), RestApiConsumerFactory.class.getName()); 248 } 249 } 250 cname = getComponentName(); 251 } 252 253 // try all components 254 if (factory == null) { 255 for (String name : getCamelContext().getComponentNames()) { 256 Component comp = getCamelContext().getComponent(name); 257 if (comp instanceof RestApiConsumerFactory) { 258 factory = (RestApiConsumerFactory) comp; 259 cname = name; 260 break; 261 } 262 } 263 } 264 265 // lookup in registry 266 if (factory == null) { 267 Set<RestApiConsumerFactory> factories = getCamelContext().getRegistry().findByType(RestApiConsumerFactory.class); 268 if (factories != null && factories.size() == 1) { 269 factory = factories.iterator().next(); 270 } 271 } 272 273 if (factory != null) { 274 // calculate the url to the rest API service 275 RestConfiguration config = getCamelContext().getRestConfiguration(cname, true); 276 277 // calculate the url to the rest API service 278 String path = getPath(); 279 if (path != null && !path.startsWith("/")) { 280 path = "/" + path; 281 } 282 283 Consumer consumer = factory.createApiConsumer(getCamelContext(), processor, path, config, getParameters()); 284 configureConsumer(consumer); 285 286 return consumer; 287 } else { 288 throw new IllegalStateException("Cannot find RestApiConsumerFactory in Registry or as a Component to use"); 289 } 290 } 291 292 @Override 293 public boolean isSingleton() { 294 return true; 295 } 296 297 @Override 298 public boolean isLenientProperties() { 299 return true; 300 } 301}