Source code: org/eclipse/osgi/framework/internal/core/PackageAdminImpl.java
1 /*******************************************************************************
2 * Copyright (c) 2003, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11
12 package org.eclipse.osgi.framework.internal.core;
13
14 import java.io.IOException;
15 import java.util.*;
16 import org.eclipse.osgi.framework.debug.Debug;
17 import org.eclipse.osgi.framework.debug.DebugOptions;
18 import org.eclipse.osgi.framework.util.SecureAction;
19 import org.eclipse.osgi.service.resolver.*;
20 import org.osgi.framework.*;
21 import org.osgi.service.packageadmin.*;
22
23 /**
24 * PackageAdmin service for the OSGi specification.
25 *
26 * Framework service which allows bundle programmers to inspect the packages
27 * exported in the framework and eagerly update or uninstall bundles.
28 *
29 * If present, there will only be a single instance of this service
30 * registered in the framework.
31 *
32 * <p> The term <i>exported package</i> (and the corresponding interface
33 * {@link ExportedPackage}) refers to a package that has actually been
34 * exported (as opposed to one that is available for export).
35 *
36 * <p> Note that the information about exported packages returned by this
37 * service is valid only until the next time {@link #refreshPackages(org.osgi.framework.Bundle[])} is
38 * called.
39 * If an ExportedPackage becomes stale, (that is, the package it references
40 * has been updated or removed as a result of calling
41 * PackageAdmin.refreshPackages()),
42 * its getName() and getSpecificationVersion() continue to return their
43 * old values, isRemovalPending() returns true, and getExportingBundle()
44 * and getImportingBundles() return null.
45 */
46 public class PackageAdminImpl implements PackageAdmin {
47
48 /** framework object */
49 protected Framework framework;
50
51 /** BundleLoaders that are pending removal. Value is BundleLoader */
52 protected Vector removalPending;
53
54 protected KeyedHashSet exportedPackages;
55 protected KeyedHashSet exportedBundles;
56
57 private long cumulativeTime;
58
59 private static boolean checkServiceClassSource = true;
60 private static boolean restrictServiceClasses = false;
61
62 /**
63 * Constructor.
64 *
65 * @param framework Framework object.
66 */
67 protected PackageAdminImpl(Framework framework) {
68 this.framework = framework;
69 }
70
71 protected void initialize() {
72 checkServiceClassSource = Boolean.valueOf(System.getProperty(Constants.OSGI_CHECKSERVICECLASSSOURCE,"true")).booleanValue(); //$NON-NLS-1$
73 restrictServiceClasses = Boolean.valueOf(System.getProperty(Constants.OSGI_RESTRICTSERVICECLASSES,"false")).booleanValue(); //$NON-NLS-1$
74 removalPending = new Vector(10, 10);
75
76 State state = framework.adaptor.getState();
77 if (!state.isResolved()) {
78 state.resolve(false);
79 }
80 exportedPackages = new KeyedHashSet(false);
81 exportedBundles = new KeyedHashSet(false);
82 }
83
84 private KeyedHashSet getExportedPackages(KeyedHashSet packageSet) {
85 State state = framework.adaptor.getState();
86 PackageSpecification[] packageSpecs = state.getExportedPackages();
87
88 for (int i = 0; i < packageSpecs.length; i++) {
89 BundleDescription bundleSpec = packageSpecs[i].getSupplier();
90 if (bundleSpec == null)
91 continue;
92 AbstractBundle bundle = framework.getBundle(bundleSpec.getBundleId());
93 if (bundle == null) {
94 BundleException be = new BundleException(Msg.formatter.getString("BUNDLE_NOT_IN_FRAMEWORK", bundleSpec)); //$NON-NLS-1$
95 framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, be);
96 continue;
97 }
98 // check export permissions before getting the host;
99 // we want to check the permissions of the fragment
100 if (!bundle.checkExportPackagePermission(packageSpecs[i].getName()))
101 continue;
102 // if we have a host then get the host bundle
103 HostSpecification hostSpec = bundleSpec.getHost();
104 if (hostSpec != null) {
105 bundleSpec = hostSpec.getSupplier();
106 if (bundleSpec == null)
107 continue;
108 bundle = framework.getBundle(bundleSpec.getBundleId());
109 if (bundle == null) {
110 BundleException be = new BundleException(Msg.formatter.getString("BUNDLE_NOT_IN_FRAMEWORK", bundleSpec)); //$NON-NLS-1$
111 framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, be);
112 continue;
113 }
114 }
115
116 if (bundle.isResolved() && bundle instanceof BundleHost) {
117 ExportedPackageImpl packagesource = new ExportedPackageImpl(packageSpecs[i], ((BundleHost) bundle).getLoaderProxy());
118 packageSet.add(packagesource);
119 }
120 }
121 return packageSet;
122 }
123
124 private KeyedHashSet getExportedBundles(KeyedHashSet bundleSet) {
125 State state = framework.adaptor.getState();
126 BundleDescription[] bundleDescripts = state.getResolvedBundles();
127 for (int i = 0; i < bundleDescripts.length; i++) {
128 BundleDescription bundledes = bundleDescripts[i];
129 AbstractBundle bundle = framework.getBundle(bundledes.getBundleId());
130 if (bundle != null && bundle.isResolved() && bundle.getSymbolicName() != null && bundle instanceof BundleHost && bundle.checkProvideBundlePermission(bundle.getSymbolicName())) {
131 BundleLoaderProxy loaderProxy = ((BundleHost) bundle).getLoaderProxy();
132 bundleSet.add(loaderProxy);
133 }
134 }
135 return bundleSet;
136 }
137
138 protected void cleanup() { //This is only called when the framework is shutting down
139 removalPending = null;
140 exportedPackages = null;
141 exportedBundles = null;
142 }
143
144 protected void addRemovalPending(BundleLoaderProxy loaderProxy) {
145 removalPending.addElement(loaderProxy);
146 }
147
148 protected void deleteRemovalPending(BundleLoaderProxy loaderProxy) throws BundleException {
149
150 boolean exporting = loaderProxy.inUse();
151 if (exporting) {
152 /* Reaching here is an internal error */
153 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
154 Debug.println("BundleLoader.unexportPackager returned true! " + loaderProxy); //$NON-NLS-1$
155 Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
156 }
157 throw new BundleException(Msg.formatter.getString("OSGI_INTERNAL_ERROR")); //$NON-NLS-1$
158 }
159 unexportResources(loaderProxy);
160 BundleLoader loader = loaderProxy.getBundleLoader();
161 loader.clear();
162 loader.close();
163 removalPending.remove(loaderProxy);
164 }
165
166 /**
167 * Gets the packages exported by the specified bundle.
168 *
169 * @param bundle The bundle whose exported packages are to be returned,
170 * or <tt>null</tt> if all the packages currently
171 * exported in the framework are to be returned. If the
172 * specified bundle is the system bundle (that is, the
173 * bundle with id 0), this method returns all the packages
174 * on the system classpath whose name does not start with
175 * "java.". In an environment where the exhaustive list
176 * of packages on the system classpath is not known in
177 * advance, this method will return all currently known
178 * packages on the system classpath, that is, all packages
179 * on the system classpath that contains one or more classes
180 * that have been loaded.
181 *
182 * @return The array of packages exported by the specified bundle,
183 * or <tt>null</tt> if the specified bundle has not exported any packages.
184 */
185 public org.osgi.service.packageadmin.ExportedPackage[] getExportedPackages(org.osgi.framework.Bundle bundle) {
186 // need to make sure the dependacies are marked before this call.
187 synchronized (framework.bundles) {
188 framework.bundles.markDependancies();
189
190 KeyedElement[] elements = exportedPackages.elements();
191 if (bundle != null) {
192 Vector result = new Vector();
193 for (int i = 0; i < elements.length; i++) {
194 ExportedPackageImpl pkgElement = (ExportedPackageImpl) elements[i];
195 if (pkgElement.supplier.getBundleHost() == bundle) {
196 result.add(pkgElement);
197 }
198 }
199 if (result.size() == 0) {
200 return null;
201 }
202 ExportedPackageImpl[] pkgElements = new ExportedPackageImpl[result.size()];
203 return (ExportedPackage[]) result.toArray(pkgElements);
204 } else {
205 if (elements.length == 0) {
206 return null;
207 }
208 ExportedPackageImpl[] pkgElements = new ExportedPackageImpl[elements.length];
209 System.arraycopy(elements, 0, pkgElements, 0, pkgElements.length);
210 return pkgElements;
211 }
212 }
213 }
214
215 /**
216 * Gets the ExportedPackage with the specified package name. All exported
217 * packages
218 * will be checked for the specified name. In an environment where the
219 * exhaustive list of packages on the system classpath is not known in
220 * advance, this method attempts to see if the named package is on the
221 * system classpath.
222 * This
223 * means that this method may discover an ExportedPackage that was
224 * not present in the list returned by <tt>getExportedPackages()</tt>.
225 *
226 * @param packageName The name of the exported package to be returned.
227 *
228 * @return The exported package with the specified name, or <tt>null</tt>
229 * if no expored package with that name exists.
230 */
231 public org.osgi.service.packageadmin.ExportedPackage getExportedPackage(String packageName) {
232 // need to make sure the dependacies are marked before this call.
233 synchronized (framework.bundles) {
234 framework.bundles.markDependancies();
235 return (ExportedPackageImpl) exportedPackages.getByKey(packageName);
236 }
237 }
238
239 /**
240 * Forces the update (replacement) or removal of packages exported by
241 * the specified bundles.
242 *
243 * <p> If no bundles are specified, this method will update or remove any
244 * packages exported by any bundles that were previously updated or
245 * uninstalled. The technique by which this is accomplished
246 * may vary among different framework implementations. One permissible
247 * implementation is to stop and restart the Framework.
248 *
249 * <p> This method returns to the caller immediately and then performs the
250 * following steps in its own thread:
251 *
252 * <ol>
253 * <p>
254 * <li> Compute a graph of bundles starting with the specified ones. If no
255 * bundles are specified, compute a graph of bundles starting with
256 * previously updated or uninstalled ones.
257 * Any bundle that imports a package that is currently exported
258 * by a bundle in the graph is added to the graph. The graph is fully
259 * constructed when there is no bundle outside the graph that imports a
260 * package from a bundle in the graph. The graph may contain
261 * <tt>UNINSTALLED</tt> bundles that are currently still
262 * exporting packages.
263 *
264 * <p>
265 * <li> Each bundle in the graph will be stopped as described in the
266 * <tt>Bundle.stop</tt> method.
267 *
268 * <p>
269 * <li> Each bundle in the graph that is in the
270 * <tt>RESOLVED</tt> state is moved
271 * to the <tt>INSTALLED</tt> state.
272 * The effect of this step is that bundles in the graph are no longer
273 * <tt>RESOLVED</tt>.
274 *
275 * <p>
276 * <li> Each bundle in the graph that is in the UNINSTALLED state is
277 * removed from the graph and is now completely removed from the framework.
278 *
279 * <p>
280 * <li> Each bundle in the graph that was in the
281 * <tt>ACTIVE</tt> state prior to Step 2 is started as
282 * described in the <tt>Bundle.start</tt> method, causing all
283 * bundles required for the restart to be resolved.
284 * It is possible that, as a
285 * result of the previous steps, packages that were
286 * previously exported no longer are. Therefore, some bundles
287 * may be unresolvable until another bundle
288 * offering a compatible package for export has been installed in the
289 * framework.
290 * </ol>
291 *
292 * <p> For any exceptions that are thrown during any of these steps, a
293 * <tt>FrameworkEvent</tt> of type <tt>ERROR</tt> is
294 * broadcast, containing the exception.
295 *
296 * @param input the bundles whose exported packages are to be updated or
297 * removed,
298 * or <tt>null</tt> for all previously updated or uninstalled bundles.
299 *
300 * @exception SecurityException if the caller does not have the
301 * <tt>AdminPermission</tt> and the Java runtime environment supports
302 * permissions.
303 */
304 public void refreshPackages(org.osgi.framework.Bundle[] input) {
305 framework.checkAdminPermission();
306
307 AbstractBundle[] copy = null;
308 if (input != null) {
309 synchronized (input) {
310 int size = input.length;
311
312 copy = new AbstractBundle[size];
313
314 System.arraycopy(input, 0, copy, 0, size);
315 }
316 }
317
318 final AbstractBundle[] bundles = copy;
319 Thread refresh = SecureAction.createThread(new Runnable() {
320 public void run() {
321 refreshPackages(bundles);
322 }
323 }, "Refresh Packages"); //$NON-NLS-1$
324
325 refresh.start();
326 }
327
328 /**
329 * Worker routine called on a seperate thread to perform the actual work.
330 *
331 * @param refresh the list of bundles to refresh
332 */
333 protected void refreshPackages(AbstractBundle[] refresh) {
334 try {
335 Vector graph = null;
336 synchronized (framework.bundles) {
337 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
338 Debug.println("refreshPackages: Initialize graph"); //$NON-NLS-1$
339 }
340 // make sure the dependencies are marked first
341 framework.bundles.markDependancies();
342
343 graph = computeAffectedBundles(refresh);
344
345 // get the state.
346 State state = framework.adaptor.getState();
347 // resolve the state.
348 state.resolve(false);
349
350 // process the delta. This will set the state of all
351 // the bundles in the graph.
352 processDelta(graph);
353 }
354 /*
355 * Resume the suspended bundles outside of the synchronization block.
356 * This will cause the bundles to be resolved using the most up-to-date
357 * generations of the bundles.
358 */
359 resumeBundles(graph);
360
361 } finally {
362 framework.publishFrameworkEvent(FrameworkEvent.PACKAGES_REFRESHED, framework.systemBundle, null);
363 }
364
365 }
366
367 private void resumeBundles(Vector graph) {
368
369 AbstractBundle[] refresh = new AbstractBundle[graph.size()];
370 boolean[] previouslyResolved = new boolean[graph.size()];
371 graph.copyInto(refresh);
372 Util.sort(refresh, 0, graph.size());
373 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
374 Debug.println("refreshPackages: restart the bundles"); //$NON-NLS-1$
375 }
376 for (int i = 0; i < refresh.length; i++) {
377 AbstractBundle bundle = (AbstractBundle) refresh[i];
378 if (bundle.isResolved())
379 framework.resumeBundle(bundle);
380 }
381 }
382
383 private void processDelta(Vector graph) {
384 AbstractBundle[] refresh = new AbstractBundle[graph.size()];
385 boolean[] previouslyResolved = new boolean[graph.size()];
386 graph.copyInto(refresh);
387 Util.sort(refresh, 0, graph.size());
388
389 Vector notify = new Vector();
390 try {
391 try {
392 /*
393 * Suspend each bundle and grab its state change lock.
394 */
395 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
396 Debug.println("refreshPackages: Suspend each bundle and acquire its state change lock"); //$NON-NLS-1$
397 }
398 for (int i = refresh.length - 1; i >= 0; i--) {
399 AbstractBundle changedBundle = refresh[i];
400 previouslyResolved[i] = changedBundle.isResolved();
401 if (changedBundle.isActive() && !changedBundle.isFragment()) {
402 boolean suspended = framework.suspendBundle(changedBundle, true);
403 if (!suspended) {
404 throw new BundleException(Msg.formatter.getString("BUNDLE_STATE_CHANGE_EXCEPTION")); //$NON-NLS-1$
405 }
406 } else {
407 changedBundle.beginStateChange();
408 }
409
410 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
411 if (changedBundle.stateChanging == null) {
412 Debug.println("Bundle state change lock is clear! " + changedBundle); //$NON-NLS-1$
413 Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
414 }
415 }
416 }
417 /*
418 * Refresh the bundles which will unexport the packages.
419 * This will move RESOLVED bundles to the INSTALLED state.
420 */
421 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
422 Debug.println("refreshPackages: refresh the bundles"); //$NON-NLS-1$
423 }
424 /*
425 * Unimport detached BundleLoaders for bundles in the graph.
426 */
427 for (int i = removalPending.size() - 1; i >= 0; i--) {
428 BundleLoaderProxy loaderProxy = (BundleLoaderProxy) removalPending.elementAt(i);
429
430 if (graph.contains(loaderProxy.getBundleHost())) {
431 framework.bundles.unMarkDependancies(loaderProxy);
432 }
433 }
434
435 for (int i = 0; i < refresh.length; i++) {
436 AbstractBundle changedBundle = refresh[i];
437 changedBundle.refresh();
438 // send out unresolved events
439 if (previouslyResolved[i])
440 framework.publishBundleEvent(BundleEvent.UNRESOLVED, changedBundle);
441 }
442
443 /*
444 * Cleanup detached BundleLoaders for bundles in the graph.
445 */
446 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
447 Debug.println("refreshPackages: unexport the removal pending packages"); //$NON-NLS-1$
448 }
449 for (int i = removalPending.size() - 1; i >= 0; i--) {
450 BundleLoaderProxy loaderProxy = (BundleLoaderProxy) removalPending.elementAt(i);
451 AbstractBundle removedBundle = loaderProxy.getBundleHost();
452
453 if (graph.contains(removedBundle)) {
454 deleteRemovalPending(loaderProxy);
455 }
456 }
457
458 // set the resolved bundles state
459 List allBundles = framework.bundles.getBundles();
460 int size = allBundles.size();
461 for (int i = 0; i < size; i++) {
462 AbstractBundle bundle = (AbstractBundle) allBundles.get(i);
463 if (bundle.isResolved())
464 continue;
465 BundleDescription bundleDes = bundle.getBundleDescription();
466 if (bundleDes != null) {
467 if (bundleDes.isResolved()) {
468 if (bundle.isFragment()) {
469 BundleHost host = (BundleHost) framework.getBundle(bundleDes.getHost().getSupplier().getBundleId());
470 if (((BundleFragment) bundle).setHost(host)) {
471 bundle.resolve(bundleDes.isSingleton());
472 }
473 } else {
474 bundle.resolve(bundleDes.isSingleton());
475 }
476 if (bundle.isResolved()) {
477 notify.addElement(bundle);
478 }
479 }
480 }
481 }
482
483 // update the exported package and bundle lists.
484 exportedPackages = getExportedPackages(exportedPackages);
485 exportedBundles = getExportedBundles(exportedBundles);
486 } finally {
487 /*
488 * Release the state change locks.
489 */
490 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
491 Debug.println("refreshPackages: release the state change locks"); //$NON-NLS-1$
492 }
493 for (int i = 0; i < refresh.length; i++) {
494 AbstractBundle changedBundle = refresh[i];
495 changedBundle.completeStateChange();
496 }
497 }
498 /*
499 * Take this opportunity to clean up the adaptor storage.
500 */
501 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
502 Debug.println("refreshPackages: clean up adaptor storage"); //$NON-NLS-1$
503 }
504 try {
505 framework.adaptor.compactStorage();
506 } catch (IOException e) {
507 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
508 Debug.println("refreshPackages exception: " + e.getMessage()); //$NON-NLS-1$
509 Debug.printStackTrace(e);
510 }
511 framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, new BundleException(Msg.formatter.getString("BUNDLE_REFRESH_FAILURE"), e)); //$NON-NLS-1$
512 }
513 } catch (BundleException e) {
514 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
515 Debug.println("refreshPackages exception: " + e.getMessage()); //$NON-NLS-1$
516 Debug.printStackTrace(e.getNestedException());
517 }
518 framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, new BundleException(Msg.formatter.getString("BUNDLE_REFRESH_FAILURE"), e)); //$NON-NLS-1$
519 }
520
521 // send out any resolved/unresolved events
522 for (int i = 0; i < notify.size(); i++) {
523 AbstractBundle changedBundle = (AbstractBundle) notify.elementAt(i);
524 framework.publishBundleEvent(changedBundle.isResolved() ? BundleEvent.RESOLVED : BundleEvent.UNRESOLVED, changedBundle);
525 }
526
527 }
528
529 private void unresolvePermissions(Vector bundles, Hashtable packages) {
530 /*
531 * All bundles must be notified of the unexported packages so that
532 * they may unresolve permissions if necessary.
533 * This done after resolve bundles so that unresolved permissions
534 * can be immediately resolved.
535 */
536 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
537 Debug.println("refreshPackages: unresolve permissions"); //$NON-NLS-1$
538 }
539 int size = bundles.size();
540 for (int i = 0; i < size; i++) {
541 AbstractBundle bundle = (AbstractBundle) bundles.elementAt(i);
542 bundle.unresolvePermissions(packages);
543 }
544 }
545
546 private Vector computeAffectedBundles(AbstractBundle[] refresh) {
547 Vector graph = new Vector();
548
549 if (refresh == null) {
550 int size = removalPending.size();
551 for (int i = 0; i < size; i++) {
552 BundleLoaderProxy loaderProxy = (BundleLoaderProxy) removalPending.elementAt(i);
553 AbstractBundle bundle = loaderProxy.getBundleHost();
554 if (!graph.contains(bundle)) {
555 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
556 Debug.println(" refresh: " + bundle); //$NON-NLS-1$
557 }
558 graph.addElement(bundle);
559
560 // add in any dependents of the removal pending loader.
561 AbstractBundle[] dependents = loaderProxy.getDependentBundles();
562 for (int j = 0; j < dependents.length; j++) {
563 if (!graph.contains(dependents[j])) {
564 graph.addElement(dependents[j]);
565 }
566 }
567 }
568 }
569 } else {
570 for (int i = 0; i < refresh.length; i++) {
571 AbstractBundle bundle = refresh[i];
572 if (bundle == framework.systemBundle) {
573 continue;
574 }
575 if (bundle.isFragment()) {
576 // if it is a fragment then put the host in the graph
577 BundleHost host = (BundleHost) bundle.getHost();
578 if (host != null) {
579 if (!graph.contains(host)) {
580 graph.addElement(host);
581 }
582 }
583 }
584
585 if (!graph.contains(bundle)) {
586 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
587 Debug.println(" refresh: " + bundle); //$NON-NLS-1$
588 }
589 graph.addElement(bundle);
590 }
591 }
592 }
593
594 /*
595 * If there is nothing to do, then return.
596 */
597 if (graph.size() == 0) {
598 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
599 Debug.println("refreshPackages: Empty graph"); //$NON-NLS-1$
600 }
601
602 return graph;
603 }
604
605 /*
606 * Complete graph.
607 */
608 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
609 Debug.println("refreshPackages: Complete graph"); //$NON-NLS-1$
610 }
611
612 boolean changed;
613 do {
614 changed = false;
615 int size = graph.size();
616 for (int i = size - 1; i >= 0; i--) {
617 AbstractBundle bundle = (AbstractBundle) graph.elementAt(i);
618 if (!bundle.isFragment()) {
619 BundleLoaderProxy loaderProxy = ((BundleHost) bundle).getLoaderProxy();
620 if (loaderProxy != null) {
621 // add any dependents
622 AbstractBundle[] dependents = loaderProxy.getDependentBundles();
623 for (int j = 0; j < dependents.length; j++) {
624 if (!graph.contains(dependents[j])) {
625 graph.addElement(dependents[j]);
626 changed = true;
627 }
628 }
629 }
630 // add in any fragments
631 org.osgi.framework.Bundle[] frags = bundle.getFragments();
632 if (frags != null) {
633 for (int j = 0; j < frags.length; j++) {
634 if (!graph.contains(frags[j])) {
635 graph.addElement(frags[j]);
636 changed = true;
637 }
638 }
639 }
640 } else {
641 // add in the host.
642 AbstractBundle host = (AbstractBundle) bundle.getHost();
643 if (host != null) {
644 if (!graph.contains(host)) {
645 graph.addElement(host);
646 changed = true;
647 }
648 }
649 }
650 // add in any singleton bundles if needed
651 AbstractBundle[] sameNames = framework.bundles.getBundles(bundle.getSymbolicName());
652 if (sameNames != null && sameNames.length > 1) {
653 for (int j = 0; j < sameNames.length; j++)
654 if (sameNames[j] != bundle && sameNames[j].isSingleton() && !graph.contains(sameNames[j])) {
655 graph.addElement(sameNames[j]);
656 changed = true;
657 }
658 }
659 }
660
661 // look for the bundles in removalPending list
662 // we always add removalPending dependants here even if
663 // the removalpending bundle was not in the original list
664 for (int i = removalPending.size() - 1; i >= 0; i--) {
665 BundleLoaderProxy removedLoaderProxy = (BundleLoaderProxy) removalPending.elementAt(i);
666 AbstractBundle removedBundle = removedLoaderProxy.getBundleHost();
667 if (!graph.contains(removedBundle)) {
668 graph.addElement(removedBundle);
669 changed = true;
670 }
671 AbstractBundle[] dependents = removedLoaderProxy.getDependentBundles();
672 for (int k = 0; k < dependents.length; k++) {
673 if (!graph.contains(dependents[k])) {
674 graph.addElement(dependents[k]);
675 changed = true;
676 }
677 }
678 }
679 } while (changed);
680
681 return graph;
682 }
683
684 /**
685 * Sets all the bundles in the state that are resolved to the resolved
686 * state. This should only be called when the framework is launching.
687 *
688 */
689 protected void setResolvedBundles() {
690 State state = framework.adaptor.getState();
691 BundleDescription[] descriptions = state.getBundles();
692 for (int i = 0; i < descriptions.length; i++) {
693 long bundleId = descriptions[i].getBundleId();
694 AbstractBundle bundle = framework.getBundle(bundleId);
695 if (bundle == null) {
696 BundleException be = new BundleException(Msg.formatter.getString("BUNDLE_NOT_IN_FRAMEWORK", descriptions[i])); //$NON-NLS-1$
697 framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, be);
698 }
699 if (bundle != framework.systemBundle) {
700 if (descriptions[i].isResolved()) {
701 if (bundle.isFragment()) {
702 BundleHost host = (BundleHost) framework.getBundle(descriptions[i].getHost().getSupplier().getBundleId());
703 if (((BundleFragment) bundle).setHost(host)) {
704 bundle.resolve(descriptions[i].isSingleton());
705 }
706 } else {
707 bundle.resolve(descriptions[i].isSingleton());
708 }
709 }
710 }
711 }
712 exportedPackages = getExportedPackages(exportedPackages);
713 exportedBundles = getExportedBundles(exportedBundles);
714 }
715
716 /**
717 * A permissible implementation of this method is to attempt to
718 * resolve all unresolved bundles installed in the framework.
719 * That is what this method does.
720 * @param bundles the set of bundles to attempt to resolve.
721 */
722 public boolean resolveBundles(org.osgi.framework.Bundle[] bundles) {
723 resolveBundles();
724 if (bundles == null)
725 synchronized (framework.bundles) {
726 List bundleList = framework.bundles.getBundles();
727 bundles = (Bundle[]) bundleList.toArray(new AbstractBundle[bundleList.size()]);
728 }
729 for (int i = 0; i < bundles.length; i++)
730 if (!((AbstractBundle) bundles[i]).isResolved())
731 return false;
732
733 return true;
734 }
735
736 /**
737 * Attempt to resolve all unresolved bundles. When this method returns
738 * all bundles are resolved that can be resolved. A resolved bundle
739 * has exported and imported packages. An unresolved bundle neither
740 * exports nor imports packages.
741 *
742 */
743 protected void resolveBundles() {
744 long start = 0;
745 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN_TIMING)
746 start = System.currentTimeMillis();
747 /*
748 * Resolve the bundles. This will make there exported packages available.
749 */
750 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
751 Debug.println("refreshBundles: resolve bundles"); //$NON-NLS-1$
752 }
753
754 Vector notify = new Vector();
755 synchronized (framework.bundles) {
756 boolean resolveNeeded = false;
757 List allBundles = framework.bundles.getBundles();
758 int size = allBundles.size();
759
760 // first check to see if there is anything to resolve
761 for (int i = 0; i < size; i++) {
762 if (!((AbstractBundle) allBundles.get(i)).isResolved())
763 resolveNeeded = true;
764 }
765 if (!resolveNeeded)
766 return;
767
768 // get the state and resolve it.
769 framework.adaptor.getState().resolve(false);
770 for (int i = 0; i < size; i++) {
771 AbstractBundle bundle = (AbstractBundle) allBundles.get(i);
772 if (bundle.isResolved() || bundle == framework.systemBundle)
773 continue;
774
775 BundleDescription changedBundleDes = bundle.getBundleDescription();
776 if (changedBundleDes == null) {
777 framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundle, new BundleException(Msg.formatter.getString("BUNDLE_NOT_IN_STATE", bundle.getLocation()))); //$NON-NLS-1$
778 }
779
780 if (changedBundleDes.isResolved()) {
781 if (bundle.isFragment()) {
782 BundleHost host = (BundleHost) framework.getBundle(changedBundleDes.getHost().getSupplier().getBundleId());
783 if (((BundleFragment) bundle).setHost(host)) {
784 bundle.resolve(changedBundleDes.isSingleton());
785 }
786 } else {
787 bundle.resolve(changedBundleDes.isSingleton());
788 }
789 if (bundle.isResolved()) {
790 notify.add(bundle);
791 }
792 }
793 }
794
795 // update the exported package and bundle lists.
796 exportedPackages = getExportedPackages(exportedPackages);
797 exportedBundles = getExportedBundles(exportedBundles);
798 }
799 for (int i = 0; i < notify.size(); i++) {
800 AbstractBundle bundle = (AbstractBundle) notify.elementAt(i);
801 if (bundle != null) {
802 framework.publishBundleEvent(bundle.isResolved() ? BundleEvent.RESOLVED : BundleEvent.UNRESOLVED, bundle);
803 }
804 }
805 if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN_TIMING) {
806 cumulativeTime = cumulativeTime + System.currentTimeMillis() - start;
807 DebugOptions.getDefault().setOption(Debug.OPTION_DEBUG_PACKAGEADMIN_TIMING + "/value", Long.toString(cumulativeTime)); //$NON-NLS-1$
808 }
809 }
810
811 protected void unexportResources(BundleLoaderProxy proxy) {
812 KeyedElement[] bundles = exportedBundles.elements();
813 for (int i = 0; i < bundles.length; i++) {
814 BundleLoaderProxy loaderProxy = (BundleLoaderProxy) bundles[i];
815 if (loaderProxy == proxy) {
816 exportedBundles.remove(proxy);
817 }
818 }
819
820 KeyedElement[] packages = exportedPackages.elements();
821 for (int i = 0; i < packages.length; i++) {
822 PackageSource source = (PackageSource) packages[i];
823 BundleLoaderProxy sourceProxy = source.getSupplier();
824 if (sourceProxy == proxy) {
825 exportedPackages.remove(source);
826 }
827 }
828 // make the proxy stale
829 proxy.setStale();
830 }
831
832 protected BundleDescription[] getBundleDescriptions(Vector graph) {
833 ArrayList result = new ArrayList();
834 int size = graph.size();
835 for (int i = 0; i < size; i++) {
836 AbstractBundle bundle = (AbstractBundle) graph.elementAt(i);
837 BundleDescription bundleDes = bundle.getBundleDescription();
838 if (bundleDes != null) {
839 result.add(bundleDes);
840 }
841 }
842 return (BundleDescription[]) result.toArray(new BundleDescription[result.size()]);
843 }
844
845 public ProvidingBundle[] getProvidingBundles(String symbolicName) {
846 if (exportedBundles == null || exportedBundles.size() == 0)
847 return null;
848
849 // need to make sure the dependacies are marked before this call.
850 framework.bundles.markDependancies();
851
852 KeyedElement[] allSymbolicBundles = exportedBundles.elements();
853 if (symbolicName == null) {
854 if (allSymbolicBundles.length == 0) {
855 return null;
856 }
857 ProvidingBundle[] result = new ProvidingBundle[allSymbolicBundles.length];
858 System.arraycopy(allSymbolicBundles, 0, result, 0, result.length);
859 return result;
860 } else {
861 ArrayList result = new ArrayList();
862 for (int i = 0; i < allSymbolicBundles.length; i++) {
863 ProvidingBundle symBundle = (ProvidingBundle) allSymbolicBundles[i];
864 if (symBundle.getSymbolicName().equals(symbolicName))
865 result.add(symBundle);
866 }
867 return (ProvidingBundle[]) result.toArray(new ProvidingBundle[result.size()]);
868 }
869 }
870
871 public org.osgi.framework.Bundle[] getBundles(String symbolicName, String versionRange) {
872 if (symbolicName == null) {
873 throw new IllegalArgumentException();
874 }
875 AbstractBundle bundles[] = framework.getBundleBySymbolicName(symbolicName);
876 if (bundles == null)
877 return null;
878
879 if (versionRange == null) {
880 AbstractBundle[] result = new AbstractBundle[bundles.length];
881 System.arraycopy(bundles, 0, result, 0, result.length);
882 return result;
883 }
884
885 // This code depends on the array of bundles being in descending
886 // version order.
887 ArrayList result = new ArrayList(bundles.length);
888 VersionRange range = new VersionRange(versionRange);
889 for (int i = 0; i < bundles.length; i++) {
890 if (range.isIncluded(bundles[i].getVersion())) {
891 result.add(bundles[i]);
892 }
893 }
894
895 if (result.size() == 0)
896 return null;
897 else
898 return (AbstractBundle[]) result.toArray(new AbstractBundle[result.size()]);
899
900 }
901
902 public org.osgi.framework.Bundle[] getFragments(org.osgi.framework.Bundle bundle) {
903 return ((AbstractBundle) bundle).getFragments();
904 }
905
906 public org.osgi.framework.Bundle[] getHosts(org.osgi.framework.Bundle bundle) {
907 org.osgi.framework.Bundle host = ((AbstractBundle) bundle).getHost();
908 if (host == null)
909 return null;
910 else
911 return new org.osgi.framework.Bundle[] {host};
912 }
913
914 public int getBundleType(org.osgi.framework.Bundle bundle) {
915 return ((AbstractBundle) bundle).isFragment() ? PackageAdminImpl.BUNDLE_TYPE_FRAGMENT : 0;
916 }
917
918 protected Class loadServiceClass(String className, AbstractBundle bundle) {
919 try {
920 // first try the PARENT class space
921 return framework.adaptor.getBundleClassLoaderParent().loadClass(className);
922 } catch (ClassNotFoundException e) {
923 // do nothing; try exported packages
924 }
925 // try exported packages; SERVICE class space
926 String pkgname = BundleLoader.getPackageName(className);
927 if (pkgname != null) {
928 PackageSource exporter = (PackageSource) exportedPackages.getByKey(pkgname);
929 if (exporter != null) {
930 Class serviceClass = exporter.getSupplier().getBundleLoader().findLocalClass(className);
931 if (serviceClass != null)
932 return serviceClass;
933 }
934 }
935 // try bundle's PRIVATE class space
936 if (bundle == null)
937 return null;
938 BundleLoader loader = bundle.getBundleLoader();
939 if (!checkServiceClassSource) {
940 try {
941 return loader.findClass(className);
942 } catch (ClassNotFoundException e1) {
943 return null;
944 }
945 }
946 if (restrictServiceClasses) {
947 // cannot have a service class from a package that a bundle
948 // provides to a NAMED class space (unless it is exported also).
949 if (pkgname == null)
950 pkgname = BundleLoader.DEFAULT_PACKAGE;
951 if (loader.getProvidedPackage(pkgname) != null)
952 return null;
953 }
954 Class serviceClass = loader.findLocalClass(className);
955 if (serviceClass == null || bundle.getBundleId() == 0)
956 return serviceClass;
957 return serviceClass.getClassLoader() == loader.createClassLoader() ? serviceClass : null;
958 }
959 }