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.Collection; 021import java.util.HashMap; 022import java.util.LinkedHashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026import java.util.concurrent.atomic.AtomicBoolean; 027 028import org.apache.camel.CamelContext; 029import org.apache.camel.Channel; 030import org.apache.camel.Consumer; 031import org.apache.camel.Endpoint; 032import org.apache.camel.EndpointAware; 033import org.apache.camel.Processor; 034import org.apache.camel.Route; 035import org.apache.camel.RouteAware; 036import org.apache.camel.Service; 037import org.apache.camel.model.OnCompletionDefinition; 038import org.apache.camel.model.OnExceptionDefinition; 039import org.apache.camel.model.ProcessorDefinition; 040import org.apache.camel.model.RouteDefinition; 041import org.apache.camel.processor.ErrorHandler; 042import org.apache.camel.spi.LifecycleStrategy; 043import org.apache.camel.spi.RouteContext; 044import org.apache.camel.spi.RoutePolicy; 045import org.apache.camel.support.ChildServiceSupport; 046import org.apache.camel.util.EventHelper; 047import org.apache.camel.util.ServiceHelper; 048import org.slf4j.Logger; 049import org.slf4j.LoggerFactory; 050 051/** 052 * Represents the runtime objects for a given {@link RouteDefinition} so that it can be stopped independently 053 * of other routes 054 * 055 * @version 056 */ 057public class RouteService extends ChildServiceSupport { 058 059 private static final Logger LOG = LoggerFactory.getLogger(RouteService.class); 060 061 private final DefaultCamelContext camelContext; 062 private final RouteDefinition routeDefinition; 063 private final List<RouteContext> routeContexts; 064 private final List<Route> routes; 065 private final String id; 066 private boolean removingRoutes; 067 private final Map<Route, Consumer> inputs = new HashMap<Route, Consumer>(); 068 private final AtomicBoolean warmUpDone = new AtomicBoolean(false); 069 private final AtomicBoolean endpointDone = new AtomicBoolean(false); 070 071 public RouteService(DefaultCamelContext camelContext, RouteDefinition routeDefinition, List<RouteContext> routeContexts, List<Route> routes) { 072 this.camelContext = camelContext; 073 this.routeDefinition = routeDefinition; 074 this.routeContexts = routeContexts; 075 this.routes = routes; 076 this.id = routeDefinition.idOrCreate(camelContext.getNodeIdFactory()); 077 } 078 079 public String getId() { 080 return id; 081 } 082 083 public CamelContext getCamelContext() { 084 return camelContext; 085 } 086 087 public List<RouteContext> getRouteContexts() { 088 return routeContexts; 089 } 090 091 public RouteDefinition getRouteDefinition() { 092 return routeDefinition; 093 } 094 095 public Collection<Route> getRoutes() { 096 return routes; 097 } 098 099 /** 100 * Gather all the endpoints this route service uses 101 * <p/> 102 * This implementation finds the endpoints by searching all the child services 103 * for {@link org.apache.camel.EndpointAware} processors which uses an endpoint. 104 */ 105 public Set<Endpoint> gatherEndpoints() { 106 Set<Endpoint> answer = new LinkedHashSet<Endpoint>(); 107 for (Route route : routes) { 108 Set<Service> services = gatherChildServices(route, true); 109 for (Service service : services) { 110 if (service instanceof EndpointAware) { 111 Endpoint endpoint = ((EndpointAware) service).getEndpoint(); 112 if (endpoint != null) { 113 answer.add(endpoint); 114 } 115 } 116 } 117 } 118 return answer; 119 } 120 121 /** 122 * Gets the inputs to the routes. 123 * 124 * @return list of {@link Consumer} as inputs for the routes 125 */ 126 public Map<Route, Consumer> getInputs() { 127 return inputs; 128 } 129 130 public boolean isRemovingRoutes() { 131 return removingRoutes; 132 } 133 134 public void setRemovingRoutes(boolean removingRoutes) { 135 this.removingRoutes = removingRoutes; 136 } 137 138 public synchronized void warmUp() throws Exception { 139 if (endpointDone.compareAndSet(false, true)) { 140 // endpoints should only be started once as they can be reused on other routes 141 // and whatnot, thus their lifecycle is to start once, and only to stop when Camel shutdown 142 for (Route route : routes) { 143 // ensure endpoint is started first (before the route services, such as the consumer) 144 ServiceHelper.startService(route.getEndpoint()); 145 } 146 } 147 148 if (warmUpDone.compareAndSet(false, true)) { 149 150 for (Route route : routes) { 151 // warm up the route first 152 route.warmUp(); 153 154 LOG.debug("Starting services on route: {}", route.getId()); 155 List<Service> services = route.getServices(); 156 157 // callback that we are staring these services 158 route.onStartingServices(services); 159 160 // gather list of services to start as we need to start child services as well 161 Set<Service> list = new LinkedHashSet<Service>(); 162 for (Service service : services) { 163 list.addAll(ServiceHelper.getChildServices(service)); 164 } 165 166 // split into consumers and child services as we need to start the consumers 167 // afterwards to avoid them being active while the others start 168 List<Service> childServices = new ArrayList<Service>(); 169 for (Service service : list) { 170 171 // inject the route 172 if (service instanceof RouteAware) { 173 ((RouteAware) service).setRoute(route); 174 } 175 176 if (service instanceof Consumer) { 177 inputs.put(route, (Consumer) service); 178 } else { 179 childServices.add(service); 180 } 181 } 182 startChildService(route, childServices); 183 184 // fire event 185 EventHelper.notifyRouteAdded(camelContext, route); 186 } 187 188 // ensure lifecycle strategy is invoked which among others enlist the route in JMX 189 for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) { 190 strategy.onRoutesAdd(routes); 191 } 192 193 // add routes to camel context 194 camelContext.addRouteCollection(routes); 195 } 196 } 197 198 protected void doStart() throws Exception { 199 // ensure we are warmed up before starting the route 200 warmUp(); 201 202 for (Route route : routes) { 203 // start the route itself 204 ServiceHelper.startService(route); 205 206 // invoke callbacks on route policy 207 if (route.getRouteContext().getRoutePolicyList() != null) { 208 for (RoutePolicy routePolicy : route.getRouteContext().getRoutePolicyList()) { 209 routePolicy.onStart(route); 210 } 211 } 212 213 // fire event 214 EventHelper.notifyRouteStarted(camelContext, route); 215 } 216 } 217 218 protected void doStop() throws Exception { 219 220 // if we are stopping CamelContext then we are shutting down 221 boolean isShutdownCamelContext = camelContext.isStopping(); 222 223 if (isShutdownCamelContext || isRemovingRoutes()) { 224 // need to call onRoutesRemove when the CamelContext is shutting down or Route is shutdown 225 for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) { 226 strategy.onRoutesRemove(routes); 227 } 228 } 229 230 for (Route route : routes) { 231 LOG.debug("Stopping services on route: {}", route.getId()); 232 233 // gather list of services to stop as we need to start child services as well 234 Set<Service> services = gatherChildServices(route, true); 235 236 // stop services 237 stopChildService(route, services, isShutdownCamelContext); 238 239 // stop the route itself 240 if (isShutdownCamelContext) { 241 ServiceHelper.stopAndShutdownServices(route); 242 } else { 243 ServiceHelper.stopServices(route); 244 } 245 246 // invoke callbacks on route policy 247 if (route.getRouteContext().getRoutePolicyList() != null) { 248 for (RoutePolicy routePolicy : route.getRouteContext().getRoutePolicyList()) { 249 routePolicy.onStop(route); 250 } 251 } 252 // fire event 253 EventHelper.notifyRouteStopped(camelContext, route); 254 } 255 if (isRemovingRoutes()) { 256 camelContext.removeRouteCollection(routes); 257 } 258 // need to warm up again 259 warmUpDone.set(false); 260 } 261 262 @Override 263 protected void doShutdown() throws Exception { 264 for (Route route : routes) { 265 LOG.debug("Shutting down services on route: {}", route.getId()); 266 267 // gather list of services to stop as we need to start child services as well 268 Set<Service> services = gatherChildServices(route, true); 269 270 // shutdown services 271 stopChildService(route, services, true); 272 273 // shutdown the route itself 274 ServiceHelper.stopAndShutdownServices(route); 275 276 // endpoints should only be stopped when Camel is shutting down 277 // see more details in the warmUp method 278 ServiceHelper.stopAndShutdownServices(route.getEndpoint()); 279 // invoke callbacks on route policy 280 if (route.getRouteContext().getRoutePolicyList() != null) { 281 for (RoutePolicy routePolicy : route.getRouteContext().getRoutePolicyList()) { 282 routePolicy.onRemove(route); 283 } 284 } 285 // fire event 286 EventHelper.notifyRouteRemoved(camelContext, route); 287 } 288 289 // need to call onRoutesRemove when the CamelContext is shutting down or Route is shutdown 290 for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) { 291 strategy.onRoutesRemove(routes); 292 } 293 294 // remove the routes from the inflight registry 295 for (Route route : routes) { 296 camelContext.getInflightRepository().removeRoute(route.getId()); 297 } 298 299 // remove the routes from the collections 300 camelContext.removeRouteCollection(routes); 301 302 // clear inputs on shutdown 303 inputs.clear(); 304 warmUpDone.set(false); 305 endpointDone.set(false); 306 } 307 308 @Override 309 protected void doSuspend() throws Exception { 310 // suspend and resume logic is provided by DefaultCamelContext which leverages ShutdownStrategy 311 // to safely suspend and resume 312 for (Route route : routes) { 313 if (route.getRouteContext().getRoutePolicyList() != null) { 314 for (RoutePolicy routePolicy : route.getRouteContext().getRoutePolicyList()) { 315 routePolicy.onSuspend(route); 316 } 317 } 318 } 319 } 320 321 @Override 322 protected void doResume() throws Exception { 323 // suspend and resume logic is provided by DefaultCamelContext which leverages ShutdownStrategy 324 // to safely suspend and resume 325 for (Route route : routes) { 326 if (route.getRouteContext().getRoutePolicyList() != null) { 327 for (RoutePolicy routePolicy : route.getRouteContext().getRoutePolicyList()) { 328 routePolicy.onResume(route); 329 } 330 } 331 } 332 } 333 334 protected void startChildService(Route route, List<Service> services) throws Exception { 335 for (Service service : services) { 336 LOG.debug("Starting child service on route: {} -> {}", route.getId(), service); 337 for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) { 338 strategy.onServiceAdd(camelContext, service, route); 339 } 340 ServiceHelper.startService(service); 341 addChildService(service); 342 } 343 } 344 345 protected void stopChildService(Route route, Set<Service> services, boolean shutdown) throws Exception { 346 for (Service service : services) { 347 LOG.debug("{} child service on route: {} -> {}", new Object[]{shutdown ? "Shutting down" : "Stopping", route.getId(), service}); 348 if (service instanceof ErrorHandler) { 349 // special for error handlers 350 for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) { 351 strategy.onErrorHandlerRemove(route.getRouteContext(), (Processor) service, route.getRouteContext().getRoute().getErrorHandlerBuilder()); 352 } 353 } else { 354 for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) { 355 strategy.onServiceRemove(camelContext, service, route); 356 } 357 } 358 if (shutdown) { 359 ServiceHelper.stopAndShutdownService(service); 360 } else { 361 ServiceHelper.stopService(service); 362 } 363 removeChildService(service); 364 } 365 } 366 367 /** 368 * Gather all child services 369 */ 370 private Set<Service> gatherChildServices(Route route, boolean includeErrorHandler) { 371 // gather list of services to stop as we need to start child services as well 372 List<Service> services = new ArrayList<Service>(); 373 services.addAll(route.getServices()); 374 // also get route scoped services 375 doGetRouteScopedServices(services, route); 376 Set<Service> list = new LinkedHashSet<Service>(); 377 for (Service service : services) { 378 list.addAll(ServiceHelper.getChildServices(service)); 379 } 380 if (includeErrorHandler) { 381 // also get route scoped error handler (which must be done last) 382 doGetRouteScopedErrorHandler(list, route); 383 } 384 Set<Service> answer = new LinkedHashSet<Service>(); 385 answer.addAll(list); 386 return answer; 387 } 388 389 /** 390 * Gather the route scoped error handler from the given route 391 */ 392 private void doGetRouteScopedErrorHandler(Set<Service> services, Route route) { 393 // only include error handlers if they are route scoped 394 boolean includeErrorHandler = !routeDefinition.isContextScopedErrorHandler(route.getRouteContext().getCamelContext()); 395 List<Service> extra = new ArrayList<Service>(); 396 if (includeErrorHandler) { 397 for (Service service : services) { 398 if (service instanceof Channel) { 399 Processor eh = ((Channel) service).getErrorHandler(); 400 if (eh != null && eh instanceof Service) { 401 extra.add((Service) eh); 402 } 403 } 404 } 405 } 406 if (!extra.isEmpty()) { 407 services.addAll(extra); 408 } 409 } 410 411 /** 412 * Gather all other kind of route scoped services from the given route, except error handler 413 */ 414 private void doGetRouteScopedServices(List<Service> services, Route route) { 415 for (ProcessorDefinition<?> output : route.getRouteContext().getRoute().getOutputs()) { 416 if (output instanceof OnExceptionDefinition) { 417 OnExceptionDefinition onExceptionDefinition = (OnExceptionDefinition) output; 418 if (onExceptionDefinition.isRouteScoped()) { 419 Processor errorHandler = onExceptionDefinition.getErrorHandler(route.getId()); 420 if (errorHandler != null && errorHandler instanceof Service) { 421 services.add((Service) errorHandler); 422 } 423 } 424 } else if (output instanceof OnCompletionDefinition) { 425 OnCompletionDefinition onCompletionDefinition = (OnCompletionDefinition) output; 426 if (onCompletionDefinition.isRouteScoped()) { 427 Processor onCompletionProcessor = onCompletionDefinition.getOnCompletion(route.getId()); 428 if (onCompletionProcessor != null && onCompletionProcessor instanceof Service) { 429 services.add((Service) onCompletionProcessor); 430 } 431 } 432 } 433 } 434 } 435 436}