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