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.impl; 018 019import java.util.ArrayList; 020import java.util.LinkedHashMap; 021import java.util.List; 022import java.util.Map; 023 024import org.apache.camel.CamelContext; 025import org.apache.camel.CamelContextAware; 026import org.apache.camel.Consumer; 027import org.apache.camel.Endpoint; 028import org.apache.camel.Exchange; 029import org.apache.camel.Producer; 030import org.apache.camel.Route; 031import org.apache.camel.Service; 032import org.apache.camel.ServiceStatus; 033import org.apache.camel.StatefulService; 034import org.apache.camel.StaticService; 035import org.apache.camel.component.rest.RestApiEndpoint; 036import org.apache.camel.component.rest.RestEndpoint; 037import org.apache.camel.spi.RestConfiguration; 038import org.apache.camel.spi.RestRegistry; 039import org.apache.camel.support.LifecycleStrategySupport; 040import org.apache.camel.support.ServiceSupport; 041import org.apache.camel.util.ObjectHelper; 042 043public class DefaultRestRegistry extends ServiceSupport implements StaticService, RestRegistry, CamelContextAware { 044 045 private CamelContext camelContext; 046 private final Map<Consumer, RestService> registry = new LinkedHashMap<>(); 047 private transient Producer apiProducer; 048 049 public void addRestService(Consumer consumer, String url, String baseUrl, String basePath, String uriTemplate, String method, 050 String consumes, String produces, String inType, String outType, String routeId, String description) { 051 RestServiceEntry entry = new RestServiceEntry(consumer, url, baseUrl, basePath, uriTemplate, method, consumes, produces, inType, outType, routeId, description); 052 registry.put(consumer, entry); 053 } 054 055 public void removeRestService(Consumer consumer) { 056 registry.remove(consumer); 057 } 058 059 @Override 060 public List<RestRegistry.RestService> listAllRestServices() { 061 return new ArrayList<>(registry.values()); 062 } 063 064 @Override 065 public int size() { 066 return registry.size(); 067 } 068 069 @Override 070 public String apiDocAsJson() { 071 // see if there is a rest-api endpoint which would be the case if rest api-doc has been explicit enabled 072 if (apiProducer == null) { 073 Endpoint restApiEndpoint = null; 074 Endpoint restEndpoint = null; 075 for (Map.Entry<String, Endpoint> entry : camelContext.getEndpointMap().entrySet()) { 076 String uri = entry.getKey(); 077 if (uri.startsWith("rest-api:")) { 078 restApiEndpoint = entry.getValue(); 079 break; 080 } else if (restEndpoint == null && uri.startsWith("rest:")) { 081 restEndpoint = entry.getValue(); 082 } 083 } 084 085 if (restApiEndpoint == null && restEndpoint != null) { 086 // no rest-api has been explicit enabled, then we need to create it first 087 RestEndpoint rest = (RestEndpoint) restEndpoint; 088 String componentName = rest.getComponentName(); 089 090 if (componentName != null) { 091 RestConfiguration config = camelContext.getRestConfiguration(componentName, true); 092 String apiComponent = config.getApiComponent() != null ? config.getApiComponent() : RestApiEndpoint.DEFAULT_API_COMPONENT_NAME; 093 String path = config.getApiContextPath() != null ? config.getApiContextPath() : "api-doc"; 094 restApiEndpoint = camelContext.getEndpoint(String.format("rest-api:%s/%s?componentName=%s&apiComponentName=%s&contextIdPattern=#name#", 095 path, camelContext.getName(), componentName, apiComponent)); 096 } 097 } 098 099 if (restApiEndpoint != null) { 100 // reuse the producer to avoid creating it 101 try { 102 apiProducer = restApiEndpoint.createProducer(); 103 camelContext.addService(apiProducer, true); 104 } catch (Exception e) { 105 throw ObjectHelper.wrapRuntimeCamelException(e); 106 } 107 } 108 } 109 110 if (apiProducer != null) { 111 try { 112 Exchange dummy = apiProducer.getEndpoint().createExchange(); 113 apiProducer.process(dummy); 114 115 String json = dummy.hasOut() ? dummy.getOut().getBody(String.class) : dummy.getIn().getBody(String.class); 116 return json; 117 } catch (Exception e) { 118 throw ObjectHelper.wrapRuntimeCamelException(e); 119 } 120 } 121 122 return null; 123 } 124 125 public CamelContext getCamelContext() { 126 return camelContext; 127 } 128 129 public void setCamelContext(CamelContext camelContext) { 130 this.camelContext = camelContext; 131 } 132 133 @Override 134 protected void doStart() throws Exception { 135 ObjectHelper.notNull(camelContext, "camelContext", this); 136 // add a lifecycle so we can keep track when consumers is being removed, so we can unregister them from our registry 137 camelContext.addLifecycleStrategy(new RemoveRestServiceLifecycleStrategy()); 138 } 139 140 @Override 141 protected void doStop() throws Exception { 142 registry.clear(); 143 } 144 145 /** 146 * Represents a rest service 147 */ 148 private final class RestServiceEntry implements RestService { 149 150 private final Consumer consumer; 151 private final String url; 152 private final String baseUrl; 153 private final String basePath; 154 private final String uriTemplate; 155 private final String method; 156 private final String consumes; 157 private final String produces; 158 private final String inType; 159 private final String outType; 160 private final String routeId; 161 private final String description; 162 163 private RestServiceEntry(Consumer consumer, String url, String baseUrl, String basePath, String uriTemplate, String method, 164 String consumes, String produces, String inType, String outType, String routeId, String description) { 165 this.consumer = consumer; 166 this.url = url; 167 this.baseUrl = baseUrl; 168 this.basePath = basePath; 169 this.uriTemplate = uriTemplate; 170 this.method = method; 171 this.consumes = consumes; 172 this.produces = produces; 173 this.inType = inType; 174 this.outType = outType; 175 this.routeId = routeId; 176 this.description = description; 177 } 178 179 public Consumer getConsumer() { 180 return consumer; 181 } 182 183 public String getUrl() { 184 return url; 185 } 186 187 public String getBaseUrl() { 188 return baseUrl; 189 } 190 191 public String getBasePath() { 192 return basePath; 193 } 194 195 public String getUriTemplate() { 196 return uriTemplate; 197 } 198 199 public String getMethod() { 200 return method; 201 } 202 203 public String getConsumes() { 204 return consumes; 205 } 206 207 public String getProduces() { 208 return produces; 209 } 210 211 public String getInType() { 212 return inType; 213 } 214 215 public String getOutType() { 216 return outType; 217 } 218 219 public String getState() { 220 // must use String type to be sure remote JMX can read the attribute without requiring Camel classes. 221 ServiceStatus status = null; 222 if (consumer instanceof StatefulService) { 223 status = ((StatefulService) consumer).getStatus(); 224 } 225 // if no status exists then its stopped 226 if (status == null) { 227 status = ServiceStatus.Stopped; 228 } 229 return status.name(); 230 } 231 232 public String getRouteId() { 233 return routeId; 234 } 235 236 public String getDescription() { 237 return description; 238 } 239 } 240 241 /** 242 * A {@link org.apache.camel.spi.LifecycleStrategy} that keeps track when a {@link Consumer} is removed 243 * and automatic un-register it from this REST registry. 244 */ 245 private final class RemoveRestServiceLifecycleStrategy extends LifecycleStrategySupport { 246 247 @Override 248 public void onServiceRemove(CamelContext context, Service service, Route route) { 249 super.onServiceRemove(context, service, route); 250 251 // if its a consumer then de-register it from the rest registry 252 if (service instanceof Consumer) { 253 removeRestService((Consumer) service); 254 } 255 } 256 } 257}