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