1 // 2 // ======================================================================== 3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. 4 // ------------------------------------------------------------------------ 5 // All rights reserved. This program and the accompanying materials 6 // are made available under the terms of the Eclipse Public License v1.0 7 // and Apache License v2.0 which accompanies this distribution. 8 // 9 // The Eclipse Public License is available at 10 // http://www.eclipse.org/legal/epl-v10.html 11 // 12 // The Apache License v2.0 is available at 13 // http://www.opensource.org/licenses/apache2.0.php 14 // 15 // You may elect to redistribute this code under either of these licenses. 16 // ======================================================================== 17 // 18 19 package org.eclipse.jetty.util.component; 20 21 import java.io.IOException; 22 import java.util.ArrayList; 23 import java.util.Collection; 24 import java.util.Collections; 25 import java.util.Iterator; 26 import java.util.List; 27 import java.util.concurrent.CopyOnWriteArrayList; 28 29 import org.eclipse.jetty.util.log.Log; 30 import org.eclipse.jetty.util.log.Logger; 31 32 /** 33 * An AggregateLifeCycle is an {@link LifeCycle} implementation for a collection of contained beans. 34 * <p> 35 * Beans can be added the AggregateLifeCycle either as managed beans or as unmanaged beans. A managed bean is started, stopped and destroyed with the aggregate. 36 * An unmanaged bean is associated with the aggregate for the purposes of {@link #dump()}, but it's lifecycle must be managed externally. 37 * <p> 38 * When a bean is added, if it is a {@link LifeCycle} and it is already started, then it is assumed to be an unmanaged bean. 39 * Otherwise the methods {@link #addBean(Object, boolean)}, {@link #manage(Object)} and {@link #unmanage(Object)} can be used to 40 * explicitly control the life cycle relationship. 41 * <p> 42 * If adding a bean that is shared between multiple {@link AggregateLifeCycle} instances, then it should be started before being added, so it is unmanaged, or 43 * the API must be used to explicitly set it as unmanaged. 44 * <p> 45 */ 46 public class AggregateLifeCycle extends AbstractLifeCycle implements Destroyable, Dumpable 47 { 48 private static final Logger LOG = Log.getLogger(AggregateLifeCycle.class); 49 private final List<Bean> _beans=new CopyOnWriteArrayList<Bean>(); 50 private boolean _started=false; 51 52 private class Bean 53 { Bean(Object b)54 Bean(Object b) 55 { 56 _bean=b; 57 } 58 final Object _bean; 59 volatile boolean _managed=true; 60 toString()61 public String toString() 62 { 63 return "{"+_bean+","+_managed+"}"; 64 } 65 } 66 67 /* ------------------------------------------------------------ */ 68 /** 69 * Start the managed lifecycle beans in the order they were added. 70 * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart() 71 */ 72 @Override doStart()73 protected void doStart() throws Exception 74 { 75 for (Bean b:_beans) 76 { 77 if (b._managed && b._bean instanceof LifeCycle) 78 { 79 LifeCycle l=(LifeCycle)b._bean; 80 if (!l.isRunning()) 81 l.start(); 82 } 83 } 84 // indicate that we are started, so that addBean will start other beans added. 85 _started=true; 86 super.doStart(); 87 } 88 89 /* ------------------------------------------------------------ */ 90 /** 91 * Stop the joined lifecycle beans in the reverse order they were added. 92 * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart() 93 */ 94 @Override doStop()95 protected void doStop() throws Exception 96 { 97 _started=false; 98 super.doStop(); 99 List<Bean> reverse = new ArrayList<Bean>(_beans); 100 Collections.reverse(reverse); 101 for (Bean b:reverse) 102 { 103 if (b._managed && b._bean instanceof LifeCycle) 104 { 105 LifeCycle l=(LifeCycle)b._bean; 106 if (l.isRunning()) 107 l.stop(); 108 } 109 } 110 } 111 112 113 /* ------------------------------------------------------------ */ 114 /** 115 * Destroy the joined Destroyable beans in the reverse order they were added. 116 * @see org.eclipse.jetty.util.component.Destroyable#destroy() 117 */ destroy()118 public void destroy() 119 { 120 List<Bean> reverse = new ArrayList<Bean>(_beans); 121 Collections.reverse(reverse); 122 for (Bean b:reverse) 123 { 124 if (b._bean instanceof Destroyable && b._managed) 125 { 126 Destroyable d=(Destroyable)b._bean; 127 d.destroy(); 128 } 129 } 130 _beans.clear(); 131 } 132 133 134 /* ------------------------------------------------------------ */ 135 /** Is the bean contained in the aggregate. 136 * @param bean 137 * @return True if the aggregate contains the bean 138 */ contains(Object bean)139 public boolean contains(Object bean) 140 { 141 for (Bean b:_beans) 142 if (b._bean==bean) 143 return true; 144 return false; 145 } 146 147 /* ------------------------------------------------------------ */ 148 /** Is the bean joined to the aggregate. 149 * @param bean 150 * @return True if the aggregate contains the bean and it is joined 151 */ isManaged(Object bean)152 public boolean isManaged(Object bean) 153 { 154 for (Bean b:_beans) 155 if (b._bean==bean) 156 return b._managed; 157 return false; 158 } 159 160 /* ------------------------------------------------------------ */ 161 /** 162 * Add an associated bean. 163 * If the bean is a {@link LifeCycle}, then it will be managed if it is not 164 * already started and umanaged if it is already started. The {@link #addBean(Object, boolean)} 165 * method should be used if this is not correct, or the {@link #manage(Object)} and {@link #unmanage(Object)} 166 * methods may be used after an add to change the status. 167 * @param o the bean object to add 168 * @return true if the bean was added or false if it has already been added. 169 */ addBean(Object o)170 public boolean addBean(Object o) 171 { 172 // beans are joined unless they are started lifecycles 173 return addBean(o,!((o instanceof LifeCycle)&&((LifeCycle)o).isStarted())); 174 } 175 176 /* ------------------------------------------------------------ */ 177 /** Add an associated lifecycle. 178 * @param o The lifecycle to add 179 * @param managed True if the LifeCycle is to be joined, otherwise it will be disjoint. 180 * @return true if bean was added, false if already present. 181 */ addBean(Object o, boolean managed)182 public boolean addBean(Object o, boolean managed) 183 { 184 if (contains(o)) 185 return false; 186 187 Bean b = new Bean(o); 188 b._managed=managed; 189 _beans.add(b); 190 191 if (o instanceof LifeCycle) 192 { 193 LifeCycle l=(LifeCycle)o; 194 195 // Start the bean if we are started 196 if (managed && _started) 197 { 198 try 199 { 200 l.start(); 201 } 202 catch(Exception e) 203 { 204 throw new RuntimeException (e); 205 } 206 } 207 } 208 return true; 209 } 210 211 /* ------------------------------------------------------------ */ 212 /** 213 * Manage a bean by this aggregate, so that it is started/stopped/destroyed with the 214 * aggregate lifecycle. 215 * @param bean The bean to manage (must already have been added). 216 */ manage(Object bean)217 public void manage(Object bean) 218 { 219 for (Bean b :_beans) 220 { 221 if (b._bean==bean) 222 { 223 b._managed=true; 224 return; 225 } 226 } 227 throw new IllegalArgumentException(); 228 } 229 230 /* ------------------------------------------------------------ */ 231 /** 232 * Unmanage a bean by this aggregate, so that it is not started/stopped/destroyed with the 233 * aggregate lifecycle. 234 * @param bean The bean to manage (must already have been added). 235 */ unmanage(Object bean)236 public void unmanage(Object bean) 237 { 238 for (Bean b :_beans) 239 { 240 if (b._bean==bean) 241 { 242 b._managed=false; 243 return; 244 } 245 } 246 throw new IllegalArgumentException(); 247 } 248 249 /* ------------------------------------------------------------ */ 250 /** Get dependent beans 251 * @return List of beans. 252 */ getBeans()253 public Collection<Object> getBeans() 254 { 255 return getBeans(Object.class); 256 } 257 258 /* ------------------------------------------------------------ */ 259 /** Get dependent beans of a specific class 260 * @see #addBean(Object) 261 * @param clazz 262 * @return List of beans. 263 */ getBeans(Class<T> clazz)264 public <T> List<T> getBeans(Class<T> clazz) 265 { 266 ArrayList<T> beans = new ArrayList<T>(); 267 for (Bean b:_beans) 268 { 269 if (clazz.isInstance(b._bean)) 270 beans.add((T)(b._bean)); 271 } 272 return beans; 273 } 274 275 276 /* ------------------------------------------------------------ */ 277 /** Get dependent beans of a specific class. 278 * If more than one bean of the type exist, the first is returned. 279 * @see #addBean(Object) 280 * @param clazz 281 * @return bean or null 282 */ getBean(Class<T> clazz)283 public <T> T getBean(Class<T> clazz) 284 { 285 for (Bean b:_beans) 286 { 287 if (clazz.isInstance(b._bean)) 288 return (T)b._bean; 289 } 290 291 return null; 292 } 293 294 /* ------------------------------------------------------------ */ 295 /** 296 * Remove all associated bean. 297 */ removeBeans()298 public void removeBeans () 299 { 300 _beans.clear(); 301 } 302 303 /* ------------------------------------------------------------ */ 304 /** 305 * Remove an associated bean. 306 */ removeBean(Object o)307 public boolean removeBean (Object o) 308 { 309 Iterator<Bean> i = _beans.iterator(); 310 while(i.hasNext()) 311 { 312 Bean b=i.next(); 313 if (b._bean==o) 314 { 315 _beans.remove(b); 316 return true; 317 } 318 } 319 return false; 320 } 321 322 /* ------------------------------------------------------------ */ dumpStdErr()323 public void dumpStdErr() 324 { 325 try 326 { 327 dump(System.err,""); 328 } 329 catch (IOException e) 330 { 331 LOG.warn(e); 332 } 333 } 334 335 /* ------------------------------------------------------------ */ dump()336 public String dump() 337 { 338 return dump(this); 339 } 340 341 /* ------------------------------------------------------------ */ dump(Dumpable dumpable)342 public static String dump(Dumpable dumpable) 343 { 344 StringBuilder b = new StringBuilder(); 345 try 346 { 347 dumpable.dump(b,""); 348 } 349 catch (IOException e) 350 { 351 LOG.warn(e); 352 } 353 return b.toString(); 354 } 355 356 /* ------------------------------------------------------------ */ dump(Appendable out)357 public void dump(Appendable out) throws IOException 358 { 359 dump(out,""); 360 } 361 362 /* ------------------------------------------------------------ */ dumpThis(Appendable out)363 protected void dumpThis(Appendable out) throws IOException 364 { 365 out.append(String.valueOf(this)).append(" - ").append(getState()).append("\n"); 366 } 367 368 /* ------------------------------------------------------------ */ dumpObject(Appendable out,Object o)369 public static void dumpObject(Appendable out,Object o) throws IOException 370 { 371 try 372 { 373 if (o instanceof LifeCycle) 374 out.append(String.valueOf(o)).append(" - ").append((AbstractLifeCycle.getState((LifeCycle)o))).append("\n"); 375 else 376 out.append(String.valueOf(o)).append("\n"); 377 } 378 catch(Throwable th) 379 { 380 out.append(" => ").append(th.toString()).append('\n'); 381 } 382 } 383 384 /* ------------------------------------------------------------ */ dump(Appendable out,String indent)385 public void dump(Appendable out,String indent) throws IOException 386 { 387 dumpThis(out); 388 int size=_beans.size(); 389 if (size==0) 390 return; 391 int i=0; 392 for (Bean b : _beans) 393 { 394 i++; 395 396 out.append(indent).append(" +- "); 397 if (b._managed) 398 { 399 if (b._bean instanceof Dumpable) 400 ((Dumpable)b._bean).dump(out,indent+(i==size?" ":" | ")); 401 else 402 dumpObject(out,b._bean); 403 } 404 else 405 dumpObject(out,b._bean); 406 } 407 408 if (i!=size) 409 out.append(indent).append(" |\n"); 410 } 411 412 /* ------------------------------------------------------------ */ dump(Appendable out,String indent,Collection<?>... collections)413 public static void dump(Appendable out,String indent,Collection<?>... collections) throws IOException 414 { 415 if (collections.length==0) 416 return; 417 int size=0; 418 for (Collection<?> c : collections) 419 size+=c.size(); 420 if (size==0) 421 return; 422 423 int i=0; 424 for (Collection<?> c : collections) 425 { 426 for (Object o : c) 427 { 428 i++; 429 out.append(indent).append(" +- "); 430 431 if (o instanceof Dumpable) 432 ((Dumpable)o).dump(out,indent+(i==size?" ":" | ")); 433 else 434 dumpObject(out,o); 435 } 436 437 if (i!=size) 438 out.append(indent).append(" |\n"); 439 } 440 } 441 } 442