BundlePackages.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.util.List;
00038 import java.util.ArrayList;
00039 import java.util.Map;
00040 import java.util.HashMap;
00041 import java.util.Iterator;
00042 
00043 import org.osgi.framework.*;
00044 
00045 
00052 class BundlePackages {
00053 
00054   final BundleImpl bundle;
00055 
00056   final int generation;
00057 
00058   private ClassLoader classLoader = null;
00059 
00060   /* Sorted list of exports */
00061   private ArrayList /* ExportPkg */ exports = new ArrayList(1);
00062 
00063   /* Sorted list of imports */
00064   private ArrayList /* ImportPkg */ imports = new ArrayList(1);
00065 
00066   private ArrayList /* String */ dImportPatterns = new ArrayList(1);
00067 
00068   private HashMap /* BundlePackages -> {List(Required),List(ExportPkg),List(ImportPkg)} */ fragments = null;
00069 
00070   /* List of RequireBundle entries. */
00071   ArrayList /* RequireBundle */ require;
00072 
00073   /* List of BundlePackages that require us. */
00074   ArrayList /* BundlePackages */ requiredBy = null;
00075 
00076   /* Sorted list of active imports */
00077   private ArrayList /* ImportPkg */ okImports = null;
00078 
00079   /* Is our packages registered */
00080   private boolean registered = false;
00081 
00082   /* Reason we failed to resolve */
00083   private String failReason = null;
00084 
00085   final static String EMPTY_STRING = "";
00086 
00090   BundlePackages(BundleImpl b, 
00091                  int gen,
00092                  String exportStr, 
00093                  String importStr, 
00094                  String dimportStr,
00095                  String requireStr) {
00096     this.bundle = b;
00097     this.generation = gen;
00098 
00099     Iterator i = Util.parseEntries(Constants.IMPORT_PACKAGE, importStr, false, true, false);
00100     while (i.hasNext()) {
00101       Map e = (Map)i.next();
00102       Iterator pi = ((List)e.remove("keys")).iterator();
00103       ImportPkg ip = new ImportPkg((String)pi.next(), e, this);
00104       for (;;) {
00105         int ii = Util.binarySearch(imports, ipComp, ip);
00106         if (ii < 0) {
00107           imports.add(-ii - 1, ip);
00108         } else {
00109           throw new IllegalArgumentException("Duplicate import definitions for - " + ip.name);
00110         }
00111         if (pi.hasNext()) {
00112           ip = new ImportPkg(ip, (String)pi.next());
00113         } else {
00114           break;
00115         }
00116       }
00117     }
00118 
00119     i = Util.parseEntries(Constants.EXPORT_PACKAGE, exportStr, false, true, false);
00120     while (i.hasNext()) {
00121       Map e = (Map)i.next();
00122       Iterator pi = ((List)e.remove("keys")).iterator();
00123       ExportPkg ep = new ExportPkg((String)pi.next(), e, this);
00124       for (;;) {
00125         int ei = Math.abs(Util.binarySearch(exports, epComp, ep) + 1);
00126         exports.add(ei, ep);
00127         if (!b.v2Manifest) {
00128           ImportPkg ip = new ImportPkg(ep);
00129           int ii = Util.binarySearch(imports, ipComp, ip);
00130           if (ii < 0) {
00131             imports.add(-ii - 1, ip);
00132           }
00133         }
00134         if (pi.hasNext()) {
00135           ep = new ExportPkg(ep, (String)pi.next());
00136         } else {
00137           break;
00138         }
00139       }
00140     }
00141 
00142     i = Util.parseEntries(Constants.DYNAMICIMPORT_PACKAGE, dimportStr, false, true, false);
00143     while (i.hasNext()) {
00144       Map e = (Map)i.next();
00145       if (e.containsKey(Constants.RESOLUTION_DIRECTIVE)) {
00146         throw new IllegalArgumentException(Constants.DYNAMICIMPORT_PACKAGE +
00147                                            " entry illegal contains a " +
00148                                            Constants.RESOLUTION_DIRECTIVE +
00149                                            " directive.");
00150       }
00151       ImportPkg tmpl = null;
00152       for (Iterator pi = ((List)e.remove("keys")).iterator(); pi.hasNext(); ) {
00153         String key = (String)pi.next();
00154         if (key.equals("*")) {
00155           key = EMPTY_STRING;
00156         } else if (key.endsWith(".*")) {
00157           key = key.substring(0, key.length() - 1);
00158         } else if (key.endsWith(".")) {
00159           throw new IllegalArgumentException(Constants.DYNAMICIMPORT_PACKAGE +
00160                                              " entry ends with '.': " + key);
00161         } else if (key.indexOf("*") != - 1) {
00162           throw new IllegalArgumentException(Constants.DYNAMICIMPORT_PACKAGE +
00163                                            " entry contains a '*': " + key);
00164         }
00165         if (tmpl != null) {
00166           dImportPatterns.add(new ImportPkg(tmpl, key));
00167         } else {
00168           tmpl = new ImportPkg(key, e, this);
00169           dImportPatterns.add(tmpl);
00170         }
00171       }
00172     }
00173     i = Util.parseEntries(Constants.REQUIRE_BUNDLE, requireStr, true, true, false);
00174     if (i.hasNext()) {
00175       require = new ArrayList();
00176       do {
00177         Map e = (Map)i.next();
00178         require.add(new RequireBundle(this,
00179                                       (String)e.get("key"),
00180                                       (String)e.get(Constants.VISIBILITY_DIRECTIVE),
00181                                       (String)e.get(Constants.RESOLUTION_DIRECTIVE),
00182                                       (String)e.get(Constants.BUNDLE_VERSION_ATTRIBUTE)));
00183         // NYI warn about unknown directives?
00184       } while (i.hasNext());
00185     } else {
00186       require = null;
00187     }
00188 
00189   }
00190 
00191 
00196   void registerPackages() {
00197     bundle.framework.packages.registerPackages(exports.iterator(), imports.iterator());
00198     registered = true;
00199   }
00200 
00201 
00206   synchronized boolean unregisterPackages(boolean force) {
00207     if (registered) {
00208       List i = okImports != null ? okImports : imports;
00209       if (bundle.framework.packages.unregisterPackages(exports, i, force)) {
00210         okImports = null;
00211         registered = false;
00212       } else {
00213         return false;
00214       }
00215     }
00216     return true;
00217   }
00218 
00219 
00227   boolean resolvePackages() {
00228     ArrayList permImports = new ArrayList(imports.size());
00229     failReason = bundle.framework.perm.missingMandatoryPackagePermissions(this, permImports);
00230     if (failReason != null) {
00231       return false;
00232     }
00233     failReason = bundle.framework.packages.resolve(bundle, permImports.iterator());
00234     if (failReason == null) {
00235       for (Iterator i = permImports.iterator(); i.hasNext(); ) {
00236         ImportPkg ip = (ImportPkg)i.next();
00237         if (ip.provider == null) { // <=> optional import with unresolved provider
00238           i.remove();
00239         }
00240       } 
00241       okImports = permImports;
00242       return true;
00243     } else {
00244       return false;
00245     }
00246   }
00247 
00248 
00254   String getResolveFailReason() {
00255     return failReason;
00256   }
00257 
00258 
00266   synchronized BundlePackages getProviderBundlePackages(String pkg) {
00267     if (okImports == null) {
00268       return null;
00269     }
00270     int ii = Util.binarySearch(okImports, ipFind, pkg);
00271     if (ii >= 0) {
00272       return ((ImportPkg)okImports.get(ii)).provider.bpkgs;
00273     }
00274     return null;
00275   }
00276 
00277 
00285   synchronized BundlePackages getDynamicProviderBundlePackages(String pkg) {
00286     if (okImports == null) {
00287       return null;
00288     }
00289     int ii = Util.binarySearch(okImports, ipFind, pkg);
00290     if (ii >= 0) {
00291       return ((ImportPkg)okImports.get(ii)).provider.bpkgs;
00292     }
00293     if (bundle.framework.perm.hasImportPackagePermission(bundle, pkg)) {
00294       for (Iterator i = dImportPatterns.iterator(); i.hasNext(); ) {
00295         ImportPkg ip = (ImportPkg)i.next();
00296         if (ip.name == EMPTY_STRING ||
00297             (ip.name.endsWith(".") && pkg.startsWith(ip.name)) ||
00298             pkg.equals(ip.name)) {
00299           ImportPkg nip = new ImportPkg(ip, pkg);
00300           ExportPkg ep = bundle.framework.packages.registerDynamicImport(nip);
00301           if (ep != null) {
00302             nip.provider = ep;
00303             okImports.add(-ii - 1, nip);
00304             return ep.bpkgs;
00305           }
00306         }
00307       }
00308     }
00309     return null;
00310   }
00311 
00312 
00321   ArrayList getRequiredBundlePackages(String pkg) {
00322     if (require != null) {
00323       ArrayList res = new ArrayList();
00324       for (Iterator i = require.iterator(); i.hasNext(); ) {
00325         RequireBundle rb = (RequireBundle)i.next();
00326         if (rb.bpkgs != null && rb.bpkgs.getExport(pkg) != null) {
00327           res.add(rb.bpkgs);
00328         }
00329       }
00330       if (!res.isEmpty()) {
00331         return res;
00332       }
00333     }
00334     return null;
00335   }
00336 
00337 
00344   List getRequiredBy() {
00345     if (requiredBy != null) {
00346       return (List)requiredBy.clone();
00347     }
00348     return new ArrayList(0);
00349   }
00350 
00351 
00357   void checkReExport(ExportPkg ep) {
00358     int i = Util.binarySearch(exports, epFind, ep.name);
00359     if (i < 0) {
00360       ExportPkg nep = new ExportPkg(ep, this);
00361       exports.add(-i - 1, nep);
00362       // Perhaps we should avoid this shortcut and go through Packages.
00363       ep.pkg.addExporter(nep);
00364     }
00365   }
00366 
00367 
00373   ExportPkg getExport(String pkg) {
00374     int i = Util.binarySearch(exports, epFind, pkg);
00375     if (i >= 0) {
00376       return (ExportPkg)exports.get(i);
00377     } else {
00378       return null;
00379     }
00380   }
00381 
00382 
00388   Iterator getExports() {
00389     return exports.iterator();
00390   }
00391   
00396   void addExport(ExportPkg pkg) {
00397     int ei = Math.abs(Util.binarySearch(exports, epComp, pkg) + 1);
00398     exports.add(ei, pkg);
00399   }
00400   
00401 
00406   void removeExport(ExportPkg pkg) {
00407     int ei = Util.binarySearch(exports, epComp, pkg);
00408     exports.remove(ei);
00409   }
00410 
00411   
00416   ImportPkg getImport(String pkg) {
00417     int i = Util.binarySearch(imports, ipFind, pkg);
00418     if (i >= 0) {
00419       return (ImportPkg)imports.get(i);
00420     } else {
00421       return null;
00422     }
00423   }
00424 
00425 
00431   Iterator getImports() {
00432     return imports.iterator();
00433   }
00434 
00435 
00441   Iterator getActiveImports() {
00442     return okImports.iterator();
00443   }
00444 
00445 
00451   ClassLoader getClassLoader() {
00452     if (classLoader == null) {
00453       classLoader = bundle.getClassLoader(this);
00454     }
00455     return classLoader;
00456   }
00457 
00458 
00463   void invalidateClassLoader() {
00464     classLoader = null;
00465   }
00466 
00467 
00473   boolean isRegistered() {
00474     return registered;
00475   }
00476 
00477 
00483   String attachFragment(BundlePackages fbpkgs) {
00484     if (fragments == null) {
00485       fragments = new HashMap();
00486     } else if (fragments.containsKey(fbpkgs.bundle)) {
00487       throw new RuntimeException("Fragments packages already attached: " + fbpkgs);
00488     }
00489 
00490     /* make sure that the fragment's bundle does not
00491        conflict with this bundle's (see 3.1.4 r4-core) */
00492     for (Iterator iiter = fbpkgs.getImports(); iiter.hasNext(); ) {
00493       ImportPkg fip = (ImportPkg)iiter.next();
00494       ImportPkg ip = getImport(fip.name);
00495 
00496       if (ip == null && bundle.state != Bundle.INSTALLED) {
00497         return "Can not dynamicly attach, because host has no import for: " + fip;
00498       }
00499       
00500       if (ip != null) {
00501         if (!fip.overlap(ip)) {
00502           return "Host bundle import package constraints need to be stricter" +
00503             "then the import package contraints in the attaching fragment.";
00504         }
00505       }
00506     }
00507 
00508     ArrayList newRequired = new ArrayList();
00509     if (fbpkgs.require != null) {
00510       for (Iterator iter = fbpkgs.require.iterator(); iter.hasNext(); ) {
00511         RequireBundle fragReq = (RequireBundle)iter.next();
00512         boolean match = false;
00513 
00514         if (require != null) {
00515           // check for conflicts
00516           for (Iterator iter2 = require.iterator(); iter2.hasNext(); ) {
00517             RequireBundle req = (RequireBundle)iter2.next();
00518             if (fragReq.name.equals(req.name)) {
00519               if (fragReq.overlap(req)) {
00520                 match = true;
00521               } else {
00522                 return "Fragment bundle required bundle does not completely overlap " +
00523                   "required bundle in host bundle.";
00524               }
00525             }
00526           }
00527         }
00528         if (!match) {
00529           if (bundle.state != Bundle.INSTALLED) {
00530             return "Can not attach a fragment with new required bundles to a resolved host";
00531           }
00532           newRequired.add(fragReq);
00533         }
00534       }
00535     }
00536     Iterator riter = newRequired.iterator();
00537     if (riter.hasNext()) {
00538       int rpos;
00539       if (require == null) {
00540         require = new ArrayList();
00541         rpos = 0;
00542       } else {
00543         rpos = require.size();
00544         for (; rpos > 0; rpos--) {
00545           RequireBundle rb = (RequireBundle)require.get(rpos - 1);
00546           if (this == rb.bpkgs) {
00547             break;
00548           }
00549           if (rb.bpkgs.bundle.id < fbpkgs.bundle.id) {
00550             break;
00551           }
00552         }
00553       }
00554       do {
00555         require.add(rpos++, (RequireBundle)riter.next());
00556       } while (riter.hasNext());
00557     }
00558 
00559     ArrayList newExports = new ArrayList();
00560     for (Iterator eiter = fbpkgs.getExports(); eiter.hasNext(); ) {
00561       ExportPkg fep = (ExportPkg) eiter.next();
00562       int ei = Util.binarySearch(exports, epComp, fep);
00563       if (ei < 0) {
00564         ExportPkg tmp = new ExportPkg(fep, this);
00565         exports.add(-ei - 1, tmp);
00566         newExports.add(tmp);
00567       }
00568     }
00569     
00570     ArrayList newImports = new ArrayList();
00571     for (Iterator iiter = fbpkgs.getImports(); iiter.hasNext(); ) {
00572       ImportPkg fip = (ImportPkg)iiter.next();;
00573       int ii = Util.binarySearch(imports, ipComp, fip);
00574       if (ii < 0) {
00575         ImportPkg tmp = new ImportPkg(fip, this);
00576         imports.add(-ii - 1, tmp);
00577         newImports.add(tmp);
00578       }
00579     }
00580 
00581     bundle.framework.packages.registerPackages(newExports.iterator(), newImports.iterator()); 
00582     fragments.put(fbpkgs.bundle, new ArrayList [] { newRequired, newExports, newImports });
00583     return null;
00584   }
00585 
00586 
00590   void detachFragment(BundleImpl fb) {
00591     if (registered) {
00592       throw new RuntimeException("NYI, detach when bpkgs are registered");
00593     }
00594     List [] added = (List [])fragments.remove(fb);
00595     for (Iterator riter = added[0].iterator(); riter.hasNext(); ) {
00596       require.remove(riter.next());
00597     }
00598     for (Iterator eiter = added[1].iterator(); eiter.hasNext(); ) {
00599       exports.remove(eiter.next());
00600     }
00601     for (Iterator iiter = added[2].iterator(); iiter.hasNext(); ) {
00602       imports.remove(iiter.next());
00603     }
00604   }
00605 
00606 
00612   public String toString() {
00613     return "BundlePackages(id=" + bundle.id + ",gen=" + generation + ")";
00614   }
00615 
00616   //
00617   // Private methods
00618   //
00619   
00620   static final Util.Comparator epComp = new Util.Comparator() {
00630       public int compare(Object oa, Object ob) throws ClassCastException {
00631         ExportPkg a = (ExportPkg)oa;
00632         ExportPkg b = (ExportPkg)ob;
00633         return a.name.compareTo(b.name);
00634       }
00635     };
00636 
00637   static final Util.Comparator epFind = new Util.Comparator() {
00647       public int compare(Object oa, Object ob) throws ClassCastException {
00648         ExportPkg a = (ExportPkg)oa;
00649         String b = (String)ob;
00650         return a.name.compareTo(b);
00651       }
00652     };
00653 
00654   static final Util.Comparator ipComp = new Util.Comparator() {
00664       public int compare(Object oa, Object ob) throws ClassCastException {
00665         ImportPkg a = (ImportPkg)oa;
00666         ImportPkg b = (ImportPkg)ob;
00667         return a.name.compareTo(b.name);
00668       }
00669     };
00670 
00671   static final Util.Comparator ipFind = new Util.Comparator() {
00681       public int compare(Object oa, Object ob) throws ClassCastException {
00682         ImportPkg a = (ImportPkg)oa;
00683         String b = (String)ob;
00684         return a.name.compareTo(b);
00685       }
00686     };
00687 
00688 }

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