BundleArchiveImpl.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.bundlestorage.file;
00036 
00037 import org.osgi.framework.*;
00038 import org.knopflerfish.framework.*;
00039 import java.io.*;
00040 import java.lang.reflect.Method;
00041 import java.util.Enumeration;
00042 import java.util.Vector;
00043 import java.util.Hashtable;
00044 import java.util.StringTokenizer;
00045 import java.util.List;
00046 import java.util.ArrayList;
00047 import java.util.Iterator;
00048 import java.util.HashMap;
00049 import java.util.Map;
00050 import java.util.Properties;
00051 import java.net.URL;
00052 
00062 class BundleArchiveImpl implements BundleArchive
00063 {
00064 
00068   private final static String LOCATION_FILE      = "location";
00069   private final static String REV_FILE           = "revision";
00070   private final static String STOP_FILE          = "stopped";
00071   private final static String STARTLEVEL_FILE    = "startlevel";
00072   private final static String PERSISTENT_FILE    = "persistent";
00073   private final static String LAST_MODIFIED_FILE = "lastModifed";
00074 
00078   private static Method mapLibraryName;
00079 
00080 
00081   private Archive archive;
00082 
00083   private long id;
00084 
00085   private String location;
00086 
00087   private boolean startOnLaunch;
00088 
00089   private FileTree bundleDir;
00090 
00091   private BundleStorageImpl storage;
00092 
00093   private Archive [] archives;
00094 
00095   private Map nativeLibs;
00096 
00097   private Map renameLibs;
00098 
00099   private int startLevel = -1;
00100 
00101   private boolean bPersistent;
00102   
00103   private long lastModified = 0;
00104 
00105   private ArrayList failedPath = null;
00106 
00107   static {
00108     try {
00109       mapLibraryName = System.class.getDeclaredMethod("mapLibraryName", new Class [] { String.class });
00110     } catch (NoSuchMethodException ignore) {
00111       mapLibraryName = null;
00112     }
00113   }
00114 
00115 
00120   BundleArchiveImpl(BundleStorageImpl bundleStorage, 
00121                     FileTree          dir, 
00122                     InputStream       is,
00123                     String            bundleLocation, 
00124                     long              bundleId)
00125     throws Exception
00126   {
00127     URL source = null;
00128     try {
00129       source = new URL(bundleLocation);
00130     } catch (Exception e) {
00131     }
00132     bundleDir       = dir;
00133     archive         = new Archive(bundleDir, 0, is, source);
00134     storage         = bundleStorage;
00135     id              = bundleId;
00136     location        = bundleLocation;
00137     startOnLaunch   = false;
00138     nativeLibs      = getNativeCode();
00139     setClassPath();
00140     putContent(STOP_FILE, new Boolean(!startOnLaunch).toString());
00141     putContent(LOCATION_FILE, location);
00142     //    putContent(STARTLEVEL_FILE, Integer.toString(startLevel));
00143   }
00144 
00149   BundleArchiveImpl(BundleStorageImpl bundleStorage, FileTree dir, long bundleId)
00150     throws Exception
00151   {
00152     bundleDir = dir;
00153     location = getContent(LOCATION_FILE);
00154     if (location == null || location.length() == 0) {
00155       throw new IOException("No bundle location information found");
00156     }
00157     int rev = -1;
00158     String s = getContent(REV_FILE);
00159     if (s != null) {
00160       try {
00161         rev = Integer.parseInt(s);
00162       } catch (NumberFormatException e) { }
00163     }
00164 
00165     s = getContent(STARTLEVEL_FILE);
00166     if (s != null) {
00167       try {
00168         startLevel = Integer.parseInt(s);
00169       } catch (NumberFormatException e) { }
00170     }
00171 
00172     bPersistent = "true".equals(getContent(PERSISTENT_FILE));
00173     
00174     s = getContent(LAST_MODIFIED_FILE);
00175     if (s != null) {
00176         try {
00177                 lastModified = Long.parseLong(s);
00178         } 
00179         catch (NumberFormatException ignore) {}
00180     }
00181 
00182     archive       = new Archive(bundleDir, rev, location);
00183     id            = bundleId;
00184     storage       = bundleStorage;
00185     startOnLaunch = !(new Boolean(getContent(STOP_FILE))).booleanValue();
00186     nativeLibs    = getNativeCode();
00187     setClassPath();
00188   }
00189 
00194   BundleArchiveImpl(BundleArchiveImpl old, InputStream is)
00195     throws Exception
00196   {
00197     bundleDir = old.bundleDir;
00198     location = old.location;
00199     storage = old.storage;
00200     id = old.id;
00201     startOnLaunch = old.startOnLaunch;
00202     bPersistent = old.bPersistent;
00203     int rev = old.archive.getRevision() + 1;
00204     archive = new Archive(bundleDir, rev, is);
00205     nativeLibs = getNativeCode();
00206     setClassPath();
00207     putContent(REV_FILE, Integer.toString(rev));
00208   }
00209 
00210 
00217   public String getAttribute(String key) {
00218     return archive.getAttribute(key);
00219   }
00220 
00224   public Hashtable getLocalizationEntries(String localeFile) {
00225     Archive.InputFlow aif = archive.getInputFlow(localeFile);
00226     if (aif != null) {
00227       Properties l = new Properties();
00228       try {
00229         l.load(aif.is);
00230       } catch (IOException _ignore) { }
00231       try {
00232         aif.is.close();
00233       } catch (IOException _ignore) { }
00234       return l;
00235     } else {
00236       return null;
00237     }
00238   }
00239 
00240 
00244   public HeaderDictionary getUnlocalizedAttributes() {
00245     return new HeaderDictionary(archive.manifest.getMainAttributes());
00246   }
00247 
00248 
00254   public long getBundleId() {
00255     return id;
00256   }
00257 
00258 
00264   public String getBundleLocation() {
00265     return location;
00266   }
00267 
00268 
00269   public int getStartLevel() {
00270     return startLevel;
00271   }
00272 
00273   
00274   public void setStartLevel(int level) throws IOException {
00275     if (startLevel != level) {
00276       startLevel = level;
00277       putContent(STARTLEVEL_FILE, Integer.toString(startLevel));
00278     }
00279   }
00280 
00281 
00282   public void setPersistent(boolean b) throws IOException {
00283     if (bPersistent != b) {
00284       bPersistent = b;
00285       putContent(PERSISTENT_FILE, b ? "true" : "false");
00286     }
00287   }
00288 
00289 
00290   public boolean isPersistent() {
00291     return bPersistent;
00292   }
00293 
00294 
00295   public long getLastModified() {
00296                 return lastModified;
00297   }
00298 
00299   
00300   public void setLastModified(long timemillisecs) throws IOException{
00301           lastModified = timemillisecs;
00302           putContent(LAST_MODIFIED_FILE, Long.toString(timemillisecs));
00303   }
00304   
00305 
00315   public byte[] getClassBytes(Integer sub, String path) throws IOException {
00316     return archives[sub.intValue()].getClassBytes(path);
00317   }
00318 
00319 
00328   public Vector componentExists(String component, boolean onlyFirst) {
00329     Vector v = null;
00330     if (component.startsWith("/")) {
00331       component = component.substring(1);
00332     }
00333     for (int i = 0; i < archives.length; i++) {
00334       Archive.InputFlow aif = archives[i].getInputFlow(component);
00335       if (aif != null) {
00336         if (v == null) {
00337           v = new Vector();
00338         }
00339         v.addElement(new Integer(i));
00340         try {
00341           aif.is.close();
00342         } 
00343         catch (IOException ignore) { }
00344         if (onlyFirst) {
00345           break;
00346         }
00347       }
00348     }
00349     return v;
00350   }
00351 
00352 
00362   public InputStream getInputStream(String component, int ix) {
00363     if (component.startsWith("/")) {
00364       component = component.substring(1);
00365     }
00366     Archive.InputFlow aif;
00367     if (ix == -1) {
00368       aif = archive.getInputFlow(component);
00369     } else {
00370       aif = archives[ix].getInputFlow(component);
00371     }
00372     return aif != null ? aif.is : null;
00373   }
00374 
00375 
00382   public String getNativeLibrary(String libName) {
00383     if (nativeLibs != null) {
00384       try {
00385         String key = (String)mapLibraryName.invoke(null, new Object[] {libName});
00386         String val = (String)nativeLibs.get(key);
00387         File file1 = new File(val);
00388         if (file1.exists() && file1.isFile()) {
00389           if (renameLibs.containsKey(key)) {
00390             File file2 = new File((String)renameLibs.get(key));
00391             if (file1.renameTo(file2)) {
00392               val = file2.getAbsolutePath();
00393               nativeLibs.put(key, val);
00394             }
00395           }
00396           StringBuffer rename = new StringBuffer(val);
00397           int index0 = val.lastIndexOf(File.separatorChar) + 1;
00398           int index1 = val.indexOf("_", index0);
00399           if((index1 > index0) && (index1 == val.length() - key.length() - 1)) {
00400             try {
00401               int prefix = Integer.parseInt(val.substring(index0, index1));
00402               rename.replace(index0, index1, Integer.toString(prefix + 1));
00403             } 
00404             catch (Throwable t) {
00405               rename.insert(index0, "0_");
00406             }
00407           } 
00408           else {
00409             rename.insert(index0, "0_");
00410           }
00411           renameLibs.put(key, rename.toString());
00412         }
00413         return val;
00414       } 
00415       catch (Exception ignore) {      
00416       }
00417     }
00418     return null;
00419   }
00420 
00421 
00427   public boolean getStartOnLaunchFlag() {
00428     return startOnLaunch;
00429   }
00430 
00431 
00437   public void setStartOnLaunchFlag(boolean value) throws IOException {
00438     if (startOnLaunch != value) {
00439       startOnLaunch = value;
00440       putContent(STOP_FILE, new Boolean(!startOnLaunch).toString());
00441     }
00442   }
00443 
00444 
00449   public void purge() {
00450     close();
00451     if (storage.removeArchive(this)) {
00452       (new File(bundleDir, LOCATION_FILE)).delete();
00453       (new File(bundleDir, STOP_FILE)).delete();
00454       (new File(bundleDir, REV_FILE)).delete();
00455       (new File(bundleDir, STARTLEVEL_FILE)).delete();
00456       (new File(bundleDir, PERSISTENT_FILE)).delete();
00457       (new File(bundleDir, LAST_MODIFIED_FILE)).delete();
00458     }
00459     archive.purge();
00460     if (bundleDir.list().length == 0) {
00461       bundleDir.delete();
00462     }
00463   }
00464 
00465 
00470   public void close() {
00471     for (int i = 0; i < archives.length; i++) {
00472       archives[i].close();
00473     }
00474     archive.close();
00475   }
00476 
00477 
00483   public List getFailedClassPathEntries() {
00484     return failedPath;
00485   }
00486 
00487   //
00488   // Private methods
00489   //
00490 
00497   private String getContent(String f) {
00498     DataInputStream in = null;
00499     try {
00500       in = new DataInputStream(new FileInputStream(new File(bundleDir, f)));
00501       return in.readUTF();
00502     } catch (IOException ignore) {
00503     } finally {
00504       if (in != null) {
00505         try {
00506           in.close();
00507         } catch (IOException ignore) { }
00508       }
00509     }
00510     return null;
00511   }
00512 
00522   static boolean isUninstalled(File dir) {
00523     String s = getContent(dir, LAST_MODIFIED_FILE);
00524     if (s == null || s.length() == 0) {
00525       return true;
00526     }
00527     s = getContent(dir, STARTLEVEL_FILE);
00528     int n = -1;
00529     try {
00530       n = Integer.parseInt(s);
00531     } catch (Exception e) {
00532     }
00533     return n == -2;
00534   }
00535 
00536   static String getContent(File dir, String f) {
00537     DataInputStream in = null;
00538     try {
00539       in = new DataInputStream(new FileInputStream(new File(dir, f)));
00540       return in.readUTF();
00541     } catch (IOException ignore) {
00542     } finally {
00543       if (in != null) {
00544         try {
00545           in.close();
00546         } catch (IOException ignore) { }
00547       }
00548     }
00549     return null;
00550   }
00551   
00552 
00560   private void putContent(String f, String content) throws IOException {
00561     DataOutputStream out = null;
00562     try {
00563       out = new DataOutputStream(new FileOutputStream(new File(bundleDir, f)));
00564       out.writeUTF(content);
00565     } finally {
00566       if (out != null) {
00567         out.close();
00568       }
00569     }
00570   }
00571 
00572 
00573   private void setClassPath() throws IOException {
00574     String bcp = getAttribute(Constants.BUNDLE_CLASSPATH);
00575     
00576     if (bcp != null) {
00577       ArrayList a = new ArrayList();
00578       StringTokenizer st = new StringTokenizer(bcp, ",");
00579       while (st.hasMoreTokens()) {
00580         String path = st.nextToken().trim();
00581         if (".".equals(path)) {
00582           a.add(archive);
00583         } else {
00584           try {
00585             a.add(archive.getSubArchive(path));
00586           } catch (IOException ioe) {
00587             if (failedPath == null) {
00588               failedPath = new ArrayList(1);
00589             }
00590             failedPath.add(path);
00591           }
00592         }
00593       }
00594       archives = (Archive [])a.toArray(new Archive[a.size()]);
00595     } 
00596     else {
00597       archives = new Archive[] { archive };
00598     }
00599 
00600   }
00601 
00611   private Map getNativeCode() throws Exception {
00612     String bnc = getAttribute(Constants.BUNDLE_NATIVECODE);
00613     if (bnc != null) {
00614       if (mapLibraryName == null) {
00615         throw new Exception("Native-Code: Not supported on non Java 2 platforms.");
00616       }
00617       String proc = Framework.getProperty(Constants.FRAMEWORK_PROCESSOR);
00618       String os =  Framework.getProperty(Constants.FRAMEWORK_OS_NAME);
00619       Version osVer = new Version(Framework.getProperty(Constants.FRAMEWORK_OS_VERSION));
00620       String osLang = Framework.getProperty(Constants.FRAMEWORK_LANGUAGE);
00621       boolean optional = false;
00622       List best = null;
00623       VersionRange bestVer = null;
00624       boolean bestLang = false;
00625 
00626       for (Iterator i = Util.parseEntries(Constants.BUNDLE_NATIVECODE, bnc, false, false, false); i.hasNext(); ) {
00627         VersionRange matchVer = null;
00628         boolean matchLang = false;
00629         Map params = (Map)i.next();
00630 
00631         List keys = (List)params.get("keys");
00632         if (keys.size() == 1 && "*".equals(keys.get(0)) && !i.hasNext()) {
00633           optional = true;
00634           break;
00635         }
00636 
00637         List pl = (List)params.get(Constants.BUNDLE_NATIVECODE_PROCESSOR);
00638         if (pl != null) {
00639           if (!containsIgnoreCase(pl, Alias.unifyProcessor(proc))) {
00640             continue;
00641           }
00642         } else {
00643           // NYI! Handle null
00644           continue;
00645         }
00646 
00647         List ol = (List)params.get(Constants.BUNDLE_NATIVECODE_OSNAME);
00648         if (ol != null) {
00649           if (!containsIgnoreCase(ol, Alias.unifyOsName(os))) {
00650             continue;
00651           }
00652         } else {
00653           // NYI! Handle null
00654           continue;
00655         }
00656 
00657         List ver = (List)params.get(Constants.BUNDLE_NATIVECODE_OSVERSION);
00658         if (ver != null) {
00659           boolean okVer = false;
00660           for (Iterator v = ver.iterator(); v.hasNext(); ) {
00661             // NYI! Handle format Exception
00662             matchVer = new VersionRange((String)v.next());
00663             if (matchVer.withinRange(osVer)) {
00664               okVer = true;
00665               break;
00666             }
00667           }
00668           if (!okVer) {
00669             continue;
00670           }
00671         }
00672 
00673         List lang = (List)params.get(Constants.BUNDLE_NATIVECODE_LANGUAGE);
00674         if (lang != null) {
00675           for (Iterator l = lang.iterator(); l.hasNext(); ) {
00676             if (osLang.equalsIgnoreCase((String)l.next())) {
00677               // Found specfied language version, search no more
00678               matchLang = true;
00679               break;
00680             }
00681           }
00682           if (!matchLang) {
00683             continue;
00684           }
00685         } 
00686 
00687         List sf = (List)params.get(Constants.SELECTION_FILTER_ATTRIBUTE);
00688         if (sf != null) {
00689           if (sf.size() == 1) {
00690             FilterImpl filter = new FilterImpl((String)sf.get(0));
00691             if (!filter.match(Framework.getProperties())) {
00692               continue;
00693             }
00694           } else {
00695             //NYI! complain about faulty selection
00696           }
00697         }
00698 
00699         // Compare to previous best
00700         if (best != null) {
00701           boolean verEqual = false;
00702           if (bestVer != null) {
00703             if (matchVer == null) {
00704               continue;
00705             }
00706             int d = bestVer.compareTo(matchVer);
00707             if (d == 0) {
00708               verEqual = true;
00709             } else if (d > 0) {
00710               continue;
00711             }
00712           } else if (matchVer == null) {
00713             verEqual = true;
00714           }
00715           if (verEqual && (!matchLang || bestLang)) {
00716             continue;
00717           }
00718         }
00719         best = keys;
00720         bestVer = matchVer;
00721         bestLang = matchLang;
00722       }
00723       if (best == null) {
00724         if (optional) {
00725           return null;
00726         } else {
00727           throw new BundleException("Native-Code: No matching libraries found.");
00728         }
00729       }
00730       renameLibs  = new HashMap();
00731       HashMap res = new HashMap();
00732       for (Iterator p = best.iterator(); p.hasNext();) {
00733         String name = (String)p.next();
00734         int sp = name.lastIndexOf('/');
00735         String key = (sp != -1) ? name.substring(sp+1) : name;
00736         res.put(key, archive.getNativeLibrary(name));
00737       }
00738       return res;
00739     }  else {
00740       // No native code in this bundle
00741       return null;
00742     }
00743   }
00744 
00748   private boolean containsIgnoreCase(List l, List l2) {
00749     for (Iterator i = l.iterator(); i.hasNext(); ) {
00750       String s = (String)i.next();
00751       for (Iterator j = l2.iterator(); j.hasNext(); ) {
00752         if (s.equalsIgnoreCase((String)j.next())) {
00753           return true;
00754         }
00755       }
00756     }
00757     return false;
00758   }
00759 
00760 
00761   public Enumeration findResourcesPath(String path) {
00762     return archive.findResourcesPath(path);
00763   }
00764 
00765 
00766   public String getJarLocation() {
00767     return archive.getPath();
00768   }
00769   
00770 }//class

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