BundleImpl.java

00001 /*
00002  * Copyright (c) 2003-2006, KNOPFLERFISH project
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following
00007  * conditions are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  *
00012  * - Redistributions in binary form must reproduce the above
00013  *   copyright notice, this list of conditions and the following
00014  *   disclaimer in the documentation and/or other materials
00015  *   provided with the distribution.
00016  *
00017  * - Neither the name of the KNOPFLERFISH project nor the names of its
00018  *   contributors may be used to endorse or promote products derived
00019  *   from this software without specific prior written permission.
00020  *
00021  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00027  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
00028  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00029  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
00030  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00031  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
00032  * OF THE POSSIBILITY OF SUCH DAMAGE.
00033  */
00034 
00035 package org.knopflerfish.framework;
00036 
00037 import java.io.*;
00038 import java.net.*;
00039 import java.security.*;
00040 
00041 import java.util.Enumeration;
00042 import java.util.Set;
00043 import java.util.Dictionary;
00044 import java.util.Hashtable;
00045 import java.util.ArrayList;
00046 import java.util.Map;
00047 import java.util.HashSet;
00048 import java.util.HashMap;
00049 import java.util.Iterator;
00050 import java.util.List;
00051 import java.util.Locale;
00052 import java.util.Vector;
00053 
00054 import org.osgi.framework.*;
00055 
00056 
00066 class BundleImpl implements Bundle {
00067 
00074   static int RESOLVED_FLAGS = RESOLVED | STARTING | ACTIVE | STOPPING;
00075 
00079   final Framework framework;
00080 
00084   final PermissionOps secure;
00085 
00089   final long id;
00090 
00094   final String location;
00095 
00099   boolean v2Manifest;
00100 
00104   String symbolicName;
00105 
00109   boolean singleton;
00110 
00114   Version version;
00115 
00119   int state;
00120 
00124   BundlePackages bpkgs;
00125 
00129   BundleArchive archive;
00130 
00134   int generation = 0;
00135 
00139   private ProtectionDomain protectionDomain;
00140 
00144   private ClassLoader classLoader = null;
00145 
00149   private HashMap /* BundlePackages -> BundleClassLoader */ oldClassLoaders = null;
00150 
00154   protected FileTree bundleDir = null;
00155 
00159   protected BundleContextImpl bundleContext = null;
00160 
00164   protected BundleActivator bactivator = null;
00165 
00170   protected long lastModified;
00171 
00176   boolean bDelayedStart = false;
00177 
00181   ArrayList fragments = null;
00182  
00186   String attachPolicy;
00187   
00192   Fragment fragment = null;
00193 
00197   private HeaderDictionary cachedHeaders = null;
00198   
00202   private HeaderDictionary cachedRawHeaders = null;
00203 
00204   
00212   BundleImpl(Framework fw, long id, String loc, ProtectionDomain pd, String sym, Version ver) {
00213     this.framework = fw;
00214     this.secure = fw.perm;
00215     this.id = id;
00216     this.location = loc;
00217     this.protectionDomain = pd;
00218     this.symbolicName = sym;
00219     this.singleton = false;
00220     this.version = ver;
00221     this.v2Manifest = true;
00222     this.attachPolicy = Constants.FRAGMENT_ATTACHMENT_ALWAYS;
00223     modified();
00224   }
00225 
00238   BundleImpl(Framework fw, BundleArchive ba) {
00239     framework = fw;
00240     secure = fw.perm;
00241     id = ba.getBundleId();
00242     location = ba.getBundleLocation();
00243     archive = ba;
00244     state = INSTALLED;
00245     checkManifestHeaders();
00246     protectionDomain = secure.getProtectionDomain(this);
00247     doExportImport();
00248     bundleDir = fw.getDataStorage(id);
00249 
00250     int oldStartLevel = archive.getStartLevel();
00251     try {
00252       if (framework.startLevelService == null) {
00253         archive.setStartLevel(0);
00254       } else {
00255         if (oldStartLevel == -1) {
00256           archive.setStartLevel(framework.startLevelService.getInitialBundleStartLevel());
00257         }
00258       }
00259     } catch (Exception e) {
00260       Debug.println("Failed to set start level on #" + id + ": " + e);
00261     }
00262 
00263     // Activate extension as soon as they are installed so that
00264     // they get added in bundle id order.
00265     if (isExtension() && resolveFragment(framework.systemBundle)) {
00266       state = RESOLVED;
00267     }
00268 
00269     lastModified = archive.getLastModified();
00270     if (lastModified == 0) {
00271       modified();
00272     }
00273   }
00274 
00275 
00276   //
00277   // Bundle interface
00278   //
00279 
00285   public int getState() {
00286     return state;
00287   }
00288 
00289 
00295   synchronized public void start() throws BundleException {
00296     secure.checkExecuteAdminPerm(this);
00297 
00298     if (isFragment()) {
00299       throw new BundleException("Cannot start a fragment bundle");
00300     }
00301 
00302     if (framework.startLevelService != null) {
00303       if (state != UNINSTALLED &&
00304          getStartLevel() > framework.startLevelService.getStartLevel()) {
00305         secure.callSetPersistent(this, true);
00306         bDelayedStart = true;
00307         return;
00308       }
00309     }
00310 
00311     switch (getUpdatedState()) {
00312     case INSTALLED:
00313       throw new BundleException("Failed, " + bpkgs.getResolveFailReason());
00314     case RESOLVED:
00315       if (framework.active) {
00316         state = STARTING;
00317         framework.listeners.bundleChanged(new BundleEvent(BundleEvent.STARTING, this));
00318         bundleContext = new BundleContextImpl(this);
00319         try {
00320           secure.callStart0(this);
00321         } catch (BundleException e) {
00322           removeBundleResources();
00323           bundleContext.invalidate();
00324           bundleContext = null;
00325           state = RESOLVED;
00326           throw e;
00327         }
00328         framework.listeners.bundleChanged(new BundleEvent(BundleEvent.STARTED, this));
00329       } else {
00330         secure.callSetPersistent(this, true);
00331         startOnLaunch(true);
00332       }
00333       break;
00334     case ACTIVE:
00335       break;
00336     case STARTING:
00337       // This happens if call start from inside the BundleActivator.start method.
00338       // Don't allow it.
00339       throw new BundleException("called from BundleActivator.start");
00340     case STOPPING:
00341       // This happens if call start from inside the BundleActivator.stop method.
00342       // Don't allow it.
00343       throw new BundleException("called from BundleActivator.stop");
00344     case UNINSTALLED:
00345       throw new IllegalStateException("Bundle is in UNINSTALLED state");
00346     }
00347   }
00348 
00349 
00350   void start0() throws BundleException {
00351     final String ba = archive.getAttribute(Constants.BUNDLE_ACTIVATOR);
00352     boolean bStarted = false;
00353 
00354     ClassLoader oldLoader = null;
00355 
00356     if (Framework.SETCONTEXTCLASSLOADER) {
00357       oldLoader = Thread.currentThread().getContextClassLoader();
00358     }
00359 
00360     try {
00361       // If SETCONTEXTCLASSLOADER, set the thread's context
00362       // class loader to the bundle class loader. This
00363       // is useful for debugging external libs using
00364       // the context class loader.
00365       if (Framework.SETCONTEXTCLASSLOADER) {
00366         Thread.currentThread().setContextClassLoader(getClassLoader());
00367       }
00368 
00369       if (ba != null) {
00370         Class c = getClassLoader().loadClass(ba.trim());
00371         bactivator = (BundleActivator)c.newInstance();
00372 
00373         bactivator.start(bundleContext);
00374         bStarted = true;
00375       } else {
00376         // Check if we have a standard Main-class attribute as
00377         // in normal executable jar files. This is a slight
00378         // extension to the OSGi spec.
00379         final String mc = archive.getAttribute("Main-class");
00380 
00381         if (mc != null) {
00382           if(Debug.packages) {
00383             Debug.println("starting main class " + mc);
00384           }
00385           Class mainClass = getClassLoader().loadClass(mc.trim());
00386 
00387           bactivator = MainClassBundleActivator.create(getClassLoader(), mainClass);
00388           bactivator.start(bundleContext);
00389           bStarted = true;
00390         }
00391       }
00392 
00393       if (!bStarted) {
00394         // Even bundles without an activator are marked as
00395         // ACTIVE.
00396         // Should we possible log an information message to
00397         // make sure users are aware of the missing activator?
00398       }
00399 
00400       state = ACTIVE;
00401       setPersistent(true);
00402       startOnLaunch(true);
00403     } catch (Throwable t) {
00404         t.printStackTrace();
00405       throw new BundleException("BundleActivator start failed", t);
00406     } finally {
00407       if (Framework.SETCONTEXTCLASSLOADER) {
00408         Thread.currentThread().setContextClassLoader(oldLoader);
00409       }
00410     }
00411   }
00412 
00413 
00417   boolean allowSetStartOnLaunchFalse() {
00418     /* boolean bCompat =
00419        framework.startLevelService == null ||
00420        framework.startLevelService.bCompat;
00421     */
00422     return
00423       // never never touch on FW shutdown
00424       !framework.shuttingdown && !archive.isPersistent();
00425 
00426     /*
00427       &&
00428 
00429       // allow touch if in startlevel compatibility mode
00430       (bCompat ||
00431       // ...also allow touch if not marked as persistant startlevel active
00432       !isPersistent());
00433     */
00434   }
00435 
00441   synchronized public void stop() throws BundleException {
00442     secure.checkExecuteAdminPerm(this);
00443 
00444     if (isFragment()) {
00445       throw new BundleException("Cannot stop a fragment bundle");
00446     }
00447 
00448     bDelayedStart = false;
00449 
00450     switch (state) {
00451     case INSTALLED:
00452     case RESOLVED:
00453       secure.callSetPersistent(this, false);
00454       // We don't want this bundle to start on launch after it has been
00455       // stopped. (Don't apply during shutdown
00456       if (allowSetStartOnLaunchFalse()) {
00457         secure.callStartOnLaunch(this, false);
00458       }
00459       break;
00460     case ACTIVE:
00461       BundleException savedException = secure.callStop0(this, true);
00462       if (savedException != null) {
00463         throw savedException;
00464       }
00465       break;
00466     case STARTING:
00467       // This happens if we call stop from inside the BundleActivator.start method.
00468       // We don't allow it.
00469       throw new BundleException("Bundle.start called from BundleActivator.stop");
00470     case STOPPING:
00471       // This happens if we call stop from inside the BundleActivator.stop method.
00472       // We don't allow it.
00473       throw new BundleException("Bundle.stop called from BundleActivator.stop");
00474     case UNINSTALLED:
00475       throw new IllegalStateException("Bundle.stop: Bundle is in UNINSTALLED state");
00476     }
00477   }
00478 
00479   synchronized BundleException stop0(boolean resetPersistent) {
00480     BundleException res = null;
00481 
00482     state = STOPPING;
00483     framework.listeners.bundleChanged(new BundleEvent(BundleEvent.STOPPING, this));
00484 
00485     if (resetPersistent) {
00486       setPersistent(false);
00487     }
00488 
00489     if (allowSetStartOnLaunchFalse()) {
00490       startOnLaunch(false);
00491     }
00492     if (bactivator != null) {
00493       try {
00494         bactivator.stop(bundleContext);
00495       } catch (Throwable e) {
00496         res = new BundleException("Bundle.stop: BundleActivator stop failed", e);
00497       }
00498       bactivator = null;
00499     }
00500 
00501     bundleContext.invalidate();
00502     bundleContext = null;
00503     removeBundleResources();
00504     state = RESOLVED;
00505     framework.listeners.bundleChanged(new BundleEvent(BundleEvent.STOPPED, this));
00506     return res;
00507   }
00508 
00509 
00515   public void update() throws BundleException {
00516     update(null);
00517   }
00518 
00519 
00525   synchronized public void update(final InputStream in) throws BundleException {
00526     try {
00527       secure.checkLifecycleAdminPerm(this);
00528       if (isExtension()) {
00529         secure.checkExtensionLifecycleAdminPerm(this);
00530       }
00531       final boolean wasActive = state == ACTIVE;
00532 
00533       switch (getUpdatedState()) {
00534       case ACTIVE:
00535         stop();
00536         // Fall through
00537       case RESOLVED:
00538       case INSTALLED:
00539         // Load new bundle
00540         secure.callUpdate0(this, in, wasActive);
00541         break;
00542       case STARTING:
00543         // Wait for RUNNING state, this doesn't happen now
00544         // since we are synchronized.
00545         throw new IllegalStateException("Bundle is in STARTING state");
00546       case STOPPING:
00547         // Wait for RESOLVED state, this doesn't happen now
00548         // since we are synchronized.
00549         throw new IllegalStateException("Bundle is in STOPPING state");
00550       case UNINSTALLED:
00551         throw new IllegalStateException("Bundle is in UNINSTALLED state");
00552       }
00553     } finally {
00554       if (in != null) {
00555         try {
00556           in.close();
00557         } catch (IOException ignore) {}
00558       }
00559 
00560     }
00561   }
00562 
00563 
00564   void update0(InputStream in, boolean wasActive) throws BundleException {
00565     final boolean wasResolved = state == RESOLVED;
00566     final int oldStartLevel = getStartLevel();
00567     BundleArchive newArchive = null;
00568     //HeaderDictionary newHeaders;
00569     try {
00570       // New bundle as stream supplied?
00571       InputStream bin;
00572       if (in == null) {
00573         // Try Bundle-UpdateLocation
00574         String update = archive.getAttribute(Constants.BUNDLE_UPDATELOCATION);
00575         if (update == null) {
00576           // Take original location
00577           update = location;
00578         }
00579         bin = (new URL(update)).openStream();
00580       } else {
00581         bin = in;
00582       }
00583 
00584       newArchive = framework.storage.updateBundleArchive(archive, bin);
00585       checkEE(newArchive);
00586       checkManifestHeaders();
00587       newArchive.setStartLevel(oldStartLevel);
00588       framework.storage.replaceBundleArchive(archive, newArchive);
00589     } catch (Exception e) {
00590       if (newArchive != null) {
00591         newArchive.purge();
00592       }
00593                   
00594       if (wasActive) {
00595         try {
00596           start();
00597         } catch (BundleException be) {
00598           framework.listeners.frameworkError(this, be);
00599         }
00600       }
00601       if (e instanceof BundleException) {
00602         throw (BundleException)e;
00603       } else {
00604         throw new BundleException("Failed to get update bundle", e);
00605       }
00606     }
00607 
00608     boolean purgeOld;
00609 
00610     if (isFragment()) {
00611       if (isAttached()) {
00612         fragment.setHost(null);
00613         purgeOld = false;
00614       } else {
00615         purgeOld = true;
00616       }
00617     } else {
00618       // Remove this bundle's packages
00619       boolean allRemoved = bpkgs.unregisterPackages(false);
00620 
00621       // Loose old bundle if no exporting packages left
00622       if (allRemoved) {
00623         if (classLoader != null) {
00624           ((BundleClassLoader)classLoader).close();
00625           classLoader = null;
00626         }
00627         purgeOld = true;
00628       } else {
00629         saveZombiePackages();
00630         purgeOld = false;
00631       }
00632     }
00633 
00634     // Activate new bundle
00635     BundleArchive oldArchive = archive;
00636     archive = newArchive;
00637     cachedRawHeaders = null;
00638     state = INSTALLED;
00639     ProtectionDomain oldProtectionDomain = protectionDomain;
00640     protectionDomain = secure.getProtectionDomain(this);
00641     doExportImport();
00642 
00643     // Purge old archive
00644     if (purgeOld) {
00645       secure.purge(this, oldProtectionDomain);
00646       oldArchive.purge();
00647     }
00648 
00649     // Broadcast updated event
00650     if (wasResolved) {
00651       framework.listeners.bundleChanged(new BundleEvent(BundleEvent.UNRESOLVED,
00652                                                         this));
00653     }
00654     framework.listeners.bundleChanged(new BundleEvent(BundleEvent.UPDATED,
00655                                                       this));
00656 
00657     // Restart bundles previously stopped in the operation
00658     if (wasActive) {
00659       try {
00660         start();
00661       } catch (BundleException be) {
00662         framework.listeners.frameworkError(this, be);
00663       }
00664     }
00665     //only when complete success
00666     modified();
00667   }
00668 
00669 
00670   void checkEE(BundleArchive ba) throws BundleException {
00671     String ee = ba.getAttribute(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT);
00672     if (ee != null) {
00673       if (Debug.packages) {
00674         Debug.println("bundle #" + ba.getBundleId() + " has EE=" + ee);
00675       }
00676       if (!framework.isValidEE(ee)) {
00677         throw new BundleException("Execution environment '" + ee + "' is not supported");
00678       }
00679     }
00680   }
00681 
00682 
00688   synchronized public void uninstall() throws BundleException {
00689     secure.checkLifecycleAdminPerm(this);
00690     if (isExtension()) {
00691       secure.checkExtensionLifecycleAdminPerm(this);
00692     }
00693     secure.callUninstall0(this);
00694   }
00695 
00696   void uninstall0() {
00697     boolean wasResolved = false;
00698 
00699     try {
00700       archive.setStartLevel(-2); // Mark as uninstalled
00701     } catch (Exception ignored) {   }
00702 
00703     cachedHeaders = getHeaders0(null);
00704 
00705     bDelayedStart = false;
00706   
00707     switch (state) {
00708     case ACTIVE:
00709       try {
00710         stop();
00711       } catch (BundleException be) {
00712         framework.listeners.frameworkError(this, be);
00713       }
00714       // Fall through
00715     case RESOLVED:
00716       wasResolved = true;
00717       // Fall through
00718     case INSTALLED:
00719 
00720       framework.bundles.remove(location);
00721 
00722       if (isFragment()) {
00723         if (isAttached()) {
00724           classLoader = null;
00725           fragment.setHost(null);
00726         } else {
00727           secure.purge(this, protectionDomain);
00728           archive.purge();
00729         }
00730       } else {
00731         if (bpkgs.unregisterPackages(false)) {
00732           if (classLoader != null) {
00733             ((BundleClassLoader)classLoader).purge();
00734             classLoader = null;
00735           } else {
00736             secure.purge(this, protectionDomain);
00737             archive.purge();
00738           }
00739         } else {
00740           saveZombiePackages();
00741           classLoader = null;
00742         }
00743         if (isFragmentHost()) {
00744           detachFragments(true);
00745         }
00746       }
00747 
00748       bpkgs = null;
00749       bactivator = null;
00750       if (bundleDir != null) {
00751         if (!bundleDir.delete()) {
00752           // Bundle dir is not deleted completely, make sure we mark
00753           // it as uninstalled for next framework restart
00754           try {
00755             archive.setStartLevel(-2); // Mark as uninstalled
00756           } catch (Exception e) {
00757             Debug.println("Failed to mark bundle " + id +
00758                           " as uninstalled, " + bundleDir +
00759                           " must be deleted manually: " + e);
00760           }
00761         }
00762         bundleDir = null;
00763       }
00764 
00765       // id, location and headers survices after uninstall.
00766       state = UNINSTALLED;
00767       modified();
00768 
00769       if (wasResolved) {
00770         framework.listeners.bundleChanged(new BundleEvent(BundleEvent.UNRESOLVED, this));
00771       }
00772       framework.listeners.bundleChanged(new BundleEvent(BundleEvent.UNINSTALLED, this));
00773       break;
00774     case STARTING:
00775       // Wait for RUNNING state, this doesn't happen now
00776       // since we are synchronized.
00777       throw new IllegalStateException("Bundle is in STARTING state");
00778     case STOPPING:
00779       // Wait for RESOLVED state, this doesn't happen now
00780       // since we are synchronized.
00781       throw new IllegalStateException("Bundle is in STOPPING state");
00782     case UNINSTALLED:
00783       throw new IllegalStateException("Bundle is in UNINSTALLED state");
00784     }
00785   }
00786 
00787 
00794   public Dictionary getHeaders() {
00795     return getHeaders(null);
00796   }
00797 
00798 
00804   public long getBundleId() {
00805     return id;
00806   }
00807 
00808 
00814   public String getLocation() {
00815     secure.checkMetadataAdminPerm(this);
00816     return location;
00817   }
00818 
00819 
00825   public ServiceReference[] getRegisteredServices() {
00826     Set sr = framework.services.getRegisteredByBundle(this);
00827     secure.filterGetServicePermission(sr);
00828     ServiceReference[] res = new ServiceReference[sr.size()];
00829     int pos = 0;
00830     for (Iterator i = sr.iterator(); i.hasNext(); ) {
00831       res[pos++] = ((ServiceRegistration)i.next()).getReference();
00832     }
00833     return res;
00834   }
00835 
00836 
00842   public ServiceReference[] getServicesInUse() {
00843     Set sr = framework.services.getUsedByBundle(this);
00844     secure.filterGetServicePermission(sr);
00845     ServiceReference[] res = new ServiceReference[sr.size()];
00846     int pos = 0;
00847     for (Iterator i = sr.iterator(); i.hasNext(); ) {
00848       res[pos++] = ((ServiceRegistration)i.next()).getReference();
00849     }
00850     return res;
00851   }
00852 
00853 
00859   public boolean hasPermission(Object permission) {
00860     checkUninstalled();
00861     if (permission instanceof Permission) {
00862       if (secure.checkPermissions()) {
00863         //get the current status from permission admin
00864         PermissionCollection pc = protectionDomain.getPermissions();
00865         return pc != null ? pc.implies((Permission)permission) : false;
00866       } else {
00867         return true;
00868       }
00869     } else {
00870       return false;
00871     }
00872   }
00873 
00874 
00878   public URL getResource(String name) {
00879     // ResourceAdminPermission checked in the classloader.
00880     checkUninstalled();
00881     if (isFragment()) {
00882       return null;
00883     }
00884     if (state == INSTALLED && !secure.okResourceAdminPerm(this)) {
00885       // We don't want to create a classloader unless we have permission to.
00886       return null;
00887     }
00888     if (getUpdatedState() != INSTALLED) {
00889       BundleClassLoader cl = (BundleClassLoader)getClassLoader();
00890       if (cl != null) {
00891         Enumeration res = cl.getBundleResources(name, true);
00892         if (res != null) {
00893           return (URL)res.nextElement();
00894         }
00895       }
00896     }
00897     return null;
00898   }
00899 
00900 
00904   public String getSymbolicName() {
00905     return symbolicName;
00906   }
00907 
00908 
00909   //
00910   // Package methods
00911   //
00912 
00919   int getUpdatedState() {
00920     if (state == INSTALLED) {
00921       synchronized (this) {
00922         if (state == INSTALLED) {
00923           if (isFragment()) {
00924             BundleImpl host = getFragmentHost();
00925             if (host != null) {
00926               if (host.state == INSTALLED) {
00927                 // Try resolve our host
00928                 host.getUpdatedState();
00929               } else {
00930                 // NYI! dynamic attach?
00931               }
00932             }
00933           } else {
00934             // TODO, should we do this as a part of package resolving.
00935             attachFragments();
00936             if (bpkgs.resolvePackages()) {
00937               if (fragments != null) {
00938                 for (Iterator i = fragments.iterator(); i.hasNext(); ) {
00939                   BundleImpl b = (BundleImpl)i.next();
00940                   b.state = RESOLVED;
00941                 }
00942               }
00943               state = RESOLVED;
00944               if (fragments != null) {
00945                 for (Iterator i = fragments.iterator(); i.hasNext(); ) {
00946                   BundleImpl b = (BundleImpl)i.next();
00947                   framework.listeners.bundleChanged(new BundleEvent(BundleEvent.RESOLVED, b));
00948                 }
00949               }
00950               framework.listeners.bundleChanged(new BundleEvent(BundleEvent.RESOLVED, this));
00951 
00952               if (id != 0) { // this is not applicable to system bundle.
00953                 List fe = archive.getFailedClassPathEntries();
00954                 if (fe != null) {
00955                   for (Iterator i = fe.iterator(); i.hasNext(); ) {
00956                     Exception e = new IOException("Failed to classpath entry: " + i.next());
00957                     framework.listeners.frameworkInfo(this, e);
00958                   }
00959                 }
00960               }
00961             } else {
00962               detachFragments(false);
00963             }
00964           } 
00965         }
00966       }
00967     }
00968     return state;
00969   }
00970 
00971 
00975   boolean resolveFragment(BundleImpl host) {
00976     if (host == getFragmentHost() && secure.okFragmentBundlePerm(this)) {
00977       try {
00978         host.attachFragment(this);
00979         fragment.setHost(host);
00980         return true;
00981       } catch (Exception _ignore) { }
00982     }
00983     // TODO, Log this?
00984     return false;
00985   }
00986 
00987 
00993   File getDataRoot() {
00994     return bundleDir;
00995   }
00996 
00997 
01005   ClassLoader getClassLoader() {
01006     if (classLoader == null) {
01007       synchronized (this) {
01008         if (classLoader == null && (state & RESOLVED_FLAGS) != 0) {
01009           classLoader = secure.callGetClassLoader0(this);
01010         }
01011       }
01012     }
01013     return classLoader;
01014   }
01015 
01016 
01017   ClassLoader getClassLoader0() {
01018     if (isFragment()) {
01019       if (isAttached()) {
01020         if (isBootClassPathExtension()) {
01021           ClassLoader root = ClassLoader.getSystemClassLoader();
01022           while (root.getParent() != null) {
01023             root = root.getParent();
01024           }
01025           return root;
01026         } else {
01027           return getFragmentHost().getClassLoader();
01028         }
01029       }
01030       return null;
01031     } else {
01032       ArrayList frags;
01033       if (isFragmentHost()) {
01034         frags = new ArrayList();
01035         for (Iterator i = fragments.iterator(); i.hasNext(); ) {
01036           frags.add(((BundleImpl)i.next()).archive);
01037         }
01038       } else {
01039         frags = null;
01040       }
01041       return new BundleClassLoader(bpkgs, archive, frags, protectionDomain, secure);
01042     }
01043   }
01044 
01045 
01051   synchronized void setStateInstalled(boolean sendEvent) {
01052     if (isFragment()) {
01053       classLoader = null;
01054       fragment.setHost(null);
01055     } else {
01056       if (classLoader != null) {
01057         ((BundleClassLoader)classLoader).close();
01058         classLoader = null;
01059       }
01060       bpkgs.unregisterPackages(true);
01061       if (isFragmentHost()) {
01062         detachFragments(true);
01063       }
01064       bpkgs.registerPackages();
01065     }
01066     
01067     state = INSTALLED;
01068     if (sendEvent) {
01069       framework.listeners.bundleChanged(new BundleEvent(BundleEvent.UNRESOLVED, this));
01070     }
01071   }
01072 
01073 
01080   ClassLoader getClassLoader(BundlePackages ebpkgs) {
01081     if (bpkgs == ebpkgs) {
01082       return getClassLoader();
01083     } else if (oldClassLoaders != null) {
01084       return (ClassLoader)oldClassLoaders.get(ebpkgs);
01085     }
01086     return null;
01087   }
01088 
01089 
01094   void purge() {
01095     if (state == UNINSTALLED) {
01096       framework.bundles.remove(location);
01097     }
01098     if (oldClassLoaders != null) {
01099       for (Iterator i = oldClassLoaders.values().iterator(); i.hasNext();) {
01100         ((BundleClassLoader)i.next()).purge();
01101       }
01102       oldClassLoaders = null;
01103     }
01104   }
01105 
01106 
01112   BundleArchive getBundleArchive(long gen, long frag) {
01113     // TODO, maybe we should always specify generation and return null if they don't match
01114     if (gen == -1 || (bpkgs != null && bpkgs.generation == gen)) {
01115       if (frag == -1) {
01116         return archive;
01117       } else {
01118         return ((BundleClassLoader)getClassLoader()).getBundleArchive(frag);
01119       }
01120     } else {
01121       for (Iterator i = oldClassLoaders.values().iterator(); i.hasNext();) {
01122         BundleClassLoader cl = (BundleClassLoader)i.next();
01123         if (cl.getBpkgs().generation == gen) {
01124           return cl.getBundleArchive(frag);
01125         }
01126       }
01127       return null;
01128     }
01129   }
01130 
01131 
01137   Iterator getExports() {
01138     if (oldClassLoaders != null) {
01139       HashSet res = new HashSet();
01140       for (Iterator i = oldClassLoaders.values().iterator(); i.hasNext();) {
01141         for (Iterator j = ((BundleClassLoader)i.next()).getBpkgs().getExports(); j.hasNext();) {
01142           res.add(j.next());
01143         }
01144       }
01145       if (bpkgs != null) {
01146         for (Iterator i = bpkgs.getExports(); i.hasNext();) {
01147           res.add(i.next());
01148         }
01149       }
01150       return res.iterator();
01151     } else if (bpkgs != null) {
01152       return bpkgs.getExports();
01153     } else {
01154       return (new ArrayList(0)).iterator();
01155     }
01156   }
01157 
01158 
01164   Iterator getImports() {
01165     if (oldClassLoaders != null) {
01166       HashSet res = new HashSet();
01167       for (Iterator i = oldClassLoaders.values().iterator(); i.hasNext();) {
01168         for (Iterator j = ((BundleClassLoader)i.next()).getBpkgs().getImports(); j.hasNext();) {
01169           res.add(j.next());
01170         }
01171       }
01172       if (bpkgs != null) {
01173         for (Iterator i = bpkgs.getImports(); i.hasNext();) {
01174           res.add(i.next());
01175         }
01176       }
01177       return res.iterator();
01178     } else if (bpkgs != null) {
01179       return bpkgs.getImports();
01180     } else {
01181       return (new ArrayList(0)).iterator();
01182     }
01183   }
01184 
01185 
01189   URL getURL(long gen, long frag, int bcpElem, String path) {
01190     try {
01191       StringBuffer u = new StringBuffer(BundleURLStreamHandler.PROTOCOL);
01192       u.append("://");
01193       u.append(id);
01194       if (gen != -1) {
01195         u.append('.').append(gen);
01196       }
01197       if (frag != -1 && frag != id) {
01198         u.append('_').append(frag);
01199       }
01200       if (bcpElem >= 0) {
01201         u.append(':').append(bcpElem);
01202       }
01203       if (!path.startsWith("/")) {
01204         u.append('/');
01205       }
01206       u.append(path);
01207       return new URL(u.toString());
01208     } catch (MalformedURLException e) {
01209       return null;
01210     }
01211   }
01212 
01213 
01214   //
01215   // Private methods
01216   //
01217 
01221   private void checkManifestHeaders() {
01222     // TBD, v2Manifest unnecessary to cache?
01223     v2Manifest = "2".equals(archive.getAttribute(Constants.BUNDLE_MANIFESTVERSION));
01224     Iterator i = Util.parseEntries(Constants.BUNDLE_SYMBOLICNAME,
01225                                    archive.getAttribute(Constants.BUNDLE_SYMBOLICNAME),
01226                                    true, true, true);
01227     Map e = null;
01228     if (i.hasNext()) {
01229       e = (Map)i.next();
01230       symbolicName = (String)e.get("key");
01231     } else {
01232       if (v2Manifest) {
01233         throw new IllegalArgumentException("Bundle has no symbolic name, location=" +
01234                                            location);
01235       } else {
01236         symbolicName = null;
01237       }
01238     }
01239     String mbv = archive.getAttribute(Constants.BUNDLE_VERSION);
01240     if (mbv != null) {
01241       try {
01242         version = new Version(mbv);
01243       } catch (Throwable ee) {
01244         if (v2Manifest) {
01245           throw new IllegalArgumentException("Bundle does not specify a valid " + 
01246               Constants.BUNDLE_VERSION + " header. Got exception: " + ee.getMessage());
01247         } else {
01248           version = Version.emptyVersion;
01249         }
01250       }
01251       
01252     } else {
01253       version = Version.emptyVersion;
01254     }
01255 
01256     attachPolicy = Constants.FRAGMENT_ATTACHMENT_ALWAYS;
01257     if (e != null) {
01258       singleton = "true".equals((String)e.get(Constants.SINGLETON_DIRECTIVE));
01259       BundleImpl snb = framework.bundles.getBundle(symbolicName, version);
01260       String tmp = (String)e.get(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE);
01261       attachPolicy = tmp == null ? Constants.FRAGMENT_ATTACHMENT_ALWAYS : tmp;
01262       // TBD! Should we allow update to same version?
01263       if (snb != null && snb != this) {
01264         throw new IllegalArgumentException("Bundle with same symbolic name and version " +
01265                                            "is already installed (" + symbolicName + ", " +
01266                                            version);
01267       }
01268     } else {
01269       singleton = false;
01270     }
01271 
01272     i = Util.parseEntries(Constants.FRAGMENT_HOST,
01273                           archive.getAttribute(Constants.FRAGMENT_HOST),
01274                           true, true, true);
01275     
01276     if (i.hasNext()) {
01277       
01278       if (archive.getAttribute(Constants.BUNDLE_ACTIVATOR) != null) {
01279         throw new IllegalArgumentException("A fragment bundle can not have a Bundle-Activator.");
01280       }
01281       
01282       e = (Map)i.next();
01283       String extension = (String)e.get(Constants.EXTENSION_DIRECTIVE);
01284       String key = (String)e.get("key");
01285       
01286       if (Constants.EXTENSION_FRAMEWORK.equals(extension) ||
01287           Constants.EXTENSION_BOOTCLASSPATH.equals(extension)) {
01288         
01289         // an extension bundle must target the system bundle.  
01290         if (!Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(key) && 
01291             !"org.knopflerfish.framework".equals(key)) {
01292           throw new IllegalArgumentException("An extension bundle must target " +
01293                                              "the system bundle(=" +
01294                                              Constants.SYSTEM_BUNDLE_SYMBOLICNAME + ")");
01295         }
01296         
01297         if (archive.getAttribute(Constants.IMPORT_PACKAGE) != null ||
01298             archive.getAttribute(Constants.REQUIRE_BUNDLE) != null ||
01299             archive.getAttribute(Constants.BUNDLE_NATIVECODE) != null ||
01300             archive.getAttribute(Constants.DYNAMICIMPORT_PACKAGE) != null ||
01301             archive.getAttribute(Constants.BUNDLE_ACTIVATOR) != null) {
01302           throw new IllegalArgumentException("An extension bundle cannot specify: " +
01303                                              Constants.IMPORT_PACKAGE + ", " +
01304                                              Constants.REQUIRE_BUNDLE + ", " +
01305                                              Constants.BUNDLE_NATIVECODE + ", " +
01306                                              Constants.DYNAMICIMPORT_PACKAGE + " or " +
01307                                              Constants.BUNDLE_ACTIVATOR);
01308         }
01309         if (!Framework.SUPPORTS_EXTENSION_BUNDLES) {
01310           if (Framework.bIsMemoryStorage) {
01311             throw new UnsupportedOperationException("Extension bundles are not supported in memory storage mode.");
01312           } else if (!Framework.EXIT_ON_SHUTDOWN) {
01313             throw new UnsupportedOperationException("Extension bundles require that the property " +
01314                                                     Main.EXITONSHUTDOWN_PROP + " is set to \"true\"");
01315           } else if (!Framework.USING_WRAPPER_SCRIPT) {
01316             throw new UnsupportedOperationException("Extension bundles require the use of a wrapper script. " +
01317                                                     "Consult the documentation");
01318           } else {
01319             throw new UnsupportedOperationException("Extension bundles are not supported.");
01320           }
01321         }
01322       } else {
01323         if (extension != null) {
01324           throw new IllegalArgumentException("Did not recognize directive " + 
01325                                              Constants.EXTENSION_DIRECTIVE
01326                                              + ":=" + extension + "." );
01327         }
01328       }
01329       
01330       if (fragment == null) {
01331         fragment = new Fragment(key,
01332                                 extension,
01333                                 (String)e.get(Constants.BUNDLE_VERSION_ATTRIBUTE));
01334       }
01335     }
01336   }
01337 
01338 
01344   void startOnLaunch(boolean value) {
01345     try {
01346       archive.setStartOnLaunchFlag(value);
01347     } catch (IOException e) {
01348       framework.listeners.frameworkError(this, e);
01349     }
01350   }
01351 
01352 
01356   void setPersistent(final boolean value) {
01357     try {
01358       archive.setPersistent(value);
01359     } catch (Exception e) {
01360       framework.listeners.frameworkError(this, e);
01361     }
01362   }
01363 
01364 
01370   void doExportImport() {
01371     bpkgs = new BundlePackages(this,
01372                                generation++,
01373                                archive.getAttribute(Constants.EXPORT_PACKAGE),
01374                                archive.getAttribute(Constants.IMPORT_PACKAGE),
01375                                archive.getAttribute(Constants.DYNAMICIMPORT_PACKAGE),
01376                                archive.getAttribute(Constants.REQUIRE_BUNDLE));
01377     if (!isFragment()) {
01378       // fragments don't export anything themselves.
01379       bpkgs.registerPackages();
01380     }
01381   }
01382 
01383 
01389   private void removeBundleResources() {
01390     framework.listeners.removeAllListeners(this);
01391     Set srs = framework.services.getRegisteredByBundle(this);
01392     for (Iterator i = srs.iterator(); i.hasNext();) {
01393       try {
01394         ((ServiceRegistration)i.next()).unregister();
01395       } catch (IllegalStateException ignore) {
01396         // Someone has unregistered the service after stop completed.
01397         // This should not occur, but we don't want get stuck in
01398         // an illegal state so we catch it.
01399       }
01400     }
01401     Set s = framework.services.getUsedByBundle(this);
01402     for (Iterator i = s.iterator(); i.hasNext(); ) {
01403       ((ServiceRegistrationImpl) i.next()).reference.ungetService(this, false);
01404     }
01405   }
01406 
01407 
01412   private void saveZombiePackages() {
01413     if (oldClassLoaders == null) {
01414       oldClassLoaders = new HashMap();
01415     }
01416     oldClassLoaders.put(bpkgs, getClassLoader());
01417     classLoader = null;
01418   }
01419 
01420 
01421   // Start level related
01422 
01426   boolean isPersistent() {
01427     return bDelayedStart || archive.isPersistent();
01428   }
01429 
01430 
01434   int getStartLevel() {
01435     if (archive != null) {
01436       return archive.getStartLevel();
01437     } else {
01438       return 0;
01439     }
01440   }
01441 
01442 
01446   void setStartLevel(int n) {
01447     // as soon as anoyone sets the start level explicitly
01448     // the level becomes persistent
01449 
01450     if(archive != null) {
01451       try {
01452         archive.setStartLevel(n);
01453       } catch (Exception e) {
01454         Debug.println("Failed to set start level on #" + getBundleId());
01455       }
01456     }
01457   }
01458 
01459   // Misc other
01460 
01468   public String toString() {
01469     return toString(0); // 0 is lowest detail
01470   }
01471 
01472   String toString(int detail) {
01473     StringBuffer sb = new StringBuffer();
01474 
01475     sb.append("BundleImpl[");
01476     sb.append("id=" + getBundleId());
01477     if(detail > 0) {
01478       sb.append(", state=" + getState());
01479     }
01480 
01481     if(detail > 1) {
01482       sb.append(", startlevel=" + getStartLevel());
01483     }
01484 
01485     if(detail > 3) {
01486       sb.append(", bDelayedStart=" + bDelayedStart);
01487     }
01488 
01489     if(detail > 4) {
01490       try {
01491         sb.append(", bPersistant=" + isPersistent());
01492       }  catch (Exception e) {
01493         sb.append(", bPersistant=" + e);
01494       }
01495     }
01496     if(detail > 4) {
01497       sb.append(", loc=" + location);
01498     }
01499 
01500     if(detail > 4) {
01501       sb.append(", symName=" + symbolicName);
01502     }
01503 
01504     sb.append("]");
01505 
01506     return sb.toString();
01507   }
01508 
01509 
01515   public Enumeration findEntries(String path, String filePattern, boolean recurse) {
01516     if (secure.okResourceAdminPerm(this)) {
01517       if (state == INSTALLED) {
01518         // We need to resolve if there are fragments involved
01519         if (!framework.bundles.getFragmentBundles(this).isEmpty()) {
01520           getUpdatedState();
01521         }
01522       }
01523       return secure.callFindEntries0(this, path, filePattern, recurse);
01524     } else {
01525       return null;
01526     }
01527   }
01528 
01529 
01533   Enumeration findEntries0(String path, String filePattern, boolean recurse) {
01534     Vector res = new Vector();
01535       if (isFragmentHost()) {
01536         for (Iterator i = fragments.iterator(); i.hasNext(); ) {
01537           BundleImpl fb = (BundleImpl)i.next();
01538           fb.addResourceEntries(res, path, filePattern, recurse);
01539         }
01540       }
01541       addResourceEntries(res, path, filePattern, recurse);
01542       return res.size() != 0 ? res.elements() : null;
01543   }
01544 
01545 
01549   void addResourceEntries(Vector res, String path, String pattern, boolean recurse) {
01550     Enumeration e = archive.findResourcesPath(path);
01551     if (e != null) {
01552       while (e.hasMoreElements()) {
01553         String fp = (String)e.nextElement();
01554         if (fp.endsWith("/")) {
01555           if (recurse) {
01556             addResourceEntries(res, fp, pattern, recurse);
01557           }
01558         } else {
01559           int l = fp.lastIndexOf('/');
01560           if (pattern == null || Util.filterMatch(pattern, fp.substring(l + 1))) {
01561             URL url = getURL(-1, -1, -1, fp);
01562             if (url != null) {
01563               res.add(url);
01564             }
01565           }
01566         }
01567       }
01568     }
01569   }
01570 
01571 
01575   public URL getEntry(String name) {
01576     if (secure.okResourceAdminPerm(this)) {
01577       checkUninstalled();
01578       try {
01579         InputStream is = secure.callGetInputStream(archive, name, 0);
01580         if (is != null) {
01581           is.close();
01582           return getURL(-1, -1, -1, name);
01583         }
01584       } catch (IOException _ignore) { }
01585     }
01586     return null;
01587   }
01588 
01589 
01593   public Enumeration getEntryPaths(String path) {
01594     if (secure.okResourceAdminPerm(this)) {
01595       checkUninstalled();
01596       return secure.callFindResourcesPath(archive, path);
01597     } else {
01598       return null;
01599     }
01600   }
01601 
01602 
01606   private Dictionary getLocaleDictionary(String locale, String baseName) {
01607     String defaultLocale = Locale.getDefault().toString();
01608 
01609     if (locale == null) {
01610       locale = defaultLocale;
01611     } else if (locale.equals("")) {
01612       return null;
01613     } 
01614 
01615     Hashtable localization_entries = new Hashtable();
01616     readLocalization("", localization_entries, baseName);
01617     readLocalization(Locale.getDefault().toString(), localization_entries, baseName);
01618     if (!locale.equals(defaultLocale)) {
01619       readLocalization(locale, localization_entries, baseName);
01620     } 
01621     
01622     return localization_entries;
01623   }
01624 
01625 
01631   private HeaderDictionary localize(Dictionary localization_entries) {
01632     HeaderDictionary localized = (HeaderDictionary)cachedRawHeaders.clone();
01633     
01634     if (localization_entries != null) {
01635       for (Enumeration e = localized.keys();
01636            e.hasMoreElements(); ) {
01637         String key = (String)e.nextElement();
01638         String unlocalizedEntry = (String)localized.get(key);
01639 
01640         if (unlocalizedEntry.startsWith("%")) {
01641           String k = unlocalizedEntry.substring(1);
01642           String val = (String)localization_entries.get(k);
01643 
01644           if (val == null) {
01645             localized.put(key, k);
01646           } else {
01647             localized.put(key, val);
01648           }
01649         }
01650       }
01651     }
01652     return localized;
01653   }
01654 
01662   protected void readLocalization(String locale, 
01663                                   Hashtable localization_entries,
01664                                   String baseName) {
01665     if (baseName == null) {
01666       baseName = Constants.BUNDLE_LOCALIZATION_DEFAULT_BASENAME;
01667     }
01668     int o = 0;
01669     String[] parts = Util.splitwords(locale, "_");
01670     String tmploc;
01671     if ("".equals(parts[0])) {
01672       tmploc = baseName;
01673     } else {
01674       tmploc = baseName + "_" + parts[0];
01675     }
01676     do {
01677       Hashtable tmp;
01678       if ((state & RESOLVED_FLAGS) != 0) {
01679         tmp = ((BundleClassLoader)getClassLoader()).getLocalizationEntries(tmploc +
01680                                                                            ".properties");
01681       } else if (archive != null) { // archive == null if this == systemBundle
01682         tmp = archive.getLocalizationEntries(tmploc  + ".properties");
01683       } else {
01684         // No where to search, return.
01685         return;
01686       }
01687       if (tmp != null) {
01688         localization_entries.putAll(tmp);
01689       }
01690       
01691       if (++o >= parts.length) {
01692         break;
01693       }
01694       tmploc = tmploc + "_" + parts[o];
01695       
01696     } while (true);
01697   }
01698 
01699 
01703   public Dictionary getHeaders(String locale) {
01704     secure.checkMetadataAdminPerm(this);
01705     return secure.callGetHeaders0(this, locale);
01706   }
01707 
01708   HeaderDictionary getHeaders0(String locale) {
01709     if (cachedRawHeaders == null) {
01710       cachedRawHeaders = archive.getUnlocalizedAttributes();
01711     }
01712 
01713     if ("".equals(locale)) {
01714       return (HeaderDictionary)cachedRawHeaders.clone();
01715     }
01716     
01717     if (state == UNINSTALLED) {
01718       return (HeaderDictionary)cachedHeaders.clone();
01719     } 
01720 
01721     String base = (String)cachedRawHeaders.get(Constants.BUNDLE_LOCALIZATION);
01722     Dictionary d;
01723     if (isFragment() && fragment.host != null) {
01724       d = fragment.host.getLocaleDictionary(locale, base);
01725     } else {
01726       d = getLocaleDictionary(locale, base);
01727     }
01728     return localize(d);
01729   }
01730 
01731 
01735   private void modified(){
01736     lastModified = System.currentTimeMillis();
01737     //TODO make sure it is persistent
01738     if(archive != null){
01739       try{
01740         archive.setLastModified(lastModified);
01741       }
01742       catch(IOException e){}
01743     }
01744   }
01745 
01750   public long getLastModified() {
01751     return lastModified;
01752   }
01753 
01754 
01758   public Enumeration getResources(String name) throws IOException {
01759     // ResourceAdminPermission checked in the classloader.
01760     checkUninstalled();
01761     if (isFragment()) {
01762       return null;
01763     }
01764     if (state == INSTALLED && !secure.okResourceAdminPerm(this)) {
01765       // We don't want to create a classloader unless we have permission to.
01766       return null;
01767     }
01768     if (getUpdatedState() != INSTALLED) {
01769       BundleClassLoader cl = (BundleClassLoader)getClassLoader();
01770       if (cl != null) {
01771         return cl.getBundleResources(name, false);
01772       }
01773     }
01774     return null;
01775   }
01776 
01777 
01782   public Class loadClass(final String name) throws ClassNotFoundException {
01783     if (secure.okClassAdminPerm(this)) {
01784       checkUninstalled();
01785       if (isFragment() && !isExtension()) {
01786         throw new ClassNotFoundException("Can not load classes from fragment bundles");
01787       }
01788       if (getUpdatedState() == INSTALLED) {
01789         framework.listeners.frameworkError(this, new BundleException("Unable to resolve bundle: " + bpkgs.getResolveFailReason()));
01790         throw new ClassNotFoundException("Unable to resolve bundle");
01791       }
01792 
01793       ClassLoader cl = getClassLoader();
01794       if (cl == null) {
01795         throw new IllegalStateException("state is uninstalled?");
01796       }
01797       return cl.loadClass(name);
01798     } else {
01799       throw new ClassNotFoundException("No AdminPermission to get class: " + name);
01800     }
01801   }
01802 
01803 
01807   boolean isFragment() {
01808     return fragment != null;
01809   }
01810 
01811 
01815   boolean isExtension() {
01816     return isFragment() &&
01817       fragment.extension != null;      
01818   }
01819 
01824   boolean extensionNeedsRestart() {
01825     return isExtension() &&
01826       (state & (INSTALLED|UNINSTALLED)) != 0;
01827     // &&
01828 //      framework.systemBundle.fragments != null &&
01829 //      framework.systemBundle.fragments.contains(this);
01830   }
01831 
01835   boolean isBootClassPathExtension() {
01836     return isExtension() &&
01837       fragment.extension.equals(Constants.EXTENSION_BOOTCLASSPATH);
01838   }
01839 
01843   boolean isFrameworkExtension() {
01844     return isExtension() &&
01845       fragment.extension.equals(Constants.EXTENSION_FRAMEWORK);
01846   }
01847 
01848 
01852   boolean isAttached() {
01853     return isFragment() &&
01854       fragment.host != null;
01855   }
01856 
01857 
01862   String getFragmentHostName() {
01863     if (isFragment()) {
01864       return fragment.name;
01865     } else {
01866       return null;
01867     }
01868   }
01869 
01870 
01875   BundleImpl getFragmentHost() {
01876     return isFragment() ? fragment.targets() : null;
01877   }
01878 
01879 
01884   boolean isFragmentHost() {
01885     return fragments != null && fragments.size() > 0;
01886   }
01887 
01888 
01892   void attachFragments() {
01893     if (!attachPolicy.equals(Constants.FRAGMENT_ATTACHMENT_NEVER)) {
01894       List hosting = framework.bundles.getFragmentBundles(this);
01895       if (hosting.size() > 0 && secure.okHostBundlePerm(this)) {
01896         // retrieve all fragments this bundle host
01897         for (Iterator iter = hosting.iterator(); iter.hasNext(); ) {
01898           BundleImpl fb = (BundleImpl)iter.next();
01899           if (fb.state == INSTALLED) {
01900             fb.resolveFragment(this);
01901           }
01902         }
01903       }
01904     }
01905   }
01906 
01907 
01911   void attachFragment(BundleImpl fragmentBundle) {
01912     checkUninstalled();
01913     if (attachPolicy.equals(Constants.FRAGMENT_ATTACHMENT_NEVER)) {
01914       throw new IllegalStateException("Bundle does not allow fragments to attach");
01915     }
01916     if (attachPolicy.equals(Constants.FRAGMENT_ATTACHMENT_RESOLVETIME) &&
01917         (state & RESOLVED_FLAGS) != 0) {
01918       throw new IllegalStateException("Bundle does not allow fragments to attach dynamicly");
01919     }
01920 
01921     String failReason = bpkgs.attachFragment(fragmentBundle.bpkgs);
01922     if (failReason != null) {
01923       throw new IllegalStateException(failReason);
01924     }
01925 
01926     if (fragments == null) {
01927       fragments = new ArrayList();
01928     }
01929     int i = 0;
01930     for (; i < fragments.size(); i++) {
01931       BundleImpl b = (BundleImpl)fragments.get(i);
01932       if (b.id > fragmentBundle.id) {
01933         break;
01934       }
01935     }
01936     fragments.add(i, fragmentBundle);
01937   }
01938 
01939 
01943   Iterator getFragments() {
01944     return fragments == null ? 
01945       new ArrayList(0).iterator() : fragments.iterator();
01946   }
01947 
01948 
01952   private void detachFragments(boolean sendEvent) {
01953     if (fragments != null) {
01954       while (fragments.size() > 0) {
01955         detachFragment((BundleImpl)fragments.get(0), sendEvent);
01956       }
01957     }
01958   }
01959 
01960 
01964   private void detachFragment(BundleImpl fb, boolean sendEvent) {
01965     // NYI! extensions
01966     if (fragments.remove(fb)) {
01967       // NYI purge control
01968       bpkgs.detachFragment(fb);
01969       if (fb.state != UNINSTALLED) {
01970         fb.setStateInstalled(sendEvent);
01971       }
01972     }
01973   }
01974 
01978   private void checkUninstalled() {
01979     if (state == UNINSTALLED) {
01980       throw new IllegalStateException("Bundle is in UNINSTALLED state");
01981     }
01982   }
01983 
01984 
01987   class Fragment {
01988     final String name;
01989     final String extension;
01990     final VersionRange versionRange;
01991     BundleImpl host = null;
01992     
01993     Fragment(String name, String extension, String range) {
01994       this.name = name;
01995       this.extension = extension;
01996       this.versionRange = range == null ?
01997         VersionRange.defaultVersionRange :     
01998         new VersionRange(range);
01999     }
02000     
02001     void setHost(BundleImpl host) {
02002       this.host = host;
02003     }
02004 
02005     BundleImpl targets() {
02006       if (host != null) {
02007         return host;
02008       }
02009 
02010       List bundles = framework.bundles.getBundles(name, versionRange);
02011 
02012       if (bundles.isEmpty()) {
02013         return null;
02014       }
02015 
02016       BundleImpl best = null;
02017 
02018       for (Iterator iter = bundles.iterator(); iter.hasNext(); ) {
02019         BundleImpl challenger = (BundleImpl)iter.next();
02020 
02021         if (challenger.state != UNINSTALLED &&
02022             !challenger.attachPolicy.
02023             equals(Constants.FRAGMENT_ATTACHMENT_NEVER) &&
02024             (best == null ||
02025              challenger.version.compareTo(best.version) > 0)) {
02026 
02027           best = challenger;
02028         }
02029       }
02030 
02031       return best;
02032     }
02033   }
02034 
02035 }//class

Generated on Mon Jan 11 21:19:13 2010 for OpenMobileIS by  doxygen 1.5.4