KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ungoverned > moduleloader > ModuleManager


1 /*
2  * ModuleLoader - A generic, policy-driven class loader.
3  * Copyright (c) 2004, Richard S. Hall
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in
14  * the documentation and/or other materials provided with the
15  * distribution.
16  * * Neither the name of the ungoverned.org nor the names of its
17  * contributors may be used to endorse or promote products derived
18  * from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * Contact: Richard S. Hall (heavy@ungoverned.org)
33  * Contributor(s):
34  *
35 **/

36 package org.ungoverned.moduleloader;
37
38 import java.util.*;
39
40 /**
41  * <p>
42  * The <tt>ModuleManager</tt> class is the core facility for defining a
43  * re-usable, policy-driven class loader for applications that require
44  * flexible class loading mechanisms. The <tt>ModuleManager</tt> is not
45  * class loader itself, but it supports the concept of a
46  * <a HREF="Module.html"><tt>Module</tt></a>,
47  * which is a unit of organization for application classes and resources.
48  * The <tt>ModuleManager</tt> has only a handful of methods that allow
49  * an application to add, remove, reset, and query modules; the intent
50  * is to place as few assumptions in the <tt>ModuleManager</tt> as possible.
51  * </p>
52  * <p>
53  * The idea is simple, allow the application to map itself into modules
54  * however it sees fit and let the <tt>ModuleManager</tt> assume the
55  * responsibility of managing the modules and loading classes and resources
56  * from them as necessary via <a HREF="ModuleClassLoader.html"><tt>ModuleClassLoader</tt></a>s
57  * that are associated with each module. In order to achieve this goal, though, the
58  * <tt>ModuleManager</tt> must make at least one assumption on behalf of
59  * the application. This assumption is that the loading of classes and resources
60  * from the available modules must happen using a search algorithm
61  * that is particular to the application itself. As a result of this assumption,
62  * the <tt>ModuleManager</tt> requires that the application provide a concrete
63  * implementation of the <a HREF="SearchPolicy.html"><tt>SearchPolicy</tt></a>
64  * interface.
65  * </p>
66  * <p>
67  * The search policy allows the <tt>ModuleLoader</tt> to let applications inject
68  * their own particular class loading policies, without dictating strict or
69  * constraining base assumptions. Of course, it is likely that many applications
70  * will use the same or very similar search policies. Because of this, another
71  * goal of the <tt>ModuleLoader</tt> approach is to foster a common library of
72  * search policies that applications are free to use or customize as they see
73  * fit. These common search policies are analagous to patterns, where each search
74  * policy is viewable as a <i>class loading pattern</i>. Some initial search
75  * policies included with the <tt>ModuleLoader</tt> are
76  * <a HREF="search/ExhaustiveSearchPolicy.html"><tt>ExhaustiveSearchPolicy</tt></a>,
77  * <a HREF="search/SelfContainedSearchPolicy.html"><tt>SelfContainedSearchPolicy</tt></a>, and
78  * <a HREF="search/ImportSearchPolicy.html"><tt>ImportSearchPolicy</tt></a>.
79  * </p>
80  * <p>
81  * Due to the fact that class loaders are tied to security and resource loading,
82  * the search policy alone is not sufficient for the <tt>ModuleLoader</tt> to
83  * perform its function. To fulfill these other purposes, the <tt>ModuleLoader</tt>
84  * introduces another policy interface, called the <a HREF="URLPolicy.html"><tt>URLPolicy</tt></a>.
85  * The <tt>URLPolicy</tt> allows the application to inject its particular policy
86  * for to purposes:
87  * </p>
88  * <ol>
89  * <li>Creating the <tt>URL</tt> associated with loading a resource, such as
90  * the <tt>URL</tt> returned from a call to <tt>Class.getResource()</tt>.
91  * </li>
92  * <li>Creating the <tt>URL</tt> that will be associated with a class's
93  * <tt>CodeSource</tt> when defining the class for purposes of security
94  * and assigning permissions.
95  * </li>
96  * </ol>
97  * <p>
98  * The <tt>ModuleLoader</tt> defines a default <tt>URLPolicy</tt>, called
99  * <a HREF="DefaultURLPolicy.html"><tt>DefaultURLPolicy</tt></a>, that provides
100  * a simple <tt>URLStreamHandler</tt> for accessing resources inside of modules
101  * and that returns <tt>null</tt> for the <tt>CodeSource</tt> <tt>URL</tt>.
102  * Applications only need to supply their own <tt>URLPolicy</tt> if the default
103  * one does not provide the appropriate behavior.
104  * </p>
105  * <p>
106  * It is possible for an application to create multiple instances of the
107  * <tt>ModuleManager</tt> within a single JVM, but it is not possible to
108  * share modules across multiple <tt>ModuleManager</tt>s. A given <tt>ModuleManager</tt>
109  * can only have one <tt>SelectionPolicy</tt> and one <tt>URLPolicy</tt>.
110  * </p>
111  * @see org.ungoverned.moduleloader.Module
112  * @see org.ungoverned.moduleloader.ModuleClassLoader
113  * @see org.ungoverned.moduleloader.SearchPolicy
114  * @see org.ungoverned.moduleloader.URLPolicy
115  * @see org.ungoverned.moduleloader.DefaultURLPolicy
116 **/

117 public class ModuleManager
118 {
119     private List m_moduleList = new ArrayList();
120     private Map m_moduleMap = new HashMap();
121     private SearchPolicy m_searchPolicy = null;
122     private URLPolicy m_urlPolicy = null;
123     private ModuleListener[] m_listeners = null;
124     private static final ModuleListener[] m_noListeners = new ModuleListener[0];
125
126     /**
127      * <p>
128      * Constructs a <tt>ModuleManager</tt> instance using the specified
129      * search policy and the default <tt>URL</tt> policy.
130      * </p>
131      * @param searchPolicy the search policy that the instance should use.
132      * @see org.ungoverned.moduleloader.SearchPolicy
133     **/

134     public ModuleManager(SearchPolicy searchPolicy)
135     {
136         this(searchPolicy, null);
137     }
138
139     /**
140      * <p>
141      * Constructs a <tt>ModuleManager</tt> instance using the specified
142      * search policy and the specified <tt>URL</tt> policy.
143      * </p>
144      * @param searchPolicy the search policy that the instance should use.
145      * @param urlPolicy the <tt>URL</tt> policy that the instance should use.
146      * @see org.ungoverned.moduleloader.SearchPolicy
147      * @see org.ungoverned.moduleloader.URLPolicy
148     **/

149     public ModuleManager(SearchPolicy searchPolicy, URLPolicy urlPolicy)
150     {
151         m_listeners = m_noListeners;
152         m_searchPolicy = searchPolicy;
153         m_searchPolicy.setModuleManager(this);
154
155         if (urlPolicy == null)
156         {
157             m_urlPolicy = new DefaultURLPolicy();
158         }
159         else
160         {
161             m_urlPolicy = urlPolicy;
162         }
163     }
164
165     /**
166      * <p>
167      * Returns the <tt>URL</tt> policy used by this instance.
168      * </p>
169      * @return the <tt>URL</tt> policy used by this instance.
170      * @see org.ungoverned.moduleloader.URLPolicy
171     **/

172     public URLPolicy getURLPolicy()
173     {
174         return m_urlPolicy;
175     }
176
177     /**
178      * <p>
179      * Returns the search policy used by this instance.
180      * </p>
181      * @return the search policy used by this instance.
182      * @see org.ungoverned.moduleloader.SearchPolicy
183     **/

184     public SearchPolicy getSearchPolicy()
185     {
186         return m_searchPolicy;
187     }
188
189     /**
190      * <p>
191      * Returns an array of all modules being managed by the
192      * <tt>ModuleManager</tt> instance. The array contains a snapshot of
193      * all modules in the <tt>ModuleManager</tt> at the time when this
194      * method was called.
195      * </p>
196      * @return an array of all modules being managed by the <tt>ModuleManager</tt>
197      * instance.
198      * @see org.ungoverned.moduleloader.Module
199     **/

200     public synchronized Module[] getModules()
201     {
202         Module[] modules = new Module[m_moduleList.size()];
203         return (Module[]) m_moduleList.toArray(modules);
204     }
205
206     /**
207      * <p>
208      * Returns a module associated with the specified identifier.
209      * </p>
210      * @param id the identifier for the module to be retrieved.
211      * @return the module associated with the identifier or <tt>null</tt>.
212      * @see org.ungoverned.moduleloader.Module
213     **/

214     public synchronized Module getModule(String JavaDoc id)
215     {
216         return (Module) m_moduleMap.get(id);
217     }
218
219     /**
220      * <p>
221      * Adds a module to the module manager. The module will have the specified
222      * unique identifier, with the associated attributes, resource sources, and
223      * library sources. If the identifier is not unique, then an exception is
224      * thrown.
225      * </p>
226      * @param id the unique identifier of the new module.
227      * @param attributes an array of key-value attribute pairs to
228      * associate with the module.
229      * @param resSources an array of <tt>ResourceSource</tt>s to associate
230      * with the module.
231      * @param libSources an array of <tt>LibrarySource</tt>s to associate
232      * with the module.
233      * @return the newly created module.
234      * @throws java.lang.IllegalArgumentException if the module identifier
235      * is not unique.
236      * @see org.ungoverned.moduleloader.Module
237      * @see org.ungoverned.moduleloader.ResourceSource
238      * @see org.ungoverned.moduleloader.LibrarySource
239     **/

240     public Module addModule(String JavaDoc id, Object JavaDoc[][] attributes,
241         ResourceSource[] resSources, LibrarySource[] libSources)
242     {
243         Module module = null;
244
245         // Use a synchronized block instead of synchronizing the
246
// method, so we can fire our event outside of the block.
247
synchronized (this)
248         {
249             if (m_moduleMap.get(id) == null)
250             {
251                 module = new Module(this, id, attributes, resSources, libSources);
252                 m_moduleList.add(module);
253                 m_moduleMap.put(id, module);
254             }
255             else
256             {
257                 throw new IllegalArgumentException JavaDoc("Module ID must be unique.");
258             }
259         }
260
261         // Fire event here instead of inside synchronized block.
262
fireModuleAdded(module);
263
264         return module;
265     }
266
267     /**
268      * <p>
269      * Resets a given module. In resetting a module, the module's associated
270      * class loader is thrown away; it is the application's responsibility to
271      * determine when and how that application code stops using classes (and
272      * subsequent instances) from the class loader of the reset module.
273      * This method allows the associated elements of the module (i.e.,
274      * attributes, resource sources, and library sources) to be changed also;
275      * if these elements have not changed then they simply need to be passed
276      * back in from the existing module. This method is useful in situations
277      * where the underlying module needs to be changed at run time, such as
278      * might be necessary if a module was updated.
279      * </p>
280      * <p>
281      * The same effect could be achieved by first removing and then re-adding
282      * a module, but with one subtle different. By removing and then re-adding
283      * a module, a new module is created and, thus, all existing references
284      * become invalid. By explicitly having this method, the <tt>ModuleManager</tt>
285      * maintains the integrity of the module reference, which is more intuitive
286      * in the case where an updated module is intended to be the same module,
287      * only updated.
288      * </p>
289      * @param module the module reset.
290      * @param attributes an array of key-value attribute pairs to
291      * associate with the module.
292      * @param resSources an array of <tt>ResourceSource</tt>s to associate
293      * with the module.
294      * @param libSources an array of <tt>LibrarySource</tt>s to associate
295      * with the module.
296      * @see org.ungoverned.moduleloader.Module
297      * @see org.ungoverned.moduleloader.ResourceSource
298      * @see org.ungoverned.moduleloader.LibrarySource
299     **/

300     public void resetModule(
301         Module module, Object JavaDoc[][] attributes,
302         ResourceSource[] resSources, LibrarySource[] libSources)
303     {
304         // Use a synchronized block instead of synchronizing the
305
// method, so we can fire our event outside of the block.
306
synchronized (this)
307         {
308             module = (Module) m_moduleMap.get(module.getId());
309             if (module != null)
310             {
311                 module.reset(attributes, resSources, libSources);
312             }
313             else
314             {
315                 // Don't fire event.
316
return;
317             }
318         }
319
320         // Fire event here instead of inside synchronized block.
321
fireModuleReset(module);
322     }
323
324     /**
325      * <p>
326      * Removes the specified module from the <tt>ModuleManager</tt>. Removing
327      * a module only removed the module from the <tt>ModuleManager</tt>. It is
328      * the application's responsibility to determine when and how application code
329      * stop using classes (and subsequent instances) that were loaded from
330      * the class loader of the removed module.
331      * </p>
332      * @param module the module to remove.
333     **/

334     public void removeModule(Module module)
335     {
336         // Use a synchronized block instead of synchronizing the
337
// method, so we can fire our event outside of the block.
338
synchronized (this)
339         {
340             if (m_moduleMap.get(module.getId()) != null)
341             {
342                 // Remove from data structures.
343
m_moduleList.remove(module);
344                 m_moduleMap.remove(module.getId());
345
346                 // Dispose of the module.
347
module.dispose();
348             }
349             else
350             {
351                 // Don't fire event.
352
return;
353             }
354         }
355
356         // Fire event here instead of inside synchronized block.
357
fireModuleRemoved(module);
358     }
359
360     /**
361      * <p>
362      * Adds a listener to the <tt>ModuleManager</tt> to listen for
363      * module added, reset, and removed events.
364      * </p>
365      * @param l the <tt>ModuleListener</tt> to add.
366     **/

367     public void addModuleListener(ModuleListener l)
368     {
369         // Verify listener.
370
if (l == null)
371         {
372             throw new IllegalArgumentException JavaDoc("Listener is null");
373         }
374
375         // Use the m_noListeners object as a lock.
376
synchronized (m_noListeners)
377         {
378             // If we have no listeners, then just add the new listener.
379
if (m_listeners == m_noListeners)
380             {
381                 m_listeners = new ModuleListener[] { l };
382             }
383             // Otherwise, we need to do some array copying.
384
// Notice, the old array is always valid, so if
385
// the dispatch thread is in the middle of a dispatch,
386
// then it has a reference to the old listener array
387
// and is not affected by the new value.
388
else
389             {
390                 ModuleListener[] newList = new ModuleListener[m_listeners.length + 1];
391                 System.arraycopy(m_listeners, 0, newList, 0, m_listeners.length);
392                 newList[m_listeners.length] = l;
393                 m_listeners = newList;
394             }
395         }
396     }
397
398     /**
399      * <p>
400      * Removes a listener from the <tt>ModuleManager</tt>.
401      * </p>
402      * @param l the <tt>ModuleListener</tt> to remove.
403     **/

404     public void removeModuleListener(ModuleListener l)
405     {
406         // Verify listener.
407
if (l == null)
408         {
409             throw new IllegalArgumentException JavaDoc("Listener is null");
410         }
411
412         // Use the m_noListeners object as a lock.
413
synchronized (m_noListeners)
414         {
415             // Try to find the instance in our list.
416
int idx = -1;
417             for (int i = 0; i < m_listeners.length; i++)
418             {
419                 if (m_listeners[i].equals(l))
420                 {
421                     idx = i;
422                     break;
423                 }
424             }
425
426             // If we have the instance, then remove it.
427
if (idx >= 0)
428             {
429                 // If this is the last listener, then point to empty list.
430
if (m_listeners.length == 1)
431                 {
432                     m_listeners = m_noListeners;
433                 }
434                 // Otherwise, we need to do some array copying.
435
// Notice, the old array is always valid, so if
436
// the dispatch thread is in the middle of a dispatch,
437
// then it has a reference to the old listener array
438
// and is not affected by the new value.
439
else
440                 {
441                     ModuleListener[] newList = new ModuleListener[m_listeners.length - 1];
442                     System.arraycopy(m_listeners, 0, newList, 0, idx);
443                     if (idx < newList.length)
444                     {
445                         System.arraycopy(m_listeners, idx + 1, newList, idx,
446                             newList.length - idx);
447                     }
448                     m_listeners = newList;
449                 }
450             }
451         }
452     }
453
454     /**
455      * <p>
456      * Fires an event indicating that the specified module was added
457      * to the <tt>ModuleManager</tt>.
458      * </p>
459      * @param module the module that was added.
460     **/

461     protected void fireModuleAdded(Module module)
462     {
463         // Event holder.
464
ModuleEvent event = null;
465
466         // Get a copy of the listener array, which is guaranteed
467
// to not be null.
468
ModuleListener[] listeners = m_listeners;
469
470         // Loop through listeners and fire events.
471
for (int i = 0; i < listeners.length; i++)
472         {
473             // Lazily create event.
474
if (event == null)
475             {
476                 event = new ModuleEvent(this, module);
477             }
478             listeners[i].moduleAdded(event);
479         }
480     }
481
482     /**
483      * <p>
484      * Fires an event indicating that the specified module was reset.
485      * </p>
486      * @param module the module that was reset.
487     **/

488     protected void fireModuleReset(Module module)
489     {
490         // Event holder.
491
ModuleEvent event = null;
492
493         // Get a copy of the listener array, which is guaranteed
494
// to not be null.
495
ModuleListener[] listeners = m_listeners;
496
497         // Loop through listeners and fire events.
498
for (int i = 0; i < listeners.length; i++)
499         {
500             // Lazily create event.
501
if (event == null)
502             {
503                 event = new ModuleEvent(this, module);
504             }
505             listeners[i].moduleReset(event);
506         }
507     }
508
509     /**
510      * <p>
511      * Fires an event indicating that the specified module was removed
512      * from the <tt>ModuleManager</tt>.
513      * </p>
514      * @param module the module that was removed.
515     **/

516     protected void fireModuleRemoved(Module module)
517     {
518         // Event holder.
519
ModuleEvent event = null;
520
521         // Get a copy of the listener array, which is guaranteed
522
// to not be null.
523
ModuleListener[] listeners = m_listeners;
524
525         // Loop through listeners and fire events.
526
for (int i = 0; i < listeners.length; i++)
527         {
528             // Lazily create event.
529
if (event == null)
530             {
531                 event = new ModuleEvent(this, module);
532             }
533             listeners[i].moduleRemoved(event);
534         }
535     }
536 }
Popular Tags