1/** 2 * Copyright (C) 2010 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package com.google.inject.service; 17 18import com.google.common.base.Preconditions; 19import com.google.common.collect.ImmutableList; 20import com.google.common.collect.Lists; 21import com.google.common.collect.Sets; 22import com.google.inject.Inject; 23import com.google.inject.Injector; 24import com.google.inject.Key; 25 26import java.util.List; 27import java.util.Set; 28import java.util.concurrent.Callable; 29import java.util.concurrent.ExecutionException; 30import java.util.concurrent.Future; 31import java.util.concurrent.FutureTask; 32 33/** 34 * A service that composes other services together in a fixed order. 35 * 36 * @author dhanji@gmail.com (Dhanji R. Prasanna) 37 */ 38public class CompositeService { 39 private final Injector injector; 40 41 private final Set<Key<? extends Service>> services = Sets.newLinkedHashSet(); 42 43 /** 44 * Represents the state of this composite service. Will equal FAILED 45 * even if only one component service fails to start or stop. In other 46 * words, all component services must start successfully for this 47 * service to be considered started and similarly for stopped. 48 */ 49 private volatile Service.State compositeState; 50 private boolean composed; 51 52 @Inject 53 CompositeService(Injector injector) { 54 this.injector = injector; 55 } 56 57 public CompositeService add(Class<? extends Service> service) { 58 return add(Key.get(service)); 59 } 60 61 public CompositeService add(Key<? extends Service> service) { 62 Preconditions.checkState(!composed, 63 "Cannot reuse a CompositeService after it has been compose()d. Please create a new one."); 64 // Verify that the binding exists. Throws an exception if not. 65 injector.getBinding(service); 66 67 services.add(service); 68 return this; 69 } 70 71 public Service compose() { 72 Preconditions.checkState(!composed, 73 "Cannot reuse a CompositeService after it has been compose()d. Please create a new one."); 74 composed = true; 75 76 // Defensive copy. 77 final List<Key<? extends Service>> services = ImmutableList.copyOf(this.services); 78 79 return new Service() { 80 public Future<State> start() { 81 final List<Future<State>> tasks = Lists.newArrayList(); 82 for (Key<? extends Service> service : services) { 83 tasks.add(injector.getInstance(service).start()); 84 } 85 86 return futureGet(tasks, State.STARTED); 87 } 88 89 public Future<State> stop() { 90 final List<Future<State>> tasks = Lists.newArrayList(); 91 for (Key<? extends Service> service : services) { 92 tasks.add(injector.getInstance(service).stop()); 93 } 94 95 return futureGet(tasks, State.STOPPED); 96 } 97 98 public State state() { 99 return compositeState; 100 } 101 }; 102 } 103 104 private FutureTask<Service.State> futureGet(final List<Future<Service.State>> tasks, 105 final Service.State state) { 106 return new FutureTask<Service.State>(new Callable<Service.State>() { 107 public Service.State call() { 108 boolean ok = true; 109 for (Future<Service.State> task : tasks) { 110 try { 111 ok = state == task.get(); 112 } catch (InterruptedException e) { 113 return compositeState = Service.State.FAILED; 114 } catch (ExecutionException e) { 115 return compositeState = Service.State.FAILED; 116 } 117 } 118 119 return compositeState = ok ? state : Service.State.FAILED; 120 } 121 }); 122 } 123} 124