Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 4a4f2f6b583e387c9301e9536a9a5888a71749d2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
package org.eclipse.jetty.util.component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;

/**
 * An AggregateLifeCycle is an {@link LifeCycle} implementation for a collection of contained beans.
 * <p>
 * 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.  
 * An unmanaged bean is associated with the aggregate for the purposes of {@link #dump()}, but it's lifecycle must be managed externally.
 * <p>
 * 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.  
 * Otherwise the methods {@link #addBean(Object, boolean)}, {@link #manage(Object)} and {@link #unmanage(Object)} can be used to 
 * explicitly control the life cycle relationship.
 * <p>
 * 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 
 * the API must be used to explicitly set it as unmanaged.
 * <p>
 */
public class AggregateLifeCycle extends AbstractLifeCycle implements Destroyable, Dumpable
{
    private static final Logger LOG = Log.getLogger(AggregateLifeCycle.class);
    private final List<Bean> _beans=new CopyOnWriteArrayList<Bean>();
    private boolean _started=false;

    private class Bean
    {
        Bean(Object b) 
        {
            _bean=b;
        }
        final Object _bean;
        volatile boolean _managed=true;
        
        public String toString()
        {
            return "{"+_bean+","+_managed+"}";
        }
    }

    /* ------------------------------------------------------------ */
    /**
     * Start the managed lifecycle beans in the order they were added.
     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
     */
    @Override
    protected void doStart() throws Exception
    {
        for (Bean b:_beans)
        {
            if (b._managed && b._bean instanceof LifeCycle)
            {
                LifeCycle l=(LifeCycle)b._bean;
                if (!l.isRunning())
                    l.start();
            }
        }
        // indicate that we are started, so that addBean will start other beans added.
        _started=true;
        super.doStart();
    }
    
    /* ------------------------------------------------------------ */
    /**
     * Stop the joined lifecycle beans in the reverse order they were added.
     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
     */
    @Override
    protected void doStop() throws Exception
    {
        _started=false;
        super.doStop();
        List<Bean> reverse = new ArrayList<Bean>(_beans);
        Collections.reverse(reverse);
        for (Bean b:reverse)
        {
            if (b._managed && b._bean instanceof LifeCycle)
            {
                LifeCycle l=(LifeCycle)b._bean;
                if (l.isRunning())
                    l.stop();
            }
        }
    }


    /* ------------------------------------------------------------ */
    /**
     * Destroy the joined Destroyable beans in the reverse order they were added.
     * @see org.eclipse.jetty.util.component.Destroyable#destroy()
     */
    public void destroy()
    {
        List<Bean> reverse = new ArrayList<Bean>(_beans);
        Collections.reverse(reverse);
        for (Bean b:reverse)
        {
            if (b._bean instanceof Destroyable && b._managed)
            {
                Destroyable d=(Destroyable)b._bean;
                d.destroy();
            }
        }
        _beans.clear();
    }


    /* ------------------------------------------------------------ */
    /** Is the bean contained in the aggregate.
     * @param bean
     * @return True if the aggregate contains the bean
     */
    public boolean contains(Object bean)
    {
        for (Bean b:_beans)
            if (b._bean==bean)
                return true;
        return false;
    }
    
    /* ------------------------------------------------------------ */
    /** Is the bean joined to the aggregate.
     * @param bean
     * @return True if the aggregate contains the bean and it is joined
     */
    public boolean isManaged(Object bean)
    {
        for (Bean b:_beans)
            if (b._bean==bean)
                return b._managed;
        return false;
    }
    
    /* ------------------------------------------------------------ */
    /**
     * Add an associated bean.
     * If the bean is a {@link LifeCycle}, then it will be managed if it is not 
     * already started and umanaged if it is already started. The {@link #addBean(Object, boolean)}
     * method should be used if this is not correct, or the {@link #manage(Object)} and {@link #unmanage(Object)}
     * methods may be used after an add to change the status.
     * @param o the bean object to add
     * @return true if the bean was added or false if it has already been added.
     */
    public boolean addBean(Object o)
    {
        // beans are joined unless they are started lifecycles
        return addBean(o,!((o instanceof LifeCycle)&&((LifeCycle)o).isStarted()));
    }
    
    /* ------------------------------------------------------------ */
    /** Add an associated lifecycle.
     * @param o The lifecycle to add
     * @param managed True if the LifeCycle is to be joined, otherwise it will be disjoint.
     * @return true if bean was added, false if already present.
     */
    public boolean addBean(Object o, boolean managed)
    {
        if (contains(o))
            return false;
        
        Bean b = new Bean(o);
        b._managed=managed;
        _beans.add(b);
        
        if (o instanceof LifeCycle)
        {
            LifeCycle l=(LifeCycle)o;

            // Start the bean if we are started
            if (managed && _started)
            {
                try
                {
                    l.start();
                }
                catch(Exception e)
                {
                    throw new RuntimeException (e);
                }
            }
        }
        return true;
    }
    
    /* ------------------------------------------------------------ */
    /**
     * Manage a bean by this aggregate, so that it is started/stopped/destroyed with the 
     * aggregate lifecycle.  
     * @param bean The bean to manage (must already have been added).
     */
    public void manage(Object bean)
    {    
        for (Bean b :_beans)
        {
            if (b._bean==bean)
            {
                b._managed=true;
                return;
            }
        }
        throw new IllegalArgumentException();
    }

    /* ------------------------------------------------------------ */
    /**
     * Unmanage a bean by this aggregate, so that it is not started/stopped/destroyed with the 
     * aggregate lifecycle.  
     * @param bean The bean to manage (must already have been added).
     */
    public void unmanage(Object bean)
    {
        for (Bean b :_beans)
        {
            if (b._bean==bean)
            {
                b._managed=false;
                return;
            }
        }
        throw new IllegalArgumentException();
    }
    
    /* ------------------------------------------------------------ */
    /** Get dependent beans 
     * @return List of beans.
     */
    public Collection<Object> getBeans()
    {
        return getBeans(Object.class);
    }
    
    /* ------------------------------------------------------------ */
    /** Get dependent beans of a specific class
     * @see #addBean(Object)
     * @param clazz
     * @return List of beans.
     */
    public <T> List<T> getBeans(Class<T> clazz)
    {
        ArrayList<T> beans = new ArrayList<T>();
        for (Bean b:_beans)
        {
            if (clazz.isInstance(b._bean))
                beans.add((T)(b._bean));
        }
        return beans;
    }

    
    /* ------------------------------------------------------------ */
    /** Get dependent beans of a specific class.
     * If more than one bean of the type exist, the first is returned.
     * @see #addBean(Object)
     * @param clazz
     * @return bean or null
     */
    public <T> T getBean(Class<T> clazz)
    {
        for (Bean b:_beans)
        {
            if (clazz.isInstance(b._bean))
                return (T)b._bean;
        }
        
        return null;
    }
    
    /* ------------------------------------------------------------ */
    /**
     * Remove all associated bean.
     */
    public void removeBeans ()
    {
        _beans.clear();
    }

    /* ------------------------------------------------------------ */
    /**
     * Remove an associated bean.
     */
    public boolean removeBean (Object o)
    {
        Iterator<Bean> i = _beans.iterator();
        while(i.hasNext())
        {
            Bean b=i.next();
            if (b._bean==o)
            {
                _beans.remove(b);
                return true;
            }
        }
        return false;
    }

    /* ------------------------------------------------------------ */
    public void dumpStdErr()
    {
        try
        {
            dump(System.err,"");
        }
        catch (IOException e)
        {
            LOG.warn(e);
        }
    }
    
    /* ------------------------------------------------------------ */
    public String dump()
    {
        return dump(this);
    }    
    
    /* ------------------------------------------------------------ */
    public static String dump(Dumpable dumpable)
    {
        StringBuilder b = new StringBuilder();
        try
        {
            dumpable.dump(b,"");
        }
        catch (IOException e)
        {
            LOG.warn(e);
        }
        return b.toString();
    }    

    /* ------------------------------------------------------------ */
    public void dump(Appendable out) throws IOException
    {
        dump(out,"");
    }

    /* ------------------------------------------------------------ */
    protected void dumpThis(Appendable out) throws IOException
    {
        out.append(String.valueOf(this)).append(" - ").append(getState()).append("\n");
    }

    /* ------------------------------------------------------------ */
    public static void dumpObject(Appendable out,Object o) throws IOException
    {
        try
        {
            if (o instanceof LifeCycle)
                out.append(String.valueOf(o)).append(" - ").append((AbstractLifeCycle.getState((LifeCycle)o))).append("\n");
            else
                out.append(String.valueOf(o)).append("\n");
        }
        catch(Throwable th)
        {
            out.append(" => ").append(th.toString()).append('\n');
        }
    }
    
    /* ------------------------------------------------------------ */
    public void dump(Appendable out,String indent) throws IOException
    {
        dumpThis(out);
        int size=_beans.size();
        if (size==0)
            return;
        int i=0;
        for (Bean b : _beans)
        {
            i++;

            out.append(indent).append(" +- ");
            if (b._managed)
            {
                if (b._bean instanceof Dumpable)
                    ((Dumpable)b._bean).dump(out,indent+(i==size?"    ":" |  "));
                else 
                    dumpObject(out,b._bean);
            }
            else 
                dumpObject(out,b._bean);
        }

        if (i!=size)
            out.append(indent).append(" |\n");
    }
    
    /* ------------------------------------------------------------ */
    public static void dump(Appendable out,String indent,Collection<?>... collections) throws IOException
    {
        if (collections.length==0)
            return;
        int size=0;
        for (Collection<?> c : collections)
            size+=c.size();    
        if (size==0)
            return;

        int i=0;
        for (Collection<?> c : collections)
        {
            for (Object o : c)
            {
                i++;
                out.append(indent).append(" +- ");

                if (o instanceof Dumpable)
                    ((Dumpable)o).dump(out,indent+(i==size?"    ":" |  "));
                else 
                    dumpObject(out,o);
            }
            
            if (i!=size)
                out.append(indent).append(" |\n");          
        }
    }
}

Back to the top