KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ungoverned > moduleloader > search > ImportSearchPolicy


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.search;
37
38 import java.net.URL JavaDoc;
39 import java.util.*;
40
41 import org.ungoverned.moduleloader.*;
42
43 /**
44  * <p>
45  * This class implements a <tt>ModuleLoader</tt> search policy to support
46  * modules that import and export classes and resources from/to one another.
47  * Modules import from other modules by specifying a set of import identifiers
48  * and associated version numbers. Modules export their classes and
49  * resources by specifying a set of export identifiers and associated
50  * versions. Exports for a given module are also treated as imports for that module,
51  * meaning that it is possible for a module that exports classes to not use
52  * the classes it exports, but to actually use classes that are exported from
53  * another module. This search policy requires the following meta-data
54  * attributes be attached to each module:
55  * </p>
56  * <ul>
57  * <li><tt>ImportSearchPolicy.EXPORTS_ATTR</tt> - the "<tt>exports</tt>"
58  * meta-data attribute is used to declare the module's exports,
59  * </li>
60  * <li><tt>ImportSearchPolicy.IMPORTS_ATTR</tt> - the "<tt>imports</tt>"
61  * meta-data attribute is used to declare the module's imports,
62  * </li>
63  * <li><tt>ImportSearchPolicy.PROPAGATES_ATTR</tt> - the "<tt>propagates</tt>"
64  * meta-data attribute is used to declare which imports are exposed or
65  * "propagated" to clients of the module's exports, and
66  * </li>
67  * <li><tt>ImportSearchPolicy.VALID_ATTR</tt> - the "<tt>valid</tt>"
68  * meta-data attribute signifies the current <i>validation</i> status
69  * of the module (this will be defined more fully below).
70  * </li>
71  * </ul>
72  * <p>
73  * The value of the <tt>ImportSearchPolicy.EXPORTS_ATTR</tt> attribute is
74  * an array of <tt>Object</tt> arrays, i.e., <tt>Object[][]</tt>. Each element
75  * in the array signifies a particular export that is offered by this
76  * associated module. Each element is an array triple of
77  * <tt>Object</tt>, where the index into this triple is:
78  * </p>
79  * <ul>
80  * <li><tt>ImportSearchPolicy.IDENTIFIER_IDX</tt> - the first element
81  * is the export identifier object, used to identify the
82  * export target. The export identifier does not have any special
83  * meaning to the search policy and any value is allowed. A
84  * typical identifier might be the package name of the exported classes,
85  * such as <tt>javax.servlet</tt>.
86  * </li>
87  * <li><tt>ImportSearchPolicy.VERSION_IDX</tt> - the second element
88  * is the export version number. The version number does not have
89  * any special meaning to the search policy and any value is allowed.
90  * A typical version number might be major, minor, and release number.
91  * </li>
92  * <li><tt>ImportSearchPolicy.RESOLVING_MODULE_IDX</tt> - the third element
93  * is the resolving module for this export; since exports are treated like
94  * imports, it is possible that the resolving module will not be the
95  * exporting module itself. This value is filled in automatically by the
96  * search policy and is initially <tt>null</tt>.
97  * </li>
98  * </ul>
99  * </p>
100  * <p>
101  * The value of the <tt>ImportSearchPolicy.IMPORTS_ATTR</tt> attribute is
102  * essentially the same as the <tt>ImportSearchPolicy.EXPORTS_ATTR</tt> defined
103  * above; the only difference is that the array of versioned identifiers denote
104  * import targets rather than exports.
105  * </p>
106  * <p>
107  * The value of the <tt>ImportSearchPolicy.PROPAGATES_ATTR</tt> attribute is
108  * an array of <tt>Object</tt>s, i.e., <tt>Object[]</tt>. Each element in the
109  * array is an identifier of a propagated import target from the
110  * <tt>ImportSearchPolicy.IMPORTS_ATTR</tt> attribute. Only identifiers for
111  * import targets are candidates for inclusion and the version number is
112  * unnecessary since it is assumed from the corresponding import target.
113  * </p>
114  * <p>
115  * The value of the <tt>ImportSearchPolicy.VALID_ATTR</tt> attribute is a
116  * <tt>Boolean</tt>. The value is initially set to <tt>Boolean.FALSE</tt>
117  * and indicates that the module has not yet been validated. After the module
118  * is validated, the value is set to <tt>Boolean.TRUE</tt>. The search policy
119  * automatically adds this attribute to all modules and maintains its value.
120  * </p>
121  * <p>
122  * These meta-data attributes help the search policy enforce consistency
123  * using a process called <i>validation</i>; validation ensures that classes
124  * and resources are only loaded from a module whose imports are satisfied.
125  * Therefore, a <i>valid</i> module is a module whose imports are satisfied and
126  * an <i>invalid</i> module is a module whose imports are not yet satisfied.
127  * An invalid module may be invalid for two reasons:
128  * </p>
129  * <p>
130  * <ol>
131  * <li>Its imports are not available or</li>
132  * <li>It has not yet been validated.</li>
133  * </ol>
134  * </p>
135  * <p>
136  * These two possibilities arise due to the fact that module validation
137  * is not performed until it is necessary (i.e., lazy evaluation). A module
138  * is automatically validated when an attempt is made to get classes or
139  * resources from it, although it is possible to manually validate a module.
140  * For a given module, called <tt>M</tt>, the validation process attempts to
141  * find an exporting module for every import target of <tt>M</tt>. If an
142  * exporter is not found for a specific import target, then the validation of
143  * module <tt>M</tt> fails. If an exporting module is found, then this module
144  * is also validated, if it is not already. As a result, the validation of
145  * module <tt>M</tt> depends on the validation of the transitive closure of
146  * all modules on which <tt>M</tt> depends. It is also possible for modules
147  * to exhibit dependency cycles; circular dependencies are allowed.
148  * Initially, a module's <tt>VALID_ATTR</tt> is set to <tt>Boolean.FALSE</tt>,
149  * but after the module is successfully validated, this attribute is set to
150  * <tt>Boolean.TRUE</tt>.
151  * </p>
152  * <p>
153  * Besides ensuring that every import target is resolved to an appropriate
154  * exporting module, the validation process also attempts to maintain
155  * consistency along "propagation" chains. Propagation occurs when a module
156  * imports classes that are also visible from its own exports; for example,
157  * an HTTP server implementation may import classes from <tt>javax.servlet</tt>
158  * and export classes that have methods that use the type <tt>javax.servlet.Servlet</tt>
159  * in their signatures. Monitoring these types of occurences is important
160  * to uncover import source and version conflicts when multiple sources or
161  * versions of an import target are available within one virtual machine. When
162  * a module <tt>M</tt> is validated, the propagation information of each
163  * module that resolves the imports of <tt>M</tt> is checked to ensure
164  * that they do not propagate conflicting sources of <tt>M</tt>'s
165  * imports; specifically, it is verified that all propagators of a
166  * particular import target have the same source module for that import
167  * target.
168  * </p>
169  * <p>
170  * To facilitate applicability in as many scenarios as possible, this search
171  * policy delegates some decisions via additional policy interfaces. The following
172  * two policy interfaces must be specified by the code that instantiates the
173  * <tt>ImportSearchPolicy</tt> object:
174  * </p>
175  * <ul>
176  * <li><tt>CompatibilityPolicy</tt> - this policy is used to determine
177  * whether import/export version numbers are compatible.
178  * </li>
179  * <li><tt>SelectionPolicy</tt> - this policy is used to resolve a specific
180  * import target when multiple candidate exporting modules exist.
181  * </li>
182  * </ul>
183  * <p>
184  * Once an instance is created with definitions of the above policy interfaces,
185  * this search policy will operate largely self-contained. There are a few utility
186  * methods for manually validating modules, adding validation listeners, and
187  * access meta-data attributes, but for the most part these are not necessary
188  * except for implementing more sophisticated infrastructure.
189  * </p>
190  * <p>
191  * The follow snippet of code illustrates a typical usage scenario for
192  * this search policy:
193  * </p>
194  * <pre>
195  * ...
196  * ImportSearchPolicy searchPolicy =
197  * new ImportSearchPolicy(
198  * new MyCompatibilityPolicy(), new MySelectionPolicy());
199  * ModuleManager mgr = new ModuleManager(searchPolicy);
200  * ...
201  * Object[][] exports = new Object[][] {
202  * { "org.apache.jasper", "2.1.0", null }
203  * };
204  * Object[][] imports = new Object[][] {
205  * { "javax.servlet", "2.3.1", null }
206  * };
207  * Object[][] attributes = new Object[][] {
208  * new Object[] { ImportSearchPolicy.EXPORTS_ATTR, exports },
209  * new Object[] { ImportSearchPolicy.IMPORTS_ATTR, imports },
210  * new Object[] { ImportSearchPolicy.PROPAGATES_ATTR, new Object[] { "javax.servlet" } }
211  * };
212  * ResourceSource[] resSources = new ResourceSource[] {
213  * new JarResourceSource(file1)
214  * new JarResourceSource(file2)
215  * };
216  * Module module = mgr.addModule(id, attributes, resSources, null);
217  * ClassLoader loader = module.getClassLoader();
218  * // Assuming that all imports are satisfied...
219  * Class clazz = loader.loadClass("org.foo.MyClass");
220  * ...
221  * </pre>
222  * <p>
223  * The above code snippet illustrates creating a module with one export and one
224  * import, where the import is also propagated via the module's export. The module
225  * has multiple resource sources, but no library sources.
226  * </p>
227  * @see org.ungoverned.moduleloader.SearchPolicy
228  * @see org.ungoverned.moduleloader.Module
229  * @see org.ungoverned.moduleloader.ModuleClassLoader
230  * @see org.ungoverned.moduleloader.ModuleManager
231 **/

232 public class ImportSearchPolicy implements SearchPolicy, ModuleListener
233 {
234     /**
235      * This is the name of the "exports" meta-data attribute that
236      * should be attached to each module. The value of this attribute
237      * is of type <tt>Object[][]</tt> and is described in the overview
238      * documentation for this class.
239     **/

240     public static final String JavaDoc EXPORTS_ATTR = "exports";
241     /**
242      * This is the name of the "imports" meta-data attribute that
243      * should be attached to each module. The value of this attribute
244      * is of type <tt>Object[][]</tt> and is described in the overview
245      * documentation for this class.
246     **/

247     public static final String JavaDoc IMPORTS_ATTR = "imports";
248     /**
249      * This is the name of the "propagates" meta-data attribute that
250      * should be attached to each module. The value of this attribute
251      * is of type <tt>Object[]</tt> and is described in the overview
252      * documentation for this class.
253     **/

254     public static final String JavaDoc PROPAGATES_ATTR = "propagates";
255     /**
256      * This is the name of the "valid" meta-data attribute that is
257      * automatically attached to each module. The value of this attribute
258      * is of type <tt>Boolean</tt> and is described in the overview
259      * documentation for this class.
260     **/

261     public static final String JavaDoc VALID_ATTR = "valid";
262
263     /**
264      * This is the index used to retrieve the import or export identifier
265      * from a given element of the <tt>EXPORTS_ATTR</tt> or the <tt>IMPORTS_ATTR</tt>
266      * attribute.
267     **/

268     public static final int IDENTIFIER_IDX = 0;
269     /**
270      * This is the index used to retrieve the import or export version number
271      * from a given element of the <tt>EXPORTS_ATTR</tt> or the <tt>IMPORTS_ATTR</tt>
272      * attribute.
273     **/

274     public static final int VERSION_IDX = 1;
275     /**
276      * This is the index used to retrieve the resolving module for an import
277      * or export target from a given element of the <tt>EXPORTS_ATTR</tt> or
278      * the <tt>IMPORTS_ATTR</tt> attribute.
279     **/

280     public static final int RESOLVING_MODULE_IDX = 2;
281
282     private ModuleManager m_mgr = null;
283     private CompatibilityPolicy m_compatPolicy = null;
284     private SelectionPolicy m_selectPolicy = null;
285     private ValidationListener[] m_listeners = null;
286     private String JavaDoc[] m_searchAttrs = { IMPORTS_ATTR, EXPORTS_ATTR };
287     private static final ValidationListener[] m_noListeners = new ValidationListener[0];
288
289     /**
290      * Constructs an import search policy instance with the supplied
291      * compatibility and selection policies.
292      * @param compatPolicy the compatibility policy implementation to be used
293      * by the search policy.
294      * @param selectPolicy the selection policy implementation to be used
295      * by the search policy.
296     **/

297     public ImportSearchPolicy(
298         CompatibilityPolicy compatPolicy,
299         SelectionPolicy selectPolicy)
300     {
301         m_compatPolicy = compatPolicy;
302         m_selectPolicy = selectPolicy;
303         m_listeners = m_noListeners;
304     }
305
306     /**
307      * Returns the compatibility policy used by this import search policy instance.
308      * @return the compatibility policy of this import search policy instance.
309     **/

310     public CompatibilityPolicy getCompatibilityPolicy()
311     {
312         return m_compatPolicy;
313     }
314
315     /**
316      * Returns the selection policy used by this import search policy instance.
317      * @return the selection policy of this import search policy instance.
318     **/

319     public SelectionPolicy getSelectionPolicy()
320     {
321         return m_selectPolicy;
322     }
323
324     // JavaDoc comment copied from SearchPolicy.
325
public void setModuleManager(ModuleManager mgr)
326         throws IllegalStateException JavaDoc
327     {
328         if (m_mgr == null)
329         {
330             m_mgr = mgr;
331             m_mgr.addModuleListener(this);
332         }
333         else
334         {
335             throw new IllegalStateException JavaDoc("Module manager is already initialized");
336         }
337     }
338
339     /**
340      * This method is part of the <tt>SearchPolicy</tt> interface; it
341      * should not be called directly. This method finds a class
342      * based on the import/export meta-data attached to the module.
343      * It first attempts to validate the target module, if it cannot
344      * be validated, then a <tt>ClassNotFoundException</tt> is thrown.
345      * Once the module is validated, the module's imports are searched
346      * for the target class, then the module's exports are searched.
347      * If the class is found in either place, then it is returned;
348      * otherwise, <tt>null</tt> is returned.
349      * @param module the target module that is loading the class.
350      * @param name the name of the class being loaded.
351      * @return the class if found, <tt>null</tt> otherwise.
352      * @throws java.lang.ClassNotFoundException if the target module
353      * could not be validated.
354     **/

355     public Class JavaDoc findClass(Module module, String JavaDoc name)
356         throws ClassNotFoundException JavaDoc
357     {
358         // First, try to validate the originating module.
359
try {
360             validate(module);
361         } catch (ValidationException ex) {
362             throw new ClassNotFoundException JavaDoc(name);
363         }
364
365         // Get the package of the target class.
366
String JavaDoc pkgName = (name.lastIndexOf('.') < 0)
367             ? "" : name.substring(0, name.lastIndexOf('.'));
368
369         // We delegate to the module's imports for finding the
370
// desired class first, then we delegate to the module's
371
// exports for finding the desired class. We do this because
372
// implicitly a module imports everything that it exports.
373
// To avoid code duplication, we use a simple array of
374
// attribute names to loop through both of the imports
375
// and exports meta-data searching for the desired class.
376
for (int attrIdx = 0; attrIdx < m_searchAttrs.length; attrIdx++)
377         {
378             Object JavaDoc[][] imports = getImportsOrExports(module, m_searchAttrs[attrIdx]);
379
380             // If the module doesn't import anything, then just
381
// return null.
382
if ((imports != null) && (imports.length > 0))
383             {
384                 for (int i = 0; i < imports.length; i++)
385                 {
386                     // Only check when the package of the class is
387
// the same as the import package.
388
if (imports[i][IDENTIFIER_IDX].equals(pkgName))
389                     {
390                         Module resolvingModule = (Module) imports[i][RESOLVING_MODULE_IDX];
391                         try
392                         {
393                             Class JavaDoc clazz =
394                                 resolvingModule.getClassLoader().searchForClass(name);
395                             if (clazz != null)
396                             {
397                                 return clazz;
398                             }
399                         }
400                         catch (Throwable JavaDoc th)
401                         {
402                             // Not much we can do here.
403
}
404                         throw new ClassNotFoundException JavaDoc(name);
405                     }
406                 }
407             }
408         }
409
410         return null;
411     }
412
413     /**
414      * This method is part of the <tt>SearchPolicy</tt> interface; it
415      * should not be called directly. This method finds a resource
416      * based on the import/export meta-data attached to the module.
417      * It first attempts to validate the target module, if it cannot
418      * be validated, then it returns <tt>null</tt>. Once the module is
419      * validated, the module's imports are searched for the target
420      * resource, then the module's exports are searched. If the resource
421      * is found in either place, then a <tt>URL</tt> to is is returned;
422      * otherwise, <tt>null</tt> is returned.
423      * @param module the target module that is loading the resource.
424      * @param name the name of the resource being loaded.
425      * @return a <tt>URL</tt> to the resource if found, <tt>null</tt> otherwise.
426     **/

427     public URL JavaDoc findResource(Module module, String JavaDoc name)
428         throws ResourceNotFoundException
429     {
430         // First, try to validate the originating module.
431
try
432         {
433             validate(module);
434         }
435         catch (ValidationException ex)
436         {
437             return null;
438         }
439
440         // Get the package of the target resource.
441
// NOTE: The package of a resource is tricky to determine, since
442
// resources do not follow the same naming conventions as classes.
443
// This code is pessimistic and assumes that the package of a
444
// resource is everything up to the last '/' character. By making
445
// this choice, it will not be possible to load resources from
446
// imports using relative resource names. For example, if a
447
// bundle exports "foo" and an importer of "foo" tries to load
448
// "/foo/bar/myresource.txt", this will not be found in the exporter
449
// because the following algorithm assumes the package name is
450
// "foo.bar", not just "foo". This only affects imported resources,
451
// local resources will work as expected.
452
String JavaDoc pkgName = (name.startsWith("/")) ? name.substring(1) : name;
453         pkgName = (pkgName.lastIndexOf('/') < 0)
454             ? "" : pkgName.substring(0, pkgName.lastIndexOf('/'));
455         pkgName = pkgName.replace('/', '.');
456
457         // We delegate to the module's imports for finding the
458
// desired class first, then we delegate to the module's
459
// exports for finding the desired class. We do this because
460
// implicitly a module imports everything that it exports.
461
// To avoid code duplication, we use a simple array of
462
// attribute names to loop through both of the imports
463
// and exports meta-data searching for the desired class.
464
for (int attrIdx = 0; attrIdx < m_searchAttrs.length; attrIdx++)
465         {
466             Object JavaDoc[][] imports = getImportsOrExports(module, m_searchAttrs[attrIdx]);
467
468             // If the module doesn't import or export anything,
469
// then just return null.
470
if ((imports != null) && (imports.length > 0))
471             {
472                 for (int i = 0; i < imports.length; i++)
473                 {
474                     // Only check when the package of the resource is
475
// the same as the import package.
476
if (imports[i][IDENTIFIER_IDX].equals(pkgName))
477                     {
478                         Module resolvingModule = (Module) imports[i][RESOLVING_MODULE_IDX];
479                         try
480                         {
481                             URL JavaDoc url =
482                                 resolvingModule.getClassLoader().searchForResource(name);
483                             if (url != null)
484                             {
485                                 return url;
486                             }
487                         }
488                         catch (Throwable JavaDoc th)
489                         {
490                             // Not much we can do here.
491
}
492                         throw new ResourceNotFoundException(name);
493                     }
494                 }
495             }
496         }
497
498         return null;
499     }
500
501     private Map m_validateMap = new HashMap();
502     private Module m_rootModule = null;
503
504     /**
505      * This method validates the specified target module. If the module
506      * is already validated, then this method returns immediately. This
507      * method synchronizes on the associated module manager to ensure that
508      * modules are not added or removed while the validation is occuring.
509      * Each import and export for the target module are resolved by first
510      * using the compatibility policy to create a list of candidate export
511      * modules, then using the selection policy to choose among the
512      * candidates. Each selected candidate is also recursively validated;
513      * this process validates a transitive closure of all dependent modules.
514      * After the selected candidate is validated, its propagated imports
515      * are checked to make sure that they do not conflict with any existing
516      * propagated imports. If no validation errors occur, then all dependent
517      * modules are marked as validated, if they are not already validated.
518      * If an error occurs, the valid state of all modules remains unchanged.
519      * @param module the module to validate.
520      * @throws org.ungoverned.moduleloader.search.ValidationException if
521      * the module or any dependent modules could not be validated.
522     **/

523     public void validate(Module module)
524         throws ValidationException
525     {
526         if (getValidAttribute(module).booleanValue())
527         {
528             return;
529         }
530
531         // Flag to indicate whether the bundle is valid or not.
532
boolean isValid = true;
533
534         // This list will be used to remember which bundles
535
// were validated so that the validation events can
536
// be fired outside of the synchronized block.
537
List fireValidatedList = null;
538
539         // Will hold the exception to be thrown or rethrown.
540
ValidationException invalidException = null;
541
542         // Synchronize on the module manager, because we don't want
543
// anything to change while we are in the middle of this
544
// operation.
545
synchronized (m_mgr)
546         {
547             // If we are already validating this module, then
548
// just return; this is necessary for cycles.
549
if (m_validateMap.get(module) != null)
550             {
551                 return;
552             }
553
554             // Add the module to the validation map; this
555
// is necessary for cycles.
556
m_validateMap.put(module, module);
557
558             // Keep track of the root module that started
559
// the validation request; this is necessary
560
// for cycles.
561
if (m_rootModule == null)
562             {
563                 m_rootModule = module;
564             }
565
566             // Now perform the validation algorithm.
567
Map propagateMap = new HashMap();
568   
569             // Validation binds the module's imports to a specific exporting
570
// module. A module also implicitly imports whatever it exports,
571
// so exports are validated in the same fashion as imports. It
572
// is possible, given the selection policy that a given export
573
// may actually be satisfied by a different module (i.e., a
574
// module is not guaranteed to be bound to what it exports). Since
575
// the imports and exports meta-data are validated in the same
576
// fashion, we will use the follow attribute array to loop and
577
// validate both imports and exports using the same code.
578
for (int attrIdx = 0; (isValid) && (attrIdx < m_searchAttrs.length); attrIdx++)
579             {
580                 // Get the imports (exports are treated as imports to)
581
// for the current module.
582
Object JavaDoc[][] imports = getImportsOrExports(module, m_searchAttrs[attrIdx]);
583                 // See if each import has available exporters.
584
for (int impIdx = 0; impIdx < imports.length; impIdx++)
585                 {
586                     // Get all exporter candidates.
587
Module[] candidates =
588                         getCompatibleModules(
589                             imports[impIdx][IDENTIFIER_IDX], imports[impIdx][VERSION_IDX]);
590                     // If there are no candidates, then prepare a
591
// validation exception.
592
if (candidates == null)
593                     {
594                         isValid = false;
595                         invalidException =
596                             new ValidationException(
597                                 "Unable to validate module",
598                                 module,
599                                 imports[impIdx][IDENTIFIER_IDX],
600                                 imports[impIdx][VERSION_IDX],
601                                 false);
602                         break;
603                     }
604
605                     // Use selection policy to choose a single export candidate.
606
Module exportingModule = m_selectPolicy.select(
607                         module, imports[impIdx][IDENTIFIER_IDX],
608                         imports[impIdx][VERSION_IDX], candidates, m_compatPolicy);
609                     // If there is no export module chosen, then prepare
610
// a validation exception.
611
if (exportingModule == null)
612                     {
613                         isValid = false;
614                         invalidException =
615                             new ValidationException(
616                                 "Unable to validate module",
617                                 module,
618                                 imports[impIdx][IDENTIFIER_IDX],
619                                 imports[impIdx][VERSION_IDX],
620                                 false);
621                         break;
622                     }
623
624                     // Make sure that the export module is
625
// also validated.
626
try
627                     {
628                         validate(exportingModule);
629                     }
630                     catch (ValidationException ex)
631                     {
632                         // Prepare to rethrow the exception if
633
// the exporter could not be validated.
634
isValid = false;
635                         invalidException = ex;
636                         break;
637                     }
638
639                     // Keep track of all propagations from each module that this
640
// module imports from. Verify that any given import always
641
// comes form the same export module, otherwise there will be
642
// class cast exceptions.
643
Object JavaDoc[] propagates = getPropagatesAttribute(exportingModule);
644                     for (int propIdx = 0; propIdx < propagates.length; propIdx++)
645                     {
646                         // If the module does not import the propagated target,
647
// then it can be safely ignored.
648
if (doesImport(module, propagates[propIdx]))
649                         {
650                             Module sourceModule =
651                                 (Module) propagateMap.get(propagates[propIdx]);
652
653                             // If the propagation source module has not already been
654
// found, then remember the resolving module of the
655
// exporting module as the source of the propagated
656
// target.
657
if (sourceModule == null)
658                             {
659                                 propagateMap.put(
660                                     propagates[propIdx],
661                                     getImportResolvingModule(
662                                         exportingModule, propagates[propIdx]));
663                             }
664                             // If the propagation source module is found, then check to
665
// see if it is propagating the import target from the same
666
// module as previously determined for this module. If not,
667
// then this is a propagation conflict.
668
else if (sourceModule !=
669                                 getImportResolvingModule(
670                                     exportingModule, propagates[propIdx]))
671                             {
672                                 isValid = false;
673                                 invalidException =
674                                     new ValidationException(
675                                         "Unable to validate module",
676                                         exportingModule,
677                                         propagates[propIdx],
678                                         null,
679                                         true);
680                                 break;
681                             }
682                         }
683                     }
684
685                     // Set the chosen exporting module for the module
686
// being validated.
687
imports[impIdx][RESOLVING_MODULE_IDX] = exportingModule;
688                 }
689             }
690
691             // Since this method is recursive, check to see it we are
692
// back at the root module that started the request, which
693
// would indicate that the request is finished.
694
if (m_rootModule == module)
695             {
696                 // If the result is valid, then we have validated successfully.
697
if (isValid)
698                 {
699                     // Loop through all modules in validate map
700
// and mark them as valid.
701
Iterator iter = m_validateMap.keySet().iterator();
702                     while (iter.hasNext())
703                     {
704                         Module m = (Module) iter.next();
705                         if (!getValidAttribute(m).booleanValue())
706                         {
707                             m.setAttribute(VALID_ATTR, Boolean.TRUE);
708                             if (fireValidatedList == null)
709                             {
710                                 fireValidatedList = new ArrayList();
711                             }
712                             fireValidatedList.add(m);
713                         }
714                     }
715                 }
716                 // If we are here, then the validate failed, so we
717
// need to reset any partially validated modules.
718
else
719                 {
720                     Iterator iter = m_validateMap.keySet().iterator();
721                     while (iter.hasNext())
722                     {
723                         Module m = (Module) iter.next();
724                         invalidate(
725                             m,
726                             m.getAttributes(),
727                             m.getResourceSources(),
728                             m.getLibrarySources());
729                     }
730                 }
731
732                 // Clear the root module and validation map
733
// before leaving the synchronized block.
734
m_rootModule = null;
735                 m_validateMap.clear();
736             }
737         }
738
739         // (Re)throw the exception if invalid, otherwise
740
// fire validation events if the validated event
741
// list is not null.
742
if (!isValid)
743         {
744             throw invalidException;
745         }
746         else if (fireValidatedList != null)
747         {
748             for (int i = 0; i < fireValidatedList.size(); i++)
749             {
750                 fireModuleValidated((Module) fireValidatedList.get(i));
751             }
752         }
753     }
754
755     /**
756      * This method returns a list of modules that have an export
757      * that is compatible with the given import identifier and version.
758      * @param identifier the import identifier.
759      * @param version the version of the import identifier.
760      * @return an array of modules that have compatible exports or <tt>null</tt>
761      * if none are found.
762     **/

763     protected Module[] getCompatibleModules(Object JavaDoc identifier, Object JavaDoc version)
764     {
765         List list = null;
766         Module[] modules = m_mgr.getModules();
767         for (int modIdx = 0; modIdx < modules.length; modIdx++)
768         {
769             Object JavaDoc[][] exports = getExportsAttribute(modules[modIdx]);
770             for (int expIdx = 0; expIdx < exports.length; expIdx++)
771             {
772                 // If the identifiers are comparable and compatible,
773
// then add the export identifier to the list.
774
if (m_compatPolicy.isCompatible(
775                         exports[expIdx][IDENTIFIER_IDX], exports[expIdx][VERSION_IDX],
776                         identifier, version))
777                 {
778                     if (list == null)
779                     {
780                         list = new ArrayList();
781                     }
782                     list.add(modules[modIdx]);
783                 }
784             }
785         }
786
787         if (list == null)
788         {
789             return null;
790         }
791
792         Module[] result = new Module[list.size()];
793         return (Module[]) list.toArray(result);
794     }
795
796     /**
797      * Invalidates a module by flushing its class loader and
798      * re-initializing its meta-data values.
799      * @param module the module to be invalidated.
800      * @param attributes the attributes associated with the module, since they
801      * might have changed.
802      * @param resSources the resource sources associated wih the module, since they
803      * might have changed.
804      * @param libSources the library sources associated wih the module, since they
805      * might have changed.
806     **/

807     public void invalidate(
808         Module module, Object JavaDoc[][] attributes,
809         ResourceSource[] resSources, LibrarySource[] libSources)
810     {
811         // Synchronize on the module manager, because we don't want
812
// anything to change while we are in the middle of this
813
// operation.
814
synchronized (m_mgr)
815         {
816             m_mgr.resetModule(module, attributes, resSources, libSources);
817         }
818
819         // Fire invalidation event if necessary.
820
fireModuleInvalidated(m_mgr.getModule(module.getId()));
821     }
822
823     //
824
// Event handling methods for validation events.
825
//
826

827     /**
828      * Adds a validation listener to this import search policy. Validation
829      * listeners are notified when a module is validated and/or invalidated
830      * by the search policy.
831      * @param l the validation listener to add.
832     **/

833     public void addValidationListener(ValidationListener l)
834     {
835         // Verify listener.
836
if (l == null)
837         {
838             throw new IllegalArgumentException JavaDoc("Listener is null");
839         }
840
841         // Use the m_noListeners object as a lock.
842
synchronized (m_noListeners)
843         {
844             // If we have no listeners, then just add the new listener.
845
if (m_listeners == m_noListeners)
846             {
847                 m_listeners = new ValidationListener[] { l };
848             }
849             // Otherwise, we need to do some array copying.
850
// Notice, the old array is always valid, so if
851
// the dispatch thread is in the middle of a dispatch,
852
// then it has a reference to the old listener array
853
// and is not affected by the new value.
854
else
855             {
856                 ValidationListener[] newList = new ValidationListener[m_listeners.length + 1];
857                 System.arraycopy(m_listeners, 0, newList, 0, m_listeners.length);
858                 newList[m_listeners.length] = l;
859                 m_listeners = newList;
860             }
861         }
862     }
863
864     /**
865      * Removes a validation listener to this import search policy.
866      * @param l the validation listener to remove.
867     **/

868     public void removeValidationListener(ValidationListener l)
869     {
870         // Verify listener.
871
if (l == null)
872         {
873             throw new IllegalArgumentException JavaDoc("Listener is null");
874         }
875
876         // Use the m_noListeners object as a lock.
877
synchronized (m_noListeners)
878         {
879             // Try to find the instance in our list.
880
int idx = -1;
881             for (int i = 0; i < m_listeners.length; i++)
882             {
883                 if (m_listeners[i].equals(l))
884                 {
885                     idx = i;
886                     break;
887                 }
888             }
889
890             // If we have the instance, then remove it.
891
if (idx >= 0)
892             {
893                 // If this is the last listener, then point to empty list.
894
if (m_listeners.length == 1)
895                 {
896                     m_listeners = m_noListeners;
897                 }
898                 // Otherwise, we need to do some array copying.
899
// Notice, the old array is always valid, so if
900
// the dispatch thread is in the middle of a dispatch,
901
// then it has a reference to the old listener array
902
// and is not affected by the new value.
903
else
904                 {
905                     ValidationListener[] newList = new ValidationListener[m_listeners.length - 1];
906                     System.arraycopy(m_listeners, 0, newList, 0, idx);
907                     if (idx < newList.length)
908                     {
909                         System.arraycopy(m_listeners, idx + 1, newList, idx,
910                             newList.length - idx);
911                     }
912                     m_listeners = newList;
913                 }
914             }
915         }
916     }
917
918     /**
919      * Fires a validation event for the specified module.
920      * @param module the module that was validated.
921     **/

922     protected void fireModuleValidated(Module module)
923     {
924         // Event holder.
925
ModuleEvent event = null;
926
927         // Get a copy of the listener array, which is guaranteed
928
// to not be null.
929
ValidationListener[] listeners = m_listeners;
930
931         // Loop through listeners and fire events.
932
for (int i = 0; i < listeners.length; i++)
933         {
934             // Lazily create event.
935
if (event == null)
936             {
937                 event = new ModuleEvent(m_mgr, module);
938             }
939             listeners[i].moduleValidated(event);
940         }
941     }
942
943     /**
944      * Fires an invalidation event for the specified module.
945      * @param module the module that was invalidated.
946     **/

947     protected void fireModuleInvalidated(Module module)
948     {
949         // Event holder.
950
ModuleEvent event = null;
951
952         // Get a copy of the listener array, which is guaranteed
953
// to not be null.
954
ValidationListener[] listeners = m_listeners;
955
956         // Loop through listeners and fire events.
957
for (int i = 0; i < listeners.length; i++)
958         {
959             // Lazily create event.
960
if (event == null)
961             {
962                 event = new ModuleEvent(m_mgr, module);
963             }
964             listeners[i].moduleInvalidated(event);
965         }
966     }
967
968     //
969
// ModuleListener methods.
970
//
971

972     /**
973      * Callback method for <tt>ModuleListener</tt>; this should not
974      * be called directly. This callback is used to initialize module
975      * meta-data attributes; it adds the <tt>VALID_ATTR</tt> attribute
976      * and initializes the resolving module entries in <tt>EXPORTS_ATTR</tt>
977      * and <tt>IMPORTS_ATTR</tt> to <tt>null</tt>.
978     **/

979     public void moduleAdded(ModuleEvent event)
980     {
981         synchronized (event.getModule())
982         {
983             // Add valid attribute to all modules.
984
event.getModule().setAttribute(VALID_ATTR, Boolean.FALSE);
985
986             for (int attrIdx = 0; attrIdx < m_searchAttrs.length; attrIdx++)
987             {
988                 Object JavaDoc[][] imports =
989                     getImportsOrExports(event.getModule(), m_searchAttrs[attrIdx]);
990                 for (int i = 0; i < imports.length; i++)
991                 {
992                     imports[i][RESOLVING_MODULE_IDX] = null;
993                 }
994             }
995         }
996     }
997
998     /**
999      * Callback method for <tt>ModuleListener</tt>; this should not
1000     * be called directly. This callback is used to re-initialize module
1001     * meta-data attributes; it adds the <tt>VALID_ATTR</tt> attribute
1002     * and initializes the resolving module entries in <tt>EXPORTS_ATTR</tt>
1003     * and <tt>IMPORTS_ATTR</tt> to <tt>null</tt>. It then invalidates
1004     * all modules that import from the reset module.
1005    **/

1006    public void moduleReset(ModuleEvent event)
1007    {
1008        // This will reset module meta-data.
1009
moduleAdded(event);
1010
1011// TODO: Synchronization?
1012
ModuleManager m_mgr = (ModuleManager) event.getSource();
1013        List list = createImporterList(m_mgr, event.getModule());
1014        for (int i = 0; (list != null) && (i < list.size()); i++)
1015        {
1016            Module module = (Module) list.get(i);
1017            invalidate(
1018                module, module.getAttributes(),
1019                module.getResourceSources(), module.getLibrarySources());
1020        }
1021    }
1022
1023    /**
1024     * Callback method for <tt>ModuleListener</tt>; this should not
1025     * be called directly. Used to listen for module removal events
1026     * in order to invalidate all the modules that import form the
1027     * removed moduled.
1028    **/

1029    public void moduleRemoved(ModuleEvent event)
1030    {
1031// TODO: Synchronization?
1032
ModuleManager m_mgr = (ModuleManager) event.getSource();
1033        List list = createImporterList(m_mgr, event.getModule());
1034        for (int i = 0; (list != null) && (i < list.size()); i++)
1035        {
1036            Module module = (Module) list.get(i);
1037            invalidate(
1038                module, module.getAttributes(),
1039                module.getResourceSources(), module.getLibrarySources());
1040        }
1041    }
1042
1043    //
1044
// Instance utility methods.
1045
//
1046

1047    /**
1048     * This utility method returns the module that exports the
1049     * specified import identifier and version. This method uses the
1050     * <tt>validate()</tt> method to find the exporting module and,
1051     * as a result, relies on the compatibility and selection
1052     * policies associated with this <tt>ImportSearchPolicy</tt>
1053     * instance. If successful, the returned module is guaranteed
1054     * to be validated. This method only needs to be used for more
1055     * advanced purposes (i.e., check import availability dynamically,
1056     * etc.) and need not be used under normal circumstances.
1057     * @param identifier the identifier of the import to resolve.
1058     * @param version the version of the import to resolve.
1059     * @return the exporting module selected to resolve the specified
1060     * import target.
1061    **/

1062    public Module resolveImportTarget(Object JavaDoc identifier, Object JavaDoc version)
1063    {
1064        // Create a fake module that imports the specified target
1065
// and then try to validate it so we can get the exporting
1066
// module that is used to satisfy the import.
1067
Object JavaDoc[] targetImport = { identifier, version, null };
1068        Object JavaDoc[][] attrs = new Object JavaDoc[][] {
1069            new Object JavaDoc[] { EXPORTS_ATTR, new Object JavaDoc[0][0] },
1070            new Object JavaDoc[] { IMPORTS_ATTR, new Object JavaDoc[][] { targetImport } },
1071            new Object JavaDoc[] { PROPAGATES_ATTR, new Object JavaDoc[0] },
1072            new Object JavaDoc[] { VALID_ATTR, Boolean.FALSE}
1073        };
1074        Module fake = new Module(m_mgr, "resolve import", attrs, null, null);
1075        try {
1076            validate(fake);
1077        } catch (ValidationException ex) {
1078            // Ignore this.
1079
}
1080        return (Module) targetImport[RESOLVING_MODULE_IDX];
1081    }
1082
1083    //
1084
// Static utility methods.
1085
//
1086

1087    private static final Object JavaDoc[][] m_emptyImports = new Object JavaDoc[0][0];
1088    private static final Object JavaDoc[] m_emptyProp = new Object JavaDoc[0];
1089
1090    /**
1091     * Utility method that returns the <tt>VALID_ATTR</tt> attribute for
1092     * the specified module.
1093     * @param module the module whose <tt>VALID_ATTR</tt> attribute is to
1094     * be retrieved.
1095     * @return an instance of <tt>Boolean</tt>.
1096    **/

1097    public static Boolean JavaDoc getValidAttribute(Module module)
1098    {
1099        Object JavaDoc value = module.getAttribute(VALID_ATTR);
1100        if (value != null)
1101        {
1102            return (Boolean JavaDoc) value;
1103        }
1104        return Boolean.FALSE;
1105    }
1106
1107    /**
1108     * Utility method that returns the <tt>IMPORTS_ATTR</tt> attribute for
1109     * the specified module.
1110     * @param module the module whose <tt>IMPORTS_ATTR</tt> attribute is to
1111     * be retrieved.
1112     * @return an <tt>Object[][]</tt> value or <tt>null</tt>.
1113    **/

1114    public static Object JavaDoc[][] getImportsAttribute(Module module)
1115    {
1116        Object JavaDoc value = module.getAttribute(IMPORTS_ATTR);
1117        if (value != null)
1118        {
1119            return (Object JavaDoc[][]) value;
1120        }
1121        return m_emptyImports;
1122    }
1123
1124    /**
1125     * Utility method that returns the <tt>EXPORTS_ATTR</tt> attribute for
1126     * the specified module.
1127     * @param module the module whose <tt>EXPORTS_ATTR</tt> attribute is to
1128     * be retrieved.
1129     * @return an <tt>Object[][]</tt> value or <tt>null</tt>.
1130    **/

1131    public static Object JavaDoc[][] getExportsAttribute(Module module)
1132    {
1133        Object JavaDoc value = module.getAttribute(EXPORTS_ATTR);
1134        if (value != null)
1135        {
1136            return (Object JavaDoc[][]) value;
1137        }
1138        return m_emptyImports;
1139    }
1140
1141    /**
1142     * Utility method that returns the <tt>IMPORTS_ATTR</tt> or the
1143     * <tt>EXPORTS_ATTR</tt> attribute for the specified module.
1144     * @param module the module whose <tt>IMPORTS_ATTR</tt> or
1145     * <tt>EXPORTS_ATTR</tt> attribute is to be retrieved.
1146     * @param name either <tt>IMPORTS_ATTR</tt> or <tt>EXPORTS_ATTR</tt>
1147     * depending on which attribute should be retrieved.
1148     * @return an <tt>Object[][]</tt> value or <tt>null</tt>.
1149    **/

1150    public static Object JavaDoc[][] getImportsOrExports(Module module, String JavaDoc name)
1151    {
1152        Object JavaDoc value = module.getAttribute(name);
1153        if (value != null)
1154        {
1155            return (Object JavaDoc[][]) value;
1156        }
1157        return m_emptyImports;
1158    }
1159
1160    /**
1161     * Utility method that returns the <tt>PROPAGATES_ATTR</tt> attribute for
1162     * the specified module.
1163     * @param module the module whose <tt>PROPAGATES_ATTR</tt> attribute is to
1164     * be retrieved.
1165     * @return an <tt>Object[]</tt> value or <tt>null</tt>.
1166    **/

1167    public static Object JavaDoc[] getPropagatesAttribute(Module module)
1168    {
1169        Object JavaDoc value = module.getAttribute(PROPAGATES_ATTR);
1170        if (value != null)
1171        {
1172            return (Object JavaDoc[]) value;
1173        }
1174        return m_emptyProp;
1175    }
1176
1177    /**
1178     * Utility method to determine if the specified module imports a given
1179     * import identifier, regardless of version. This method checks both
1180     * imports and exports, since a module is assumed to import what it exports.
1181     * @param module the module to check.
1182     * @param identifier the import identifier to check.
1183     * @return <tt>true</tt> if the module imports the specified
1184     * import identifier or <tt>false</tt> if it does not.
1185    **/

1186    public static boolean doesImport(Module module, Object JavaDoc identifier)
1187    {
1188        Object JavaDoc[][] imports = getImportsAttribute(module);
1189        for (int i = 0; i < imports.length; i++)
1190        {
1191            if (imports[i][IDENTIFIER_IDX].equals(identifier))
1192            {
1193                return true;
1194            }
1195        }
1196        imports = getExportsAttribute(module);
1197        for (int i = 0; i < imports.length; i++)
1198        {
1199            if (imports[i][IDENTIFIER_IDX].equals(identifier))
1200            {
1201                return true;
1202            }
1203        }
1204        return false;
1205    }
1206
1207    /**
1208     * Utility method to create a list of modules that import from
1209     * the specified module.
1210     * @param mgr the module manager that contains the module.
1211     * @param module the module for which to create an importer list.
1212     * @return a list of modules that import from the specified module
1213     * or <tt>null</tt>.
1214    **/

1215    public static List createImporterList(ModuleManager mgr, Module module)
1216    {
1217        List list = null;
1218        Module[] modules = mgr.getModules();
1219        for (int modIdx = 0; modIdx < modules.length; modIdx++)
1220        {
1221            Object JavaDoc[][] imports = getImportsAttribute(modules[modIdx]);
1222            for (int impIdx = 0; impIdx < imports.length; impIdx++)
1223            {
1224                if (imports[impIdx][RESOLVING_MODULE_IDX] == module)
1225                {
1226                    if (list == null)
1227                    {
1228                        list = new ArrayList();
1229                    }
1230                    list.add(modules[modIdx]);
1231                    break;
1232                }
1233            }
1234        }
1235
1236        return list;
1237    }
1238
1239    /**
1240     * Utility method to get the import version number associated with a specific
1241     * import identifier of the specified module.
1242     * @param module the module to investigate.
1243     * @param identifier the import identifier for which the version should
1244     * be retrieved.
1245     * @return the version number object or <tt>null</tt>.
1246    **/

1247    public static Object JavaDoc getImportVersion(Module module, Object JavaDoc identifier)
1248    {
1249        Object JavaDoc[][] imports = getImportsAttribute(module);
1250        for (int i = 0; i < imports.length; i++)
1251        {
1252            if (imports[i][IDENTIFIER_IDX].equals(identifier))
1253            {
1254                return imports[i][VERSION_IDX];
1255            }
1256        }
1257        return null;
1258    }
1259
1260    /**
1261     * Utility method to get the export version number associated with a specific
1262     * export identifier of the specified module.
1263     * @param module the module to investigate.
1264     * @param identifier the export identifier for which the version should
1265     * be retrieved.
1266     * @return the version number object or <tt>null</tt>.
1267    **/

1268    public static Object JavaDoc getExportVersion(Module module, Object JavaDoc identifier)
1269    {
1270        Object JavaDoc[][] exports = getExportsAttribute(module);
1271        for (int i = 0; i < exports.length; i++)
1272        {
1273            if (exports[i][IDENTIFIER_IDX].equals(identifier))
1274            {
1275                return exports[i][VERSION_IDX];
1276            }
1277        }
1278        return null;
1279    }
1280
1281    /**
1282     * Utility method to get the resolving module of a specific import
1283     * identifier for the specified module.
1284     * @param module the module to investigate.
1285     * @param identifier the import identifier for which the resolving
1286     * module should be retrieved.
1287     * @return the resolving module or <tt>null</tt>.
1288    **/

1289    public static Module getImportResolvingModule(Module module, Object JavaDoc identifier)
1290    {
1291        Object JavaDoc[][] imports = getImportsAttribute(module);
1292
1293        for (int i = 0; i < imports.length; i++)
1294        {
1295            if (imports[i][IDENTIFIER_IDX].equals(identifier))
1296            {
1297                return (Module) imports[i][RESOLVING_MODULE_IDX];
1298            }
1299        }
1300
1301        return null;
1302    }
1303
1304    /**
1305     * Utility method to get the resolving module of a specific export
1306     * identifier for the specified module.
1307     * @param module the module to investigate.
1308     * @param identifier the export identifier for which the resolving
1309     * module should be retrieved.
1310     * @return the resolving module or <tt>null</tt>.
1311    **/

1312    public static Module getExportResolvingModule(Module module, Object JavaDoc identifier)
1313    {
1314        Object JavaDoc[][] exports = getExportsAttribute(module);
1315
1316        for (int i = 0; i < exports.length; i++)
1317        {
1318            if (exports[i][IDENTIFIER_IDX].equals(identifier))
1319            {
1320                return (Module) exports[i][RESOLVING_MODULE_IDX];
1321            }
1322        }
1323
1324        return null;
1325    }
1326}
Popular Tags