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     */
017    package org.apache.camel.impl;
018    
019    import java.util.ArrayList;
020    import java.util.Arrays;
021    import java.util.Collection;
022    import java.util.HashMap;
023    import java.util.List;
024    import java.util.Map;
025    
026    import org.apache.camel.CamelContext;
027    import org.apache.camel.Consumer;
028    import org.apache.camel.Navigate;
029    import org.apache.camel.Route;
030    import org.apache.camel.Service;
031    import org.apache.camel.model.RouteDefinition;
032    import org.apache.camel.spi.LifecycleStrategy;
033    import org.apache.camel.spi.RouteContext;
034    import org.apache.camel.util.EventHelper;
035    import org.apache.camel.util.ServiceHelper;
036    import org.apache.commons.logging.Log;
037    import org.apache.commons.logging.LogFactory;
038    
039    /**
040     * Represents the runtime objects for a given {@link RouteDefinition} so that it can be stopped independently
041     * of other routes
042     *
043     * @version $Revision: 830861 $
044     */
045    public class RouteService extends ServiceSupport {
046    
047        private static final Log LOG = LogFactory.getLog(RouteService.class);
048    
049        private final DefaultCamelContext camelContext;
050        private final RouteDefinition routeDefinition;
051        private final List<RouteContext> routeContexts;
052        private final List<Route> routes;
053        private final String id;
054        private boolean startInputs = true;
055        private final Map<Route, Consumer> inputs = new HashMap<Route, Consumer>();
056    
057        public RouteService(DefaultCamelContext camelContext, RouteDefinition routeDefinition, List<RouteContext> routeContexts, List<Route> routes) {
058            this.camelContext = camelContext;
059            this.routeDefinition = routeDefinition;
060            this.routeContexts = routeContexts;
061            this.routes = routes;
062            this.id = routeDefinition.idOrCreate(camelContext.getNodeIdFactory());
063        }
064    
065        public String getId() {
066            return id;
067        }
068    
069        public CamelContext getCamelContext() {
070            return camelContext;
071        }
072    
073        public List<RouteContext> getRouteContexts() {
074            return routeContexts;
075        }
076    
077        public RouteDefinition getRouteDefinition() {
078            return routeDefinition;
079        }
080    
081        public Collection<Route> getRoutes() {
082            return routes;
083        }
084    
085        /**
086         * Sets whether inputs (consumers) should be started when starting the routes
087         * <p/>
088         * By default inputs are started.
089         *
090         * @param flag flag to either start inputs or not
091         */
092        public void startInputs(boolean flag) {
093            this.startInputs = flag;
094        }
095    
096        /**
097         * Gets the inputs to the routes.
098         *
099         * @return list of {@link Consumer} as inputs for the routes
100         */
101        public Map<Route, Consumer> getInputs() {
102            return inputs;
103        }
104    
105        protected void doStart() throws Exception {
106            camelContext.addRouteCollection(routes);
107    
108            for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) {
109                strategy.onRoutesAdd(routes);
110            }
111    
112            for (Route route : routes) {
113                if (LOG.isTraceEnabled()) {
114                    LOG.trace("Starting route services: " + route);
115                }
116    
117                // TODO: We should also consider processors which are not services then we can manage all processors as well
118                // otherwise its only the processors which is a Service
119    
120                List<Service> services = route.getServices();
121    
122                // callback that we are staring these services
123                route.onStartingServices(services);
124    
125                // gather list of services to start as we need to start child services as well
126                List<Service> list = new ArrayList<Service>();
127                for (Service service : services) {
128                    doGetChildServices(list, service);
129                }
130    
131                // split into consumers and child services as we need to start the consumers
132                // afterwards to avoid them being active while the others start
133                List<Service> childServices = new ArrayList<Service>();
134                for (Service service : list) {
135                    if (service instanceof Consumer) {
136                        inputs.put(route, (Consumer) service);
137                    } else {
138                        childServices.add(service);
139                    }
140                }
141                startChildService(route, childServices);
142    
143                // start the route itself
144                ServiceHelper.startService(route);
145    
146                // fire event
147                EventHelper.notifyRouteStarted(camelContext, route);
148            }
149    
150            if (startInputs) {
151                // start the input consumers
152                for (Map.Entry<Route, Consumer> entry : inputs.entrySet()) {
153                    Route route = entry.getKey();
154                    Consumer consumer = entry.getValue();
155                    startChildService(route, consumer);
156                }
157            }
158        }
159    
160        protected void doStop() throws Exception {
161            // clear inputs
162            inputs.clear();
163    
164            for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) {
165                strategy.onRoutesRemove(routes);
166            }
167    
168            for (Route route : routes) {
169                if (LOG.isTraceEnabled()) {
170                    LOG.trace("Stopping route: " + route);
171                }
172                // getServices will not add services again
173                List<Service> services = route.getServices();
174    
175                // gather list of services to stop as we need to start child services as well
176                List<Service> list = new ArrayList<Service>();
177                for (Service service : services) {
178                    doGetChildServices(list, service);
179                }
180                stopChildService(route, list);
181    
182                // stop the route itself
183                ServiceHelper.stopService(route);
184    
185                // fire event
186                EventHelper.notifyRouteStopped(camelContext, route);
187            }
188    
189            camelContext.removeRouteCollection(routes);
190        }
191    
192        protected void startChildService(Route route, Service... services) throws Exception {
193            List<Service> list = new ArrayList<Service>(Arrays.asList(services));
194            startChildService(route, list);
195        }
196    
197        protected void startChildService(Route route, List<Service> services) throws Exception {
198            for (Service service : services) {
199                for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) {
200                    strategy.onServiceAdd(camelContext, service, route);
201                }
202                ServiceHelper.startService(service);
203                addChildService(service);
204            }
205        }
206    
207        protected void stopChildService(Route route, List<Service> services) throws Exception {
208            for (Service service : services) {
209                for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) {
210                    strategy.onServiceRemove(camelContext, service, route);
211                }
212                ServiceHelper.stopService(service);
213                removeChildService(service);
214            }
215        }
216    
217        /**
218         * Need to recursive start child services for routes
219         */
220        private static void doGetChildServices(List<Service> services, Service service) throws Exception {
221            services.add(service);
222    
223            if (service instanceof Navigate) {
224                Navigate<?> nav = (Navigate<?>) service;
225                if (nav.hasNext()) {
226                    List<?> children = nav.next();
227                    for (Object child : children) {
228                        if (child instanceof Service) {
229                            doGetChildServices(services, (Service) child);
230                        }
231                    }
232                }
233            }
234        }
235    
236    }