KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > opensugar > cube > packageAdmin > PackageAdminImpl


1 /*
2  * JEFFREE: Java(TM) Embedded Framework FREE
3  * Copyright (C) 1999-2003 - Opensugar
4  *
5  * The contents of this file are subject to the Jeffree Public License,
6  * as defined by the file JEFFREE_LICENSE.TXT
7  *
8  * You may not use this file except in compliance with the License.
9  * You may obtain a copy of the License on the Objectweb web site
10  * (www.objectweb.org).
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
14  * the specific terms governing rights and limitations under the License.
15  *
16  * The Original Code is JEFFREE, including the java package com.opensugar.cube,
17  * released January 1, 2003.
18  *
19  * The Initial Developer of the Original Code is Opensugar.
20  * The Original Code is Copyright Opensugar.
21  * All Rights Reserved.
22  *
23  * Initial developer(s): Pierre Scokaert (Opensugar)
24  * Contributor(s):
25  */

26
27 package com.opensugar.cube.packageAdmin;
28
29 import com.opensugar.cube.AbstractCube;
30 import com.opensugar.cube.BundleClassLoader;
31 import com.opensugar.cube.BundleImpl;
32 import com.opensugar.cube.NamedPropertySet;
33
34 import org.osgi.framework.Bundle;
35 import org.osgi.framework.FrameworkEvent;
36 import org.osgi.framework.Constants;
37 import org.osgi.service.packageadmin.PackageAdmin;
38 import org.osgi.service.packageadmin.ExportedPackage;
39
40 import java.util.Hashtable;
41 import java.util.Vector;
42 import java.util.Enumeration;
43 import java.net.URL;
44
45 public class PackageAdminImpl implements PackageAdmin {
46
47    private AbstractCube cube;
48
49    // exported packages are exported by resolved bundles (therefore really available)
50
private Hashtable exportedPackages; // maps package name to ExportedPackageImpl object
51

52    // proposed exports are packages proposed for export by bundles
53
private Hashtable proposedExports; // maps bundles to PackageExport objects
54
// required imports are package required by bundles
55
private Hashtable requiredImports; // maps bundles to PackageImport objects
56

57    public PackageAdminImpl( AbstractCube cube ) {
58       this.cube = cube;
59
60       exportedPackages = new Hashtable();
61       proposedExports = new Hashtable();
62       requiredImports = new Hashtable();
63    }
64
65 // *****************************************************************************
66
//
67
// implements PackageAdmin
68
//
69
// *****************************************************************************
70

71    public ExportedPackage getExportedPackage( String packageName ) {
72       return (ExportedPackage)exportedPackages.get( packageName );
73    }
74
75    public ExportedPackage[] getExportedPackages( Bundle bundle ) {
76       Hashtable clone = (Hashtable)exportedPackages.clone();
77       Enumeration enum = clone.elements();
78       Vector vec = new Vector();
79       ExportedPackage exportedPackage;
80       while ( enum.hasMoreElements() ) {
81          exportedPackage = (ExportedPackage)enum.nextElement();
82          if ( bundle == null || bundle.getBundleId() == exportedPackage.getExportingBundle().getBundleId() ) {
83             vec.addElement( exportedPackage );
84          }
85       }
86       ExportedPackage[] ret = new ExportedPackage[ vec.size() ];
87       vec.copyInto( ret );
88       return ret;
89    }
90
91    public void refreshPackages( Bundle[] bundles ) {
92       final Bundle[] bs = bundles;
93       ( new Thread( new Runnable() {
94          public void run() {
95             doRefreshPackages( bs );
96          }
97       } ) ).start();
98    }
99
100
101 // *****************************************************************************
102
//
103
// methods for exporting, importing, unimporting, and refreshing packages
104
//
105
// *****************************************************************************
106

107    // Called by a bundle when the bundle is installed or updated,
108
// to notify the package registry that the specified package is proposed for export
109
// by the bundle
110
//
111
// Packages are really exported only when the bundle becomes resolved.
112
// Resolving bundles is the business of this class's resolveBundles method.
113
public synchronized void exportPackage( Bundle exportingBundle, String packageName, String specificationVersion, BundleClassLoader classLoader ) {
114       getProposedExports( exportingBundle ).addElement( new PackageExport( packageName, specificationVersion, exportingBundle, classLoader ) );
115
116       // special treatment for system bundle
117
// system bundle packages are immediately exported (the system bundle is never
118
// in installed state, so it makes no sense for its bundles not to be exported)
119
if ( exportingBundle.getBundleId() == 0 ) {
120          exportedPackages.put( packageName, new ExportedPackageImpl( packageName, specificationVersion, exportingBundle, classLoader ) );
121       }
122    }
123
124    // Called by a bundle when the bundle is installed or updated,
125
// to notify package registry that the specificed package is required by the bundle
126
//
127
// Packages are really imported only when the bundle becomes resolved.
128
// Resolving bundles is the business of this class's resolveBundles method.
129
public synchronized void importPackage( Bundle importingBundle, String packageName, String specificationVersion ) {
130       getRequiredImports( importingBundle ).addElement( new PackageImport( importingBundle, packageName, specificationVersion ) );
131    }
132
133    // called by a bundle when the bundle is uninstalled or in first stage of bundle update,
134
// to notify package registry that the specified bundle no longer participates
135
// in bundle sharing
136
public synchronized void bundleUpdatedOrUninstalled( BundleImpl bundle ) {
137       // remove the specified bundle from the list of importers of all exported packages
138
// set the removal pending flag of any package exported by the specified bundle
139
Enumeration enum = exportedPackages.elements();
140       ExportedPackageImpl exportedPackage;
141       while ( enum.hasMoreElements() ) {
142          exportedPackage = (ExportedPackageImpl)enum.nextElement();
143          if ( bundle.getBundleId() == exportedPackage.getExportingBundle().getBundleId() ) {
144             exportedPackage.setRemovalPending();
145             // do not remove specified bundle from the list of importing bundles of the
146
// exported package (this is an implicit import)
147
}
148          else {
149             // this call will have no effect if the the bundle is not an importer of the package
150
exportedPackage.removeImportingBundle( bundle );
151          }
152       }
153
154       // remove proposed exports and required imports for the bundle
155
proposedExports.remove( bundle );
156       requiredImports.remove( bundle );
157    }
158
159
160 // *****************************************************************************
161
//
162
// class and resource loading
163
//
164
// *****************************************************************************
165

166    public Class loadClass( Bundle requestingBundle, String fullyQualifiedClassName ) throws ClassNotFoundException {
167       // Imported packages cannot be searched by a bundle that is not resolved
168
// That is ok, because bundle will not be in the importing bundles of an exported
169
// package if it is not resolved
170

171       // Determine the package name from the fully qualified class name.
172
int n = fullyQualifiedClassName.lastIndexOf( "." );
173       if ( n == -1 ) {
174          throw new IllegalArgumentException( "Cannot get a class that does not belong to a package: " + fullyQualifiedClassName );
175       }
176       String packageName = fullyQualifiedClassName.substring( 0, n );
177
178       // Get the ExportedPackageImpl for the package.
179
ExportedPackageImpl exportedPackage = (ExportedPackageImpl)exportedPackages.get( packageName );
180       if ( exportedPackage != null ) {
181          // Special case for java.security (which need not be explicitly imported but may
182
// not be provided by system class loader and may then be exported by the system
183
// bundle)
184
if ( packageName.equals( "java.security" ) ) {
185             exportedPackage.loadClass( fullyQualifiedClassName );
186          }
187          else if ( exportedPackage.isImportingBundle( requestingBundle ) ) {
188             return exportedPackage.loadClass( fullyQualifiedClassName );
189          }
190       }
191
192       // No exported package found corresponding to class name, or requesting bundle
193
// does not import the required package
194
throw new ClassNotFoundException( fullyQualifiedClassName );
195    }
196
197    public synchronized URL getResource( Bundle requestingBundle, String name ) {
198       // Imported packages cannot be searched by a bundle that is not resolved
199
// That is ok, because bundle will not be in the importing bundles of an exported
200
// package if it is not resolved
201

202       // Determine the package from the resource name.
203
int n = name.lastIndexOf( "/" );
204       if ( n == -1 ) {
205          return null;
206       }
207       String packageName = name.substring( 0, n );
208       packageName = packageName.replace( '/', '.' );
209       // Get the ExportedPackageImpl for the package.
210
ExportedPackageImpl exportedPackage = (ExportedPackageImpl)exportedPackages.get( packageName );
211       if ( exportedPackage != null && exportedPackage.isImportingBundle( requestingBundle ) ) {
212          return exportedPackage.getResource( name );
213       }
214
215       // No exported package found corresponding to resource name, or requesting bundle
216
// does not import the required package
217
return null;
218    }
219
220
221 // *****************************************************************************
222
//
223
// utility methods
224
//
225
// *****************************************************************************
226

227    public synchronized PackageImport[] getUnsatisfiedImports( Bundle bundle ) {
228       Vector unsatisfied = (Vector)getRequiredImports( bundle ).clone();
229       Vector satisfied = new Vector();
230       for ( int i = 0; i < unsatisfied.size(); i++ ) {
231          PackageImport required = (PackageImport)unsatisfied.elementAt( i );
232          ExportedPackageImpl available = (ExportedPackageImpl)exportedPackages.get( required.getName() );
233          if ( available != null ) {
234             GoRoCoVersion requiredVersion = new GoRoCoVersion( required.getSpecificationVersion() );
235             GoRoCoVersion availableVersion = new GoRoCoVersion( available.getSpecificationVersion() );
236             // if either available version or required version (but not both) is undefined
237
// available version is considered greater
238
// see implementation of GoRoCoVersion.compareTo()
239
if ( availableVersion.compareTo( requiredVersion ) >= 0 ) {
240                satisfied.addElement( required );
241             }
242          }
243       }
244       for ( int i = 0; i < satisfied.size(); i++ ) {
245          unsatisfied.removeElement( satisfied.elementAt( i ) );
246       }
247
248       // Do not include unsatisfied implicit package imports, unless it is for a package
249
// that is already exported (in which case if the available version is adequate,
250
// the import will already have been removed above, and if the available version
251
// is not adequate, then we should include the import)
252
Vector implicit = new Vector();
253       Vector proposed = getProposedExports( bundle );
254       for ( int i = 0; i < unsatisfied.size(); i++ ) {
255          PackageImport pack1 = (PackageImport)unsatisfied.elementAt( i );
256          if ( exportedPackages.get( pack1.getName() ) == null ) {
257             for ( int j = 0; j < proposed.size(); j++ ) {
258                PackageExport pack2 = (PackageExport)proposed.elementAt( j );
259                if ( pack2 != null && pack2.getName().equals( pack1.getName() ) ) {
260                   GoRoCoVersion v1 = new GoRoCoVersion( pack2.getSpecificationVersion() );
261                   GoRoCoVersion v2 = new GoRoCoVersion( pack1.getSpecificationVersion() );
262                   // if either v1 or required v2 (but not both) is undefined
263
// v1 is considered greater
264
// see implementation of GoRoCoVersion.compareTo()
265
if ( v1.compareTo( v2 ) >= 0 ) {
266                      implicit.addElement( pack1 );
267                   }
268                }
269             }
270          }
271       }
272       for ( int i = 0; i < implicit.size(); i++ ) {
273          unsatisfied.removeElement( implicit.elementAt( i ) );
274       }
275
276       PackageImport[] ret = new PackageImport[ unsatisfied.size() ];
277       unsatisfied.copyInto( ret );
278       return ret;
279    }
280
281 // *****************************************************************************
282
//
283
// refresh packages
284
//
285
// *****************************************************************************
286

287    // called to do package refresh synchronously (used by cube on startup)
288
// this is the method that actually does the work
289
// the refreshPackages() method just creates a thread in which this method
290
// is executed asynchronously
291
public synchronized void doRefreshPackages( Bundle[] bundles ) {
292       // 1. Compute a graph of bundles starting with the specified ones. If no bundles
293
// are specified, compute a graph of bundles starting with previously updated
294
// or uninstalled ones. Any bundle that imports a package that is currently
295
// exported by a bundle in the graph is added to the graph. The graph is fully
296
// contructed when there is no bundles outside the graph that imports a package
297
// from a bundle in the graph. The graph may contain UNINSTALLED bundles that are
298
// currently still exporting packages.
299
BundleImpl[] graph = buildGraph( bundles );
300       // 2. Each bundle in the graph will be stopped as described in the Bundle.stop
301
// method.
302
//
303
// 3. Each bundle in the graph that is in the RESOLVED state is moved to the
304
// INSTALLED state. The effect of this step is that bundles in the graph are no
305
// long RESOLVED.
306
Vector activeBundles = new Vector();
307       for ( int i = 0; i < graph.length; i++ ) {
308          // determine if we should attempt to restart the bundle at the end of the
309
// refresh operation
310
if ( graph[ i ].getState() == graph[ i ].ACTIVE || graph[ i ].getState() == graph[ i ].STARTING ) {
311             activeBundles.addElement( graph[ i ] );
312          }
313
314          // ok to call stop on a bundle in any state, except uninstalled (which will
315
// cause an exception)
316
//
317
// special treatment for system bundle which should not be stopped
318
if ( graph[ i ].getState() != graph[ i ].UNINSTALLED ) {
319             try {
320                graph[ i ].stop( true );
321             }
322             catch ( Throwable e ) {
323                // For any exceptions that are thrown, a FrameworkEvent
324
// of type ERROR is broadcast, containing the exception.
325
cube.fireFrameworkEvent( FrameworkEvent.ERROR, cube.getBundle( 0 ), e );
326             }
327          }
328
329          // now all bundles in the graph should be uninstalled, installed, or resolved
330
//
331
// uninstalled bundles need to be unresolved so their exported packages are wiped
332
// installed bundle need not be resolved (they have no exported packages to remove
333
// and their pending imports are registered)
334
// resolved bundle need to be unresolved so their exported packages are wiped
335
//
336
// could be stopping... what do we do?
337
unresolveBundle( (BundleImpl)graph[ i ] );
338       }
339
340       // 4. Each bundle in the graph that is in the UNINSTALLED state is removed from
341
// the graph and is now completely removed from the framework.
342
cube.wipeUninstalledBundles( graph );
343
344       resolveBundles();
345
346       // 5. Each bundle in the graph that was in the ACTIVE state prior to step 2 is
347
// started as described in the Bundle.start method, causing all bundles required
348
// for the restart to be resolved. It is possible that, as a result of the previous
349
// steps, packages that were previously exported no longer are.
350
// Threfore, some bundles may be unresolvable until another bundle offering a
351
// compatible package for export has been installed in the Framework.
352
//
353
Bundle bundle;
354       for ( int i = 0; i < activeBundles.size(); i++ ) {
355          bundle = (Bundle)activeBundles.elementAt( i );
356          if ( bundle.getState() == bundle.RESOLVED ) {
357             try {
358                bundle.start();
359             }
360             catch ( Throwable e ) {
361                // For any exceptions that are thrown, a FrameworkEvent
362
// of type ERROR is broadcast, containing the exception.
363
cube.fireFrameworkEvent( FrameworkEvent.ERROR, cube.getBundle( 0 ), e );
364             }
365          }
366       }
367    }
368
369    // compute a graph of bundles starting with the specified ones. If no bundles
370
// are specified, compute a graph of bundles starting with previously updated
371
// or uninstalled ones. Any bundle that imports a package that is currently
372
// exported by a bundle in the graph is added to the graph. The graph is fully
373
// contructed when there is no bundles outside the graph that imports a package
374
// from a bundle in the graph. The graph may contain UNINSTALLED bundles that are
375
// currently still exporting packages.
376
private BundleImpl[] buildGraph( Bundle[] bundles ) {
377       // Start the graph
378
Vector graph = new Vector();
379       if ( bundles != null ) {
380          // if specified bundles not null, start graph with specified bundles
381
for ( int i = 0; i < bundles.length; i++ ) {
382             graph.addElement( bundles[ i ] );
383          }
384       }
385       else {
386          // if specified bundles null, start graph with bundles that were previously
387
// updated or uninstalled
388
// any bundle that exports a package and that has been updated or uninstalled
389
// can be found by traversing the list of exporetd packages
390
// and seeing which ones have their removePending flag set
391
// (we take care of bundles that do not export packages later)
392
Enumeration enum = exportedPackages.elements();
393          ExportedPackageImpl exportedPackage;
394          while ( enum.hasMoreElements() ) {
395             exportedPackage = (ExportedPackageImpl)enum.nextElement();
396             if ( exportedPackage.isRemovalPending() && graph.indexOf( exportedPackage.getExportingBundle() ) == -1 ) {
397                graph.addElement( exportedPackage.getExportingBundle() );
398             }
399          }
400          // we must also add to the graph any bundles that are uninstalled and that
401
// do not export packages. This is just so that these bundles can be included
402
// in the list of bundles to completely wipe from the framework
403
Bundle[] all = cube.getBundles();
404          for ( int i = 0; i < all.length; i++ ) {
405             if ( all[ i ].getState() == all[ i ].UNINSTALLED && graph.indexOf( all[ i ] ) == -1 ) {
406                graph.addElement( all[ i ] );
407             }
408          }
409          // we don't really care here about bundles that have been updated and that
410
// do not export packages. If do not import packages exported by bundles in the
411
// graph we don't need to do anything with those bundles. If on the other hand
412
// they do import packages exported by bundles in the graph, then they will
413
// be included in the graph later
414
}
415
416       // Build the graph
417
boolean done = false;
418       while ( !done ) {
419          // for each package exported by a bundle in the graph, see if there is an
420
// importing bundle that is not in the graph
421
Vector bundlesToAdd = new Vector();
422          for ( int i = 0; i < graph.size(); i++ ) {
423             Vector exports = getProposedExports( (Bundle)graph.elementAt( i ) );
424             for ( int j = 0; j < exports.size(); j++ ) {
425                PackageExport export = (PackageExport)exports.elementAt( j );
426                Enumeration enum = requiredImports.keys();
427                while ( enum.hasMoreElements() ) {
428                   Bundle bundle = (Bundle)enum.nextElement();
429                   Vector imports = getRequiredImports( bundle );
430                   for ( int k = 0; k < imports.size(); k++ ) {
431                      PackageImport packageImport = (PackageImport)imports.elementAt( k );
432                      if ( packageImport.getName().equals( export.getName() ) && graph.indexOf( bundle ) == -1 && bundlesToAdd.indexOf( bundle ) == -1 ) {
433                         bundlesToAdd.addElement( bundle );
434                      }
435                   }
436                }
437             }
438          }
439
440          if ( bundlesToAdd.size() > 0 ) {
441             for ( int i = 0; i < bundlesToAdd.size(); i++ ) {
442                graph.addElement( bundlesToAdd.elementAt( i ) );
443             }
444          }
445          else {
446             done = true;
447          }
448       }
449
450       BundleImpl[] ret = new BundleImpl[ graph.size() ];
451       graph.copyInto( ret );
452       return ret;
453    }
454
455    private synchronized void unresolveBundle( BundleImpl bundle ) {
456       // if the bundle is no longer resolved, it must not import packages
457
// also, if the bundle is no longer resolved, it must not export packages
458
Enumeration enum = exportedPackages.elements();
459       while ( enum.hasMoreElements() ) {
460          ExportedPackageImpl exportedPackage = (ExportedPackageImpl)enum.nextElement();
461          if ( exportedPackage.getExportingBundle().getBundleId() == bundle.getBundleId() ) {
462             // if the package is exported by the bundle itself, remove it from the
463
// list of exported bundles (unresolved bundles do not participate in
464
// package sharing), and set its stale flag
465
exportedPackage.setStale();
466             exportedPackages.remove( exportedPackage.getName() );
467          }
468          else {
469             // if the package is exported by another bundle, and the bundle being
470
// unresolve imports it, remove it from the list of importers of the package
471
// (unresolved bundles do not participate in package sharing)
472
//
473
// this call will have no effect if bundle is not an importer of
474
// exportedPackage (no need to check explicitly if bundle is an importer of
475
// the package)
476
exportedPackage.removeImportingBundle( bundle );
477          }
478       }
479
480       if ( bundle.getState() == bundle.RESOLVED ) {
481          bundle.setInstalled();
482       }
483    }
484
485 // *****************************************************************************
486
//
487
// private utility methods
488
//
489
// *****************************************************************************
490

491    private Vector getProposedExports( Bundle bundle ) {
492       if ( proposedExports.get( bundle ) == null ) {
493          proposedExports.put( bundle, new Vector() );
494       }
495       return (Vector)proposedExports.get( bundle );
496    }
497
498    private Vector getRequiredImports( Bundle bundle ) {
499       if ( requiredImports.get( bundle ) == null ) {
500          requiredImports.put( bundle, new Vector() );
501       }
502       return (Vector)requiredImports.get( bundle );
503    }
504
505    // Called by cube in the final stage of bundle installation or update, or during
506
// package refresh
507
// This is the time the list of installed bundles should be traversed to see if any
508
// can be resolved as a result of the new bundle installation or update.
509
//
510
// Resolving bundles is not a simple matter.
511
//
512
// We can have more than one pending export for a given package name.
513
// If the bundle that exports the package with highest version can be resolved,
514
// that should be the package that is used. But if that bundle cannot be resolved,
515
// we should try to see if any of the bundles exporting lower versions of the
516
// package can be resolved, in which case the lower package version will be used.
517
//
518
// To make things worse, we can have bundles that have cross package dependencies,
519
// meaning that some groups of bundles are either all resolved, or none of them is.
520
//
521
public synchronized void resolveBundles() {
522       // Determine the list of bundles that are installed (i.e. waiting to be resolved)
523
Bundle[] bundles = cube.getBundles();
524       Vector potentiallyResolvedBundles = new Vector();
525       for ( int i = 0; i < bundles.length; i++ ) {
526          if ( bundles[ i ].getState() == bundles[ i ].INSTALLED ) {
527             potentiallyResolvedBundles.addElement( bundles[ i ] );
528          }
529       }
530
531       boolean done = false;
532       Vector notResolvable;
533       while ( !done ) {
534          notResolvable = simulatePackageExportsFor( potentiallyResolvedBundles );
535          if ( notResolvable.size() == 0 ) {
536             done = true;
537          }
538          else {
539             for ( int i = 0; i < notResolvable.size(); i++ ) {
540                potentiallyResolvedBundles.removeElement( notResolvable.elementAt( i ) );
541             }
542          }
543       }
544
545       // At each iteration of the above loop, either notResolvable.size() == 0, in which
546
// case the loop is exited, or notResolvable.size() > 0, in which case we
547
// start again with a smaller potentiallyResolvedBundles vector.
548
// If potentiallyResolvedBundles.size() reaches zero, then the vector that will
549
// be returned from simulatePackageExportsFor() will have zero size and the
550
// loop will exit.
551
// Therefore, the loop must end in finite time.
552
//
553
// When the loop ends, the bundles in the potentiallyResolvedBundles vector are
554
// the bundles that can be resolved at this stage.
555

556       // Resolve the bundles that can be resolved at this stage
557
//
558
// First, handle exported packages
559
// create real package exports for all proposed exports (except the proposed
560
// exports with the same package name as a package that is already exported)
561
Hashtable toExport = new Hashtable();
562       for ( int i = 0; i < potentiallyResolvedBundles.size(); i++ ) {
563          Bundle bundle = (Bundle)potentiallyResolvedBundles.elementAt( i );
564          Vector proposedExports = getProposedExports( bundle );
565          for ( int j = 0; j < proposedExports.size(); j++ ) {
566             PackageExport proposedExport = (PackageExport)proposedExports.elementAt( j );
567             ExportedPackageImpl alreadyExported = (ExportedPackageImpl)exportedPackages.get( proposedExport.getName() );
568             boolean export = false;
569             if ( alreadyExported != null ) {
570                if ( !alreadyExported.isOpen() ) {
571                   // if a package with the same name is already exported but not yet open, we still
572
// have a chance to upgrade it before it gets opened
573
GoRoCoVersion proposedVersion = new GoRoCoVersion( proposedExport.getSpecificationVersion() );
574                   GoRoCoVersion alreadyExportedVersion = new GoRoCoVersion( alreadyExported.getSpecificationVersion() );
575                   if ( alreadyExportedVersion.isUndefined() && !proposedVersion.isUndefined() ) {
576                      export = true;
577                   }
578                   else {
579                      // if alreadyExportedVersion is not undefined and proposedVersion
580
// is undefined, do not export proposed version
581
// then alreadyExportedVersion is considered greater
582
// see implementation of GoRoCoVersion.compareTo()
583
if ( alreadyExportedVersion.compareTo( proposedVersion ) < 0 ) {
584                         export = true;
585                      }
586                   }
587                }
588             }
589             else {
590                PackageExport alreadyInList = (PackageExport)toExport.get( proposedExport.getName() );
591                if ( alreadyInList == null ) {
592                   export = true;
593                }
594                else {
595                   // if several proposed exports found with the same name, take the one
596
// with highest version
597
GoRoCoVersion version1 = new GoRoCoVersion( alreadyInList.getSpecificationVersion() );
598                   GoRoCoVersion version2 = new GoRoCoVersion( proposedExport.getSpecificationVersion() );
599                   // if either version1 or version2 (but not both) is undefined
600
// version1 is considered greater
601
// see implementation of GoRoCoVersion.compareTo()
602
if ( version1.compareTo( version2 ) < 0 ) {
603                      export = true;
604                   }
605                }
606             }
607             if ( export ) {
608                toExport.put( proposedExport.getName(), proposedExport );
609             }
610          }
611       }
612       Enumeration enum = toExport.elements();
613       while ( enum.hasMoreElements() ) {
614          // create real package export
615
PackageExport packageExport = (PackageExport)enum.nextElement();
616          ExportedPackageImpl exportedPackage = new ExportedPackageImpl( packageExport.getName(), packageExport.getSpecificationVersion(), packageExport.getExportingBundle(), packageExport.getClassLoader() );
617          ExportedPackageImpl unopenedExistingExportBeingUpgraded = (ExportedPackageImpl)exportedPackages.get( packageExport.getName() );
618          if ( unopenedExistingExportBeingUpgraded != null ) {
619             Bundle[] importingBundles = unopenedExistingExportBeingUpgraded.getImportingBundles();
620             for ( int i = 0; i < importingBundles.length; i++ ) {
621                exportedPackage.addImportingBundle( importingBundles[ i ] );
622             }
623             unopenedExistingExportBeingUpgraded.setStale();
624          }
625          exportedPackages.put( packageExport.getName(), exportedPackage );
626       }
627
628       // Second, handle imported packages
629
// transform required imports into real package imports
630
//
631
// Finally, set the bundle's state to resolved
632
for ( int i = 0; i < potentiallyResolvedBundles.size(); i++ ) {
633          BundleImpl bundle = (BundleImpl)potentiallyResolvedBundles.elementAt( i );
634          Vector requiredImports = getRequiredImports( bundle );
635          for ( int j = 0; j < requiredImports.size(); j++ ) {
636             PackageImport requiredImport = (PackageImport)requiredImports.elementAt( j );
637             // add bundle to the list of importers of the exported package
638
( (ExportedPackageImpl)exportedPackages.get( requiredImport.getName() ) ).addImportingBundle( bundle );
639          }
640          bundle.setResolved();
641       }
642    }
643
644    // Pretend that all the specified bundles can be resolved.
645
// Then, we get a simulated list of exported packages made up of all the already
646
// exported packages and the proposed exports of those bundles (except for the proposed
647
// exports that have a package name of a package that is already exported).
648
// See, in that situation, which bundles (if any) still miss packages in order to
649
// be resolved, and return the list of those packages.
650
//
651
// If the returned list is empty, then all the specified bundles can be resolved at
652
// this stage.
653
// If the returned list is not empty, then the bundles in the returned list definitely
654
// cannot be resolved at this stage, and there is a question mark over the other
655
// bundles in the specified list.
656
private Vector simulatePackageExportsFor( Vector bundles ) {
657       // Build simulated list of exported packages
658
Hashtable potentialExports = new Hashtable();
659       for ( int i = 0; i < bundles.size(); i++ ) {
660          Bundle bundle = (Bundle)bundles.elementAt( i );
661          Vector proposedExports = getProposedExports( bundle );
662          for ( int j = 0; j < proposedExports.size(); j++ ) {
663             PackageExport proposedExport = (PackageExport)proposedExports.elementAt( j );
664             ExportedPackageImpl alreadyExported = (ExportedPackageImpl)exportedPackages.get( proposedExport.getName() );
665             boolean addToPotentialExports = false;
666             if ( alreadyExported != null ) {
667                if ( !alreadyExported.isOpen() ) {
668                   // if a package with the same name is already exported but not yet open, we still
669
// have a chance to upgrade it before it gets opened
670
GoRoCoVersion proposedVersion = new GoRoCoVersion( proposedExport.getSpecificationVersion() );
671                   GoRoCoVersion alreadyExportedVersion = new GoRoCoVersion( alreadyExported.getSpecificationVersion() );
672                   if ( alreadyExportedVersion.isUndefined() && !proposedVersion.isUndefined() ) {
673                      addToPotentialExports = true;
674                   }
675                   else {
676                      // if alreadyExportedVersion is not undefined and proposedVersion
677
// is undefined, do not export proposed version
678
// then alreadyExportedVersion is considered greater
679
// see implementation of GoRoCoVersion.compareTo()
680
if ( alreadyExportedVersion.compareTo( proposedVersion ) < 0 ) {
681                         addToPotentialExports = true;
682                      }
683                   }
684                }
685             }
686             else {
687                PackageExport alreadyInList = (PackageExport)potentialExports.get( proposedExport.getName() );
688                if ( alreadyInList == null ) {
689                   addToPotentialExports = true;
690                }
691                else {
692                   // if we have several proposed exports with the same package name, use the
693
// package with the highest specification version
694
GoRoCoVersion version1 = new GoRoCoVersion( alreadyInList.getSpecificationVersion() );
695                   GoRoCoVersion version2 = new GoRoCoVersion( proposedExport.getSpecificationVersion() );
696                   // if either version1 or version2 (but not both) is undefined
697
// version1 is considered greater
698
// see implementation of GoRoCoVersion.compareTo()
699
if ( version1.compareTo( version2 ) < 0 ) {
700                      addToPotentialExports = true;
701                   }
702                }
703             }
704             if ( addToPotentialExports ) {
705                potentialExports.put( proposedExport.getName(), proposedExport );
706             }
707          }
708       }
709       // The simulated list of exported packages contains all the packages that are
710
// currently exported, as well as the potential exports
711
// The potential exports contain all the package exports proposed by the
712
// potentially resolved bundles (but only the highest version of the package
713
// if there are several proposed exports for a given package), except for exports
714
// of packages with the same name as a package that is already exported
715
//
716
// There is one exception which makes it possible for the potential exports to
717
// contain a package export for a package that is already exported: if the package
718
// that is already exported is not open (i.e. noone has loaded classes from that
719
// bundle) and if the proposed export has a greater version that the currently
720
// exported package.
721
Hashtable simulatedExportedPackages = (Hashtable)exportedPackages.clone();
722       Enumeration enum = potentialExports.elements();
723       while ( enum.hasMoreElements() ) {
724          PackageExport potentialExport = (PackageExport)enum.nextElement();
725          simulatedExportedPackages.put( potentialExport.getName(), potentialExport );
726       }
727
728       // Having obtained the simulated list of exported packages, see which bundles
729
// still cannot be resolved
730
Vector bundlesStillNotResolved = new Vector();
731       for ( int i = 0; i < bundles.size(); i++ ) {
732          Bundle bundle = (Bundle)bundles.elementAt( i );
733          Vector requiredImports = getRequiredImports( bundle );
734          for ( int j = 0; j < requiredImports.size(); j++ ) {
735             PackageImport required = (PackageImport)requiredImports.elementAt( j );
736             HasVersion available = (HasVersion)simulatedExportedPackages.get( required.getName() );
737             if ( available == null ) {
738 //System.out.println( bundle.getBundleId() + " not resolved - missing: " + required.getName() );
739
bundlesStillNotResolved.addElement( bundle );
740                break;
741             }
742             else {
743                GoRoCoVersion requiredVersion = new GoRoCoVersion( required.getSpecificationVersion() );
744                GoRoCoVersion availableVersion = new GoRoCoVersion( available.getSpecificationVersion() );
745                // if either available version or required version (but not both) is undefined
746
// available version is considered greater
747
// see implementation of GoRoCoVersion.compareTo()
748
if ( availableVersion.compareTo( requiredVersion ) < 0 ) {
749 //System.out.println( bundle.getBundleId() + " not resolved - " + required.getName() + " v" + reqyuredVersion.toString() + " required but v" + availableVersion.toString() + " available" );
750
bundlesStillNotResolved.addElement( bundle );
751                   break;
752                }
753             }
754          }
755       }
756
757       // The bundles in bundlesStillNotResolved can definitely not be resolved at this
758
// stage
759
return bundlesStillNotResolved;
760    }
761
762 }
763
Popular Tags