00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
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
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
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
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
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
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
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
00696 }
00697 }
00698
00699
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
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 }