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