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