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.util;
018    
019    import java.util.Arrays;
020    import java.util.Collection;
021    import java.util.LinkedHashSet;
022    import java.util.List;
023    import java.util.Set;
024    
025    import org.apache.camel.Channel;
026    import org.apache.camel.Navigate;
027    import org.apache.camel.Processor;
028    import org.apache.camel.Service;
029    import org.apache.camel.ShutdownableService;
030    import org.apache.camel.StatefulService;
031    import org.apache.camel.SuspendableService;
032    import org.slf4j.Logger;
033    import org.slf4j.LoggerFactory;
034    
035    /**
036     * A collection of helper methods for working with {@link Service} objects.
037     * 
038     * @version
039     */
040    public final class ServiceHelper {
041        private static final Logger LOG = LoggerFactory.getLogger(ServiceHelper.class);
042    
043        /**
044         * Utility classes should not have a public constructor.
045         */
046        private ServiceHelper() {
047        }
048    
049        /**
050         * Starts the given {@code value} if it's a {@link Service} or a collection of it.
051         * <p/>
052         * Calling this method has no effect if {@code value} is {@code null}.
053         * 
054         * @see #startService(Service)
055         * @see #startServices(Collection)
056         */
057        public static void startService(Object value) throws Exception {
058            if (value instanceof Service) {
059                startService((Service)value);
060            } else if (value instanceof Collection) {
061                startServices((Collection<?>)value);
062            }
063        }
064        
065        /**
066         * Starts the given {@code service}.
067         * <p/>
068         * Calling this method has no effect if {@code service} is {@code null}.
069         * 
070         * @see Service#start()
071         */
072        public static void startService(Service service) throws Exception {
073            if (service != null) {
074                service.start();
075            }
076        }
077    
078        /**
079         * Starts each element of the given {@code services} if {@code services} itself is
080         * not {@code null}, otherwise this method would return immediately.
081         * 
082         * @see #startServices(Collection)
083         */
084        public static void startServices(Object... services) throws Exception {
085            if (services == null) {
086                return;
087            }
088            List<Object> list = Arrays.asList(services);
089            startServices(list);
090        }
091    
092        /**
093         * Starts each element of the given {@code services} if {@code services} itself is
094         * not {@code null}, otherwise this method would return immediately.
095         * 
096         * @see #startService(Object)
097         */
098        public static void startServices(Collection<?> services) throws Exception {
099            if (services == null) {
100                return;
101            }
102            for (Object value : services) {
103                startService(value);
104            }
105        }
106    
107        /**
108         * Stops each element of the given {@code services} if {@code services} itself is
109         * not {@code null}, otherwise this method would return immediately.
110         * <p/>
111         * If there's any exception being thrown while stopping the elements one after the
112         * other this method would rethrow the <b>first</b> such exception being thrown.
113         * 
114         * @see #stopServices(Collection)
115         */
116        public static void stopServices(Object... services) throws Exception {
117            if (services == null) {
118                return;
119            }
120            List<Object> list = Arrays.asList(services);
121            stopServices(list);
122        }
123    
124        /**
125         * Stops the given {@code value}, rethrowing the first exception caught.
126         * <p/>
127         * Calling this method has no effect if {@code value} is {@code null}.
128         * 
129         * @see Service#stop()
130         * @see #stopServices(Collection)
131         */
132        public static void stopService(Object value) throws Exception {
133            if (isStopped(value)) {
134                // only stop service if not already stopped
135                LOG.trace("Service already stopped: {}", value);
136                return;
137            }
138            if (value instanceof Service) {
139                Service service = (Service)value;
140                LOG.trace("Stopping service {}", value);
141                service.stop();
142            } else if (value instanceof Collection) {
143                stopServices((Collection<?>)value);
144            }
145        }
146    
147        /**
148         * Stops each element of the given {@code services} if {@code services} itself is
149         * not {@code null}, otherwise this method would return immediately.
150         * <p/>
151         * If there's any exception being thrown while stopping the elements one after the
152         * other this method would rethrow the <b>first</b> such exception being thrown.
153         * 
154         * @see #stopService(Object)
155         */
156        public static void stopServices(Collection<?> services) throws Exception {
157            if (services == null) {
158                return;
159            }
160            Exception firstException = null;
161            for (Object value : services) {
162                try {
163                    stopService(value);
164                } catch (Exception e) {
165                    if (LOG.isDebugEnabled()) {
166                        LOG.debug("Caught exception stopping service: " + value, e);
167                    }
168                    if (firstException == null) {
169                        firstException = e;
170                    }
171                }
172            }
173            if (firstException != null) {
174                throw firstException;
175            }
176        }
177    
178        /**
179         * Stops and shutdowns each element of the given {@code services} if {@code services} itself is
180         * not {@code null}, otherwise this method would return immediately.
181         * <p/>
182         * If there's any exception being thrown while stopping/shutting down the elements one after
183         * the other this method would rethrow the <b>first</b> such exception being thrown.
184         * 
185         * @see #stopAndShutdownServices(Collection)
186         */
187        public static void stopAndShutdownServices(Object... services) throws Exception {
188            if (services == null) {
189                return;
190            }
191            List<Object> list = Arrays.asList(services);
192            stopAndShutdownServices(list);
193        }
194    
195        /**
196         * Stops and shutdowns the given {@code service}, rethrowing the first exception caught.
197         * <p/>
198         * Calling this method has no effect if {@code value} is {@code null}.
199         * 
200         * @see #stopService(Object)
201         * @see ShutdownableService#shutdown()
202         */
203        public static void stopAndShutdownService(Object value) throws Exception {
204            stopService(value);
205    
206            // then try to shutdown
207            if (value instanceof ShutdownableService) {
208                ShutdownableService service = (ShutdownableService)value;
209                LOG.trace("Shutting down service {}", value);
210                service.shutdown();
211            }
212        }
213    
214        /**
215         * Stops and shutdowns each element of the given {@code services} if {@code services}
216         * itself is not {@code null}, otherwise this method would return immediately.
217         * <p/>
218         * If there's any exception being thrown while stopping/shutting down the elements one after
219         * the other this method would rethrow the <b>first</b> such exception being thrown.
220         * 
221         * @see #stopService(Object)
222         * @see ShutdownableService#shutdown()
223         */
224        public static void stopAndShutdownServices(Collection<?> services) throws Exception {
225            if (services == null) {
226                return;
227            }
228            Exception firstException = null;
229    
230            for (Object value : services) {
231    
232                try {
233                    // must stop it first
234                    stopService(value);
235    
236                    // then try to shutdown
237                    if (value instanceof ShutdownableService) {
238                        ShutdownableService service = (ShutdownableService)value;
239                        LOG.trace("Shutting down service: {}", service);
240                        service.shutdown();
241                    }
242                } catch (Exception e) {
243                    if (LOG.isDebugEnabled()) {
244                        LOG.debug("Caught exception shutting down service: " + value, e);
245                    }
246                    if (firstException == null) {
247                        firstException = e;
248                    }
249                }
250            }
251            if (firstException != null) {
252                throw firstException;
253            }
254        }
255    
256        /**
257         * Resumes each element of the given {@code services} if {@code services} itself is
258         * not {@code null}, otherwise this method would return immediately.
259         * <p/>
260         * If there's any exception being thrown while resuming the elements one after the
261         * other this method would rethrow the <b>first</b> such exception being thrown.
262         * 
263         * @see #resumeService(Service)
264         */
265        public static void resumeServices(Collection<?> services) throws Exception {
266            if (services == null) {
267                return;
268            }
269            Exception firstException = null;
270            for (Object value : services) {
271                if (value instanceof Service) {
272                    Service service = (Service)value;
273                    try {
274                        resumeService(service);
275                    } catch (Exception e) {
276                        if (LOG.isDebugEnabled()) {
277                            LOG.debug("Caught exception resuming service: " + service, e);
278                        }
279                        if (firstException == null) {
280                            firstException = e;
281                        }
282                    }
283                }
284            }
285            if (firstException != null) {
286                throw firstException;
287            }
288        }
289    
290        /**
291         * Resumes the given {@code service}.
292         * <p/>
293         * If {@code service} is a {@link org.apache.camel.SuspendableService} then
294         * it's {@link org.apache.camel.SuspendableService#resume()} is called but
295         * <b>only</b> if {@code service} is already {@link #isSuspended(Object)
296         * suspended}.
297         * <p/>
298         * If {@code service} is <b>not</b> a
299         * {@link org.apache.camel.SuspendableService} then it's
300         * {@link org.apache.camel.Service#start()} is called.
301         * <p/>
302         * Calling this method has no effect if {@code service} is {@code null}.
303         * 
304         * @param service the service
305         * @return <tt>true</tt> if either <tt>resume</tt> method or
306         *         {@link #startService(Service)} was called, <tt>false</tt>
307         *         otherwise.
308         * @throws Exception is thrown if error occurred
309         * @see #startService(Service)
310         */
311        public static boolean resumeService(Service service) throws Exception {
312            if (service instanceof SuspendableService) {
313                SuspendableService ss = (SuspendableService) service;
314                if (ss.isSuspended()) {
315                    LOG.debug("Resuming service {}", service);
316                    ss.resume();
317                    return true;
318                } else {
319                    return false;
320                }
321            } else {
322                startService(service);
323                return true;
324            }
325        }
326    
327        /**
328         * Suspends each element of the given {@code services} if {@code services} itself is
329         * not {@code null}, otherwise this method would return immediately.
330         * <p/>
331         * If there's any exception being thrown while suspending the elements one after the
332         * other this method would rethrow the <b>first</b> such exception being thrown.
333         * 
334         * @see #suspendService(Service)
335         */
336        public static void suspendServices(Collection<?> services) throws Exception {
337            if (services == null) {
338                return;
339            }
340            Exception firstException = null;
341            for (Object value : services) {
342                if (value instanceof Service) {
343                    Service service = (Service)value;
344                    try {
345                        suspendService(service);
346                    } catch (Exception e) {
347                        if (LOG.isDebugEnabled()) {
348                            LOG.debug("Caught exception suspending service: " + service, e);
349                        }
350                        if (firstException == null) {
351                            firstException = e;
352                        }
353                    }
354                }
355            }
356            if (firstException != null) {
357                throw firstException;
358            }
359        }
360    
361        /**
362         * Suspends the given {@code service}.
363         * <p/>
364         * If {@code service} is a {@link org.apache.camel.SuspendableService} then
365         * it's {@link org.apache.camel.SuspendableService#suspend()} is called but
366         * <b>only</b> if {@code service} is <b>not</b> already
367         * {@link #isSuspended(Object) suspended}.
368         * <p/>
369         * If {@code service} is <b>not</b> a
370         * {@link org.apache.camel.SuspendableService} then it's
371         * {@link org.apache.camel.Service#stop()} is called.
372         * <p/>
373         * Calling this method has no effect if {@code service} is {@code null}.
374         * 
375         * @param service the service
376         * @return <tt>true</tt> if either the <tt>suspend</tt> method or
377         *         {@link #stopService(Object)} was called, <tt>false</tt>
378         *         otherwise.
379         * @throws Exception is thrown if error occurred
380         * @see #stopService(Object)
381         */
382        public static boolean suspendService(Service service) throws Exception {
383            if (service instanceof SuspendableService) {
384                SuspendableService ss = (SuspendableService) service;
385                if (!ss.isSuspended()) {
386                    LOG.trace("Suspending service {}", service);
387                    ss.suspend();
388                    return true;
389                } else {
390                    return false;
391                }
392            } else {
393                stopService(service);
394                return true;
395            }
396        }
397    
398        /**
399         * Is the given service stopping or already stopped?
400         *
401         * @return <tt>true</tt> if stopping or already stopped, <tt>false</tt> otherwise
402         * @see StatefulService#isStopping()
403         * @see StatefulService#isStopped()
404         */
405        public static boolean isStopped(Object value) {
406            if (value instanceof StatefulService) {
407                StatefulService service = (StatefulService) value;
408                if (service.isStopping() || service.isStopped()) {
409                    return true;
410                }
411            }
412            return false;
413        }
414    
415        /**
416         * Is the given service starting or already started?
417         *
418         * @return <tt>true</tt> if starting or already started, <tt>false</tt> otherwise
419         * @see StatefulService#isStarting()
420         * @see StatefulService#isStarted()
421         */
422        public static boolean isStarted(Object value) {
423            if (value instanceof StatefulService) {
424                StatefulService service = (StatefulService) value;
425                if (service.isStarting() || service.isStarted()) {
426                    return true;
427                }
428            }
429            return false;
430        }
431        
432        /**
433         * Is the given service suspending or already suspended?
434         *
435         * @return <tt>true</tt> if suspending or already suspended, <tt>false</tt> otherwise
436         * @see StatefulService#isSuspending()
437         * @see StatefulService#isSuspended()
438         */
439        public static boolean isSuspended(Object value) {
440            if (value instanceof StatefulService) {
441                StatefulService service = (StatefulService) value;
442                if (service.isSuspending() || service.isSuspended()) {
443                    return true;
444                }
445            }
446            return false;
447        }
448    
449        /**
450         * Gathers all child services by navigating the service to recursively gather all child services.
451         * <p/>
452         * The returned set does <b>not</b> include the childern being error handler.
453         *
454         * @param service the service
455         * @return the services, including the parent service, and all its children
456         */
457        public static Set<Service> getChildServices(Service service) {
458            return getChildServices(service, false);
459        }
460    
461        /**
462         * Gathers all child services by navigating the service to recursively gather all child services.
463         *
464         * @param service the service
465         * @param includeErrorHandler whether to include error handlers
466         * @return the services, including the parent service, and all its children
467         */
468        public static Set<Service> getChildServices(Service service, boolean includeErrorHandler) {
469            Set<Service> answer = new LinkedHashSet<Service>();
470            doGetChildServices(answer, service, includeErrorHandler);
471            return answer;
472        }
473    
474        private static void doGetChildServices(Set<Service> services, Service service, boolean includeErrorHandler) {
475            services.add(service);
476            if (service instanceof Navigate) {
477                Navigate<?> nav = (Navigate<?>) service;
478                if (nav.hasNext()) {
479                    List<?> children = nav.next();
480                    for (Object child : children) {
481                        if (child instanceof Channel) {
482                            if (includeErrorHandler) {
483                                // special for error handler as they are tied to the Channel
484                                Processor errorHandler = ((Channel) child).getErrorHandler();
485                                if (errorHandler != null && errorHandler instanceof Service) {
486                                    services.add((Service) errorHandler);
487                                }
488                            }
489                            Processor next = ((Channel) child).getNextProcessor();
490                            if (next != null && next instanceof Service) {
491                                services.add((Service) next);
492                            }
493                        }
494                        if (child instanceof Service) {
495                            doGetChildServices(services, (Service) child, includeErrorHandler);
496                        }
497                    }
498                }
499            }
500        }
501        
502    }