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