KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > avalon > fortress > util > LifecycleExtensionManager


1 /*
2  * Copyright 2003-2004 The Apache Software Foundation
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12  * implied.
13  *
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.avalon.fortress.util;
19
20 import org.apache.avalon.framework.context.Context;
21 import org.apache.avalon.framework.logger.AbstractLogEnabled;
22 import org.apache.avalon.lifecycle.Accessor;
23 import org.apache.avalon.lifecycle.Creator;
24
25 import java.util.ArrayList JavaDoc;
26 import java.util.Collections JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.List JavaDoc;
29
30 /**
31  * <code>LifecycleExtensionManager</code> class. This class manages a list
32  * of extensions objects that are executed on components during the various
33  * stages of their lifecycles.
34  *
35  * <p>
36  * It provides methods for adding extension objects to the system,
37  * and a method for executing them on a particular component object. The
38  * current context is also passed in to the extension objects to facilitate
39  * the communication of any global values.
40  * </p>
41  *
42  * <p>
43  * Extensions are stored internally in a list. This guarentees that the
44  * order in which they are executed matches the order in which they are
45  * inserted.
46  * </p>
47  *
48  * @author <a HREF="mailto:dev@avalon.apache.org">Avalon Development Team</a>
49  * @version CVS $Revision: 1.12 $ $Date: 2004/02/28 15:16:26 $
50  */

51 public final class LifecycleExtensionManager
52         extends AbstractLogEnabled
53 {
54     public static final String JavaDoc ROLE = LifecycleExtensionManager.class.getName();
55
56     // extensions objects
57
private final CachedArrayList m_accessorExtensions = new CachedArrayList();
58     private final CachedArrayList m_creatorExtensions = new CachedArrayList();
59     private boolean m_readOnly = false;
60
61     /**
62      * Make the extension manager read only (immutable).
63      */

64     public void makeReadOnly()
65     {
66         m_readOnly = true;
67     }
68
69     /**
70      * Create a copy; it will be writeable even if the original was not.
71      */

72     public LifecycleExtensionManager writeableCopy()
73     {
74         final LifecycleExtensionManager copy = new LifecycleExtensionManager();
75         copy.m_accessorExtensions.copyFrom( m_accessorExtensions );
76         copy.m_creatorExtensions.copyFrom( m_creatorExtensions );
77
78         return copy;
79     }
80
81     /**
82      * <code>executeAccessExtensions</code> method, executes all access
83      * level extensions on the given component.
84      *
85      * @param component a <code>Component</code> instance
86      * @param context a <code>Context</code> instance
87      * @exception Exception if an error occurs
88      */

89     public void executeAccessExtensions( final Object JavaDoc component, final Context context )
90             throws Exception JavaDoc
91     {
92         executeExtensions( m_accessorExtensions.toArray(), component, context, ACCESS );
93     }
94
95     /**
96      * <code>executeReleaseExtensions</code> method, executes all release
97      * level extensions on the given component.
98      *
99      * @param component a <code>Component</code> instance
100      * @param context a <code>Context</code> instance
101      * @exception Exception if an error occurs
102      */

103     public void executeReleaseExtensions( final Object JavaDoc component, final Context context )
104             throws Exception JavaDoc
105     {
106         executeExtensions( m_accessorExtensions.toArray(), component, context, RELEASE );
107     }
108
109     /**
110      * <code>executeCreationExtensions</code> method, executes all creation
111      * level extensions on the given component.
112      *
113      * @param component a <code>Component</code> instance
114      * @param context a <code>Context</code> instance
115      * @exception Exception if an error occurs
116      */

117     public void executeCreationExtensions( final Object JavaDoc component, final Context context )
118             throws Exception JavaDoc
119     {
120         executeExtensions( m_creatorExtensions.toArray(), component, context, CREATE );
121     }
122
123     /**
124      * <code>executeDestructionExtensions</code> method, executes all
125      * destruction level extensions on the given component.
126      *
127      * @param component a <code>Component</code> instance
128      * @param context a <code>Context</code> instance
129      * @exception Exception if an error occurs
130      */

131     public void executeDestructionExtensions( final Object JavaDoc component, final Context context )
132             throws Exception JavaDoc
133     {
134         executeExtensions( m_creatorExtensions.toArray(), component, context, DESTROY );
135     }
136
137     // The following methods define operations that modify the internal list
138
// of extensions. I've refrained from returning the List directly, via a
139
// getExtensions() method for the following reasons:
140
//
141
// 1. Returning List breaks encapsulation, implicitly exposing all of List's
142
// current and future operations to the client
143
// 2. List operates with type Object, not LifecycleExtension which means we need
144
// more error handling code if we make it possible for the user to add instances
145
// of any type to the extension lists.
146
// 3. Wrapping add/remove methods allow us to add optimizations to improve performance
147
// (eg. to convert the List to an array upon each add/remove, and not upon each
148
// execute operation)
149
// 4. The book 'Refactoring' says we shouldn't do it :-)
150
//
151
// I'm open to suggestions though if there's any better ideas ?
152

153     /**
154      * Adds an accessor extension to the manager
155      *
156      * @param extension a <code>Accessor</code> instance
157      */

158     public void addAccessorExtension( final Accessor extension )
159     {
160         checkWriteable();
161         m_accessorExtensions.add( extension );
162     }
163
164     /**
165      * Adds a creator extension to the manager
166      *
167      * @param extension a <code>Creator</code> instance
168      */

169     public void addCreatorExtension( final Creator extension )
170     {
171         checkWriteable();
172         m_creatorExtensions.add( extension );
173     }
174
175     /**
176      * Inserts an accessor extension at a given index in the manager
177      *
178      * @param position an <code>int</code> index value
179      * @param extension a <code>Accessor</code> instance
180      */

181     public void insertAccessorExtension( final int position, final Accessor extension )
182     {
183         checkWriteable();
184         m_accessorExtensions.insert( position, extension );
185     }
186
187     /**
188      * Inserts a creator extension at a given index in the manager
189      *
190      * @param position an <code>int</code> index value
191      * @param extension a <code>Creator</code> instance
192      */

193     public void insertCreatorExtension( final int position, final Creator extension )
194     {
195         checkWriteable();
196         m_creatorExtensions.insert( position, extension );
197     }
198
199     /**
200      * Removes a particular accessor extension from the manager
201      *
202      * @param position an <code>int</code> index value
203      * @return a <code>Accessor</code> instance
204      */

205     public Accessor removeAccessorExtension( final int position )
206     {
207         checkWriteable();
208         return (Accessor) m_accessorExtensions.remove( position );
209     }
210
211     /**
212      * Removes a particular creator extension from the manager
213      *
214      * @param position an <code>int</code> index value
215      * @return a <code>Creator</code> instance
216      */

217     public Creator removeCreatorExtension( final int position )
218     {
219         checkWriteable();
220         return (Creator) m_creatorExtensions.remove( position );
221     }
222
223     /**
224      * Obtain an iterator.
225      *
226      * @return an <code>Iterator</code> instance
227      */

228     public Iterator JavaDoc accessorExtensionsIterator()
229     {
230         return m_accessorExtensions.iterator();
231     }
232
233     /**
234      * Obtain an iterator.
235      *
236      * @return an <code>Iterator</code> instance
237      */

238     public Iterator JavaDoc creatorExtensionsIterator()
239     {
240         return m_creatorExtensions.iterator();
241     }
242
243     /**
244      * Find out the total number of accessor extensions registered with this manager
245      *
246      * @return an <code>int</code> value
247      */

248     public int accessorExtensionsCount()
249     {
250         return m_accessorExtensions.size();
251     }
252
253     /**
254      * Find out the total number of creator extensions registered with this manager
255      *
256      * @return an <code>int</code> value
257      */

258     public int creatorExtensionsCount()
259     {
260         return m_creatorExtensions.size();
261     }
262
263     /**
264      * Obtain the particular accessor extension at the given index
265      *
266      * @param index an <code>int</code> index value
267      * @return a <code>Accessor</code> instance
268      */

269     public Accessor getAccessorExtension( final int index )
270     {
271         return (Accessor) m_accessorExtensions.get( index );
272     }
273
274     /**
275      * Obtain the particular creator extension at the given index
276      *
277      * @param index an <code>int</code> index value
278      * @return a <code>Creator</code> instance
279      */

280     public Creator getCreatorExtension( final int index )
281     {
282         return (Creator) m_creatorExtensions.get( index );
283     }
284
285     /**
286      * Clears all accessor extensions registered with this manager
287      */

288     public void clearAccessorExtensions()
289     {
290         checkWriteable();
291         m_accessorExtensions.clear();
292     }
293
294     /**
295      * Clears all creator extensions registered with this manager
296      */

297     public void clearCreatorExtensions()
298     {
299         checkWriteable();
300         m_creatorExtensions.clear();
301     }
302
303     // Lifecycle method constants, these are passed to executeExtensions()
304
protected static final int ACCESS = 0;
305     protected static final int RELEASE = 1;
306     protected static final int CREATE = 2;
307     protected static final int DESTROY = 3;
308
309     /**
310      * <code>executeExtensions</code> method, executes a given array of
311      * lifecycle interfaces on a given component.
312      *
313      * @param component a <code>Component</code> instance
314      * @param context a <code>Context</code> instance
315      * @param type a constant, referencing which phase the
316      * extensions array adheres to
317      *
318      * @exception Exception if an error occurs
319      */

320     protected void executeExtensions( final Object JavaDoc[] extensions,
321                                       final Object JavaDoc component,
322                                       final Context context,
323                                       final int type )
324             throws Exception JavaDoc
325     {
326         switch ( type )
327         {
328             case ACCESS:
329                 for ( int i = 0; i < extensions.length; ++i )
330                 {
331                     ( (Accessor) extensions[i] ).access( component, context );
332                 }
333                 break;
334
335             case RELEASE:
336                 for ( int i = 0; i < extensions.length; ++i )
337                 {
338                     ( (Accessor) extensions[i] ).release( component, context );
339                 }
340                 break;
341
342             case CREATE:
343                 for ( int i = 0; i < extensions.length; ++i )
344                 {
345                     ( (Creator) extensions[i] ).create( component, context );
346                 }
347                 break;
348
349             case DESTROY:
350                 for ( int i = 0; i < extensions.length; ++i )
351                 {
352                     ( (Creator) extensions[i] ).destroy( component, context );
353                 }
354                 break;
355
356             default:
357                 if ( getLogger().isErrorEnabled() )
358                 {
359                     final String JavaDoc message =
360                             "Incorrect extension phase specified: " + type;
361                     getLogger().error( message );
362                 }
363         }
364     }
365
366     /**
367      * Utility method to check if LifecycleExtensionsManager
368      * is writeable and if not throw exception.
369      *
370      * @throws IllegalStateException if context is read only
371      */

372     protected final void checkWriteable()
373             throws IllegalStateException JavaDoc
374     {
375         if ( m_readOnly )
376         {
377             final String JavaDoc message =
378                     "LifecycleExtensionsManager is read only and can not be modified";
379             throw new IllegalStateException JavaDoc( message );
380         }
381     }
382
383     /**
384      * <code>CachedArrayList</code> class.
385      *
386      * <p>
387      * This class wraps a synchronized ArrayList to provide an optimized
388      * <code>toArray()</code> method that returns an internally cached array,
389      * rather than a new array generated per <code>toArray()</code>
390      * invocation.
391      * </p>
392      *
393      * <p>
394      * Use of the class by the Manager results in <code>toArray()</code>
395      * being invoked far more often than any other method. Caching the value
396      * <code>toArray</code> normally returns is intended to be a performance
397      * optimization.
398      * </p>
399      *
400      * <p>
401      * The cached array value is updated upon each write operation to the
402      * List.
403      * </p>
404      *
405      * <p>
406      * REVISIT(MC): investigate using FastArrayList from collections ?
407      * </p>
408      */

409     private final class CachedArrayList
410     {
411         // Empty array constant
412
private final Object JavaDoc[] EMPTY_ARRAY = new Object JavaDoc[0];
413
414         // Actual list for storing elements
415
private final List JavaDoc m_proxy = Collections.synchronizedList( new ArrayList JavaDoc() );
416
417         // Proxy cache, saves unnecessary conversions from List to Array
418
private Object JavaDoc[] m_cache = EMPTY_ARRAY;
419
420         /**
421          * Become a copy of another CachedArrayList.
422          */

423         public void copyFrom( final CachedArrayList original )
424         {
425             m_proxy.clear();
426             m_proxy.addAll( original.m_proxy );
427             m_cache = original.m_cache; // it won't mutate anyway :-)
428
}
429
430         /**
431          * Add an object to the list
432          *
433          * @param object an <code>Object</code> value
434          */

435         public void add( final Object JavaDoc object )
436         {
437             m_proxy.add( object );
438             m_cache = m_proxy.toArray();
439         }
440
441         /**
442          * Insert an object into a particular position in the list
443          *
444          * @param position an <code>int</code> value
445          * @param object an <code>Object</code> value
446          */

447         public void insert( final int position, final Object JavaDoc object )
448         {
449             m_proxy.add( position, object );
450             m_cache = m_proxy.toArray();
451         }
452
453         /**
454          * Remove an object from the list
455          *
456          * @param position an <code>int</code> value
457          * @return a <code>Object</code> value
458          */

459         public Object JavaDoc remove( final int position )
460         {
461             final Object JavaDoc object = m_proxy.remove( position );
462             m_cache = m_proxy.toArray();
463             return object;
464         }
465
466         /**
467          * Obtain an iterator. This iterator is read-only.
468          *
469          * @return an <code>Iterator</code> value
470          */

471         public Iterator JavaDoc iterator()
472         {
473             final Iterator JavaDoc base = m_proxy.iterator();
474             return new UnmodifiableIterator( base );
475         }
476
477         /**
478          * Obtain the size of the list
479          *
480          * @return an <code>int</code> value
481          */

482         public int size()
483         {
484             return m_proxy.size();
485         }
486
487         /**
488          * Access an object that is in the list
489          *
490          * @param index an <code>int</code> value
491          * @return a <code>Object</code> value
492          */

493         public Object JavaDoc get( final int index )
494         {
495             return m_proxy.get( index );
496         }
497
498         /**
499          * Find out the index of an object in the list
500          *
501          * @param object an <code>Object</code> value
502          * @return an <code>int</code> value
503          */

504         public int indexOf( final Object JavaDoc object )
505         {
506             return m_proxy.indexOf( object );
507         }
508
509         /**
510          * Clear the list
511          */

512         public void clear()
513         {
514             m_proxy.clear();
515             m_cache = EMPTY_ARRAY;
516         }
517
518         /**
519          * Obtain the list as an array. Subsequents calls to this method
520          * will return the same array object, until a write operation is
521          * performed on the list.
522          *
523          * @return an <code>Object[]</code> value
524          */

525         public Object JavaDoc[] toArray()
526         {
527             return m_cache;
528         }
529     }
530
531     /**
532      * Read only iterator.
533      */

534     private static class UnmodifiableIterator implements Iterator JavaDoc
535     {
536         private final Iterator JavaDoc m_base;
537
538         UnmodifiableIterator( final Iterator JavaDoc base )
539         {
540             if ( base == null ) throw new NullPointerException JavaDoc( "base can not be null" );
541             m_base = base;
542         }
543
544         public boolean hasNext()
545         {
546             return m_base.hasNext();
547         }
548
549         public Object JavaDoc next()
550         {
551             return m_base.next();
552         }
553
554         public void remove()
555         {
556             throw new UnsupportedOperationException JavaDoc( "Unmodifiable iterator" );
557         }
558     }
559 }
560
Popular Tags