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