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