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.knopflerfish.framework.*;
00038 import org.osgi.framework.*;
00039 import java.io.*;
00040 import java.net.*;
00041
00042 import java.util.Enumeration;
00043 import java.util.StringTokenizer;
00044 import java.util.Iterator;
00045 import java.util.Vector;
00046 import java.util.jar.*;
00047 import java.util.zip.*;
00048 import java.util.Hashtable;
00049
00057 class Archive {
00058
00062 final private static String ARCHIVE = "jar";
00063
00067 final private static String SUBDIR = "sub";
00068
00069
00070
00071 final private static String OSGI_OPT_DIR = "OSGI-OPT/";
00072
00077 final private static boolean unpack = new Boolean(System.getProperty("org.knopflerfish.framework.bundlestorage.file.unpack", "true")).booleanValue();
00078
00083 final private static boolean alwaysUnpack = new Boolean(System.getProperty("org.knopflerfish.framework.bundlestorage.file.always_unpack", "false")).booleanValue();
00084
00089 boolean bReference = new Boolean(System.getProperty("org.knopflerfish.framework.bundlestorage.file.reference", "false")).booleanValue();
00090
00094 private FileTree file;
00095
00099 private ZipFile jar;
00100
00104 Manifest manifest ;
00105
00110 private ZipEntry subJar ;
00111
00122 Archive(File dir, int rev, InputStream is) throws IOException {
00123 this(dir, rev, is, null);
00124 }
00125
00126 FileTree refFile = null;
00127
00128 Archive(File dir, int rev, InputStream is, URL source) throws IOException {
00129 BufferedInputStream bis;
00130
00131 if(System.getProperty("java.vendor").startsWith("Skelmir")){
00132 bis = new BufferedInputStream(is, is.available());
00133 }
00134 else{
00135 bis = new BufferedInputStream(is, 8192);
00136 }
00137
00138 ZipInputStream zi = null;
00139
00140
00141 if(source != null && "reference".equals(source.getProtocol())) {
00142 bReference = true;
00143 }
00144
00145 if (alwaysUnpack) {
00146 zi = new ZipInputStream(bis);
00147 } else if (unpack) {
00148
00149 if(System.getProperty("java.vendor").startsWith("Skelmir")){
00150 bis.mark(98304);
00151 }
00152 else{
00153
00154 bis.mark(16000);
00155 }
00156 JarInputStream ji = new JarInputStream(bis);
00157 manifest = ji.getManifest();
00158 if (manifest != null) {
00159 if (checkManifest()) {
00160 zi = ji;
00161 } else {
00162 bis.reset();
00163 }
00164 } else {
00165
00166
00167 bis.reset();
00168 }
00169 }
00170 file = new FileTree(dir, ARCHIVE + rev);
00171
00172
00173 if (zi != null) {
00174 File f;
00175 file.mkdirs();
00176 if (manifest != null) {
00177 f = new File(file, "META-INF");
00178 f.mkdir();
00179 BufferedOutputStream o = new BufferedOutputStream(new FileOutputStream(new File(f, "MANIFEST.MF")));
00180 try {
00181 manifest.write(o);
00182 } finally {
00183 o.close();
00184 }
00185 }
00186 ZipEntry ze;
00187 while ((ze = zi.getNextEntry()) != null) {
00188 if (!ze.isDirectory()) {
00189 if (isSkipped(ze.getName())) {
00190
00191 } else {
00192 StringTokenizer st = new StringTokenizer(ze.getName(), "/");
00193 f = new File(file, st.nextToken());
00194 while (st.hasMoreTokens()) {
00195 f.mkdir();
00196 f = new File(f, st.nextToken());
00197 }
00198 loadFile(f, zi, true);
00199 }
00200 }
00201 }
00202 jar = null;
00203 } else {
00204
00205
00206 if (source != null && bReference && "file".equals(source.getProtocol())) {
00207 refFile = new FileTree(source.getFile());
00208 jar = new ZipFile(refFile);
00209 } else {
00210 loadFile(file, bis, true);
00211 jar = new ZipFile(file);
00212 }
00213 }
00214 if (manifest == null) {
00215 manifest = getManifest();
00216 checkManifest();
00217 }
00218 }
00219
00220
00225 boolean isSkipped(String pathName) {
00226 return pathName.startsWith(OSGI_OPT_DIR);
00227 }
00228
00229
00236 Archive(File dir, int rev, String location) throws IOException {
00237 String [] f = dir.list();
00238 file = null;
00239 if (rev != -1) {
00240 file = new FileTree(dir, ARCHIVE + rev);
00241 } else {
00242 rev = Integer.MAX_VALUE;
00243 for (int i = 0; i < f.length; i++) {
00244 if (f[i].startsWith(ARCHIVE)) {
00245 try {
00246 int c = Integer.parseInt(f[i].substring(ARCHIVE.length()));
00247 if (c < rev) {
00248 rev = c;
00249 file = new FileTree(dir, f[i]);
00250 }
00251 } catch (NumberFormatException ignore) { }
00252 }
00253 }
00254 }
00255 for (int i = 0; i < f.length; i++) {
00256 if (f[i].startsWith(ARCHIVE)) {
00257 try {
00258 int c = Integer.parseInt(f[i].substring(ARCHIVE.length()));
00259 if (c != rev) {
00260 (new FileTree(dir, f[i])).delete();
00261 }
00262 } catch (NumberFormatException ignore) { }
00263 }
00264 if (f[i].startsWith(SUBDIR)) {
00265 try {
00266 int c = Integer.parseInt(f[i].substring(SUBDIR.length()));
00267 if (c != rev) {
00268 (new FileTree(dir, f[i])).delete();
00269 }
00270 } catch (NumberFormatException ignore) { }
00271 }
00272 }
00273 if (file == null || !file.exists()) {
00274 if(bReference && (location != null)) {
00275 try {
00276 URL source = new URL(location);
00277 if("file".equals(source.getProtocol())) {
00278 refFile = file = new FileTree(source.getFile());
00279 }
00280 } catch (Exception e) {
00281 throw new IOException("Bad file URL stored in referenced jar in: " +
00282 dir.getAbsolutePath() +
00283 ", location=" + location);
00284 }
00285 }
00286 if(file == null || !file.exists()) {
00287 throw new IOException("No saved jar file found in: " + dir.getAbsolutePath() + ", old location=" + location);
00288 }
00289 }
00290
00291 if (file.isDirectory()) {
00292 jar = null;
00293 } else {
00294 jar = new ZipFile(file);
00295 }
00296 manifest = getManifest();
00297 }
00298
00299
00310 Archive(Archive a, String path) throws IOException {
00311 if (a.jar != null) {
00312 jar = a.jar;
00313 subJar = jar.getEntry(path);
00314 if (subJar == null) {
00315 throw new IOException("No such JAR component: " + path);
00316 }
00317
00318 if (subJar.isDirectory() && !path.endsWith("/")) {
00319 subJar = jar.getEntry(path + "/");
00320 }
00321 file = a.file;
00322 } else {
00323 file = findFile(a.file, path);
00324 if (file.isDirectory()) {
00325 jar = null;
00326 } else {
00327 jar = new ZipFile(file);
00328 }
00329 }
00330 }
00331
00332
00338 public String toString() {
00339 if (subJar != null) {
00340 return file.getAbsolutePath() + "(" + subJar.getName() + ")";
00341 } else {
00342 return file.getAbsolutePath();
00343 }
00344 }
00345
00346
00352 int getRevision() {
00353 try {
00354 return Integer.parseInt(file.getName().substring(ARCHIVE.length()));
00355 } catch (NumberFormatException ignore) {
00356
00357 return -1;
00358 }
00359 }
00360
00361
00368 String getAttribute(String key) {
00369 Attributes a = manifest.getMainAttributes();
00370 if (a != null) {
00371 return a.getValue(key);
00372 }
00373 return null;
00374 }
00375
00376
00385 byte[] getClassBytes(String classFile) throws IOException {
00386 if(bClosed) {
00387 return null;
00388 }
00389 InputFlow cif = getInputFlow(classFile);
00390 if (cif != null) {
00391 byte[] bytes;
00392 if (cif.length >= 0) {
00393 bytes = new byte[(int)cif.length];
00394 DataInputStream dis = new DataInputStream(cif.is);
00395 dis.readFully(bytes);
00396 } else {
00397 bytes = new byte[0];
00398 byte[] tmp = new byte[8192];
00399 try {
00400 int len;
00401 while ((len = cif.is.read(tmp)) > 0) {
00402 byte[] oldbytes = bytes;
00403 bytes = new byte[oldbytes.length + len];
00404 System.arraycopy(oldbytes, 0, bytes, 0, oldbytes.length);
00405 System.arraycopy(tmp, 0, bytes, oldbytes.length, len);
00406 }
00407 }
00408 catch (EOFException ignore) {
00409
00410
00411 }
00412 }
00413 cif.is.close();
00414 return bytes;
00415 } else {
00416 return null;
00417 }
00418 }
00419
00420
00427 InputFlow getInputFlow(String component) {
00428 if(bClosed) {
00429 return null;
00430 }
00431 if (component.startsWith("/")) {
00432 throw new RuntimeException("Assert! Path should never start with / here");
00433 }
00434 ZipEntry ze;
00435 try {
00436 if (jar != null) {
00437 if (subJar != null) {
00438 if (subJar.isDirectory()) {
00439 ze = jar.getEntry(subJar.getName() + component);
00440 } else {
00441 JarInputStream ji = new JarInputStream(jar.getInputStream(subJar));
00442 do {
00443 ze = ji.getNextJarEntry();
00444 if (ze == null) {
00445 ji.close();
00446 return null;
00447 }
00448 } while (!component.equals(ze.getName()));
00449 return new InputFlow((InputStream)ji, ze.getSize());
00450 }
00451 } else {
00452 ze = jar.getEntry(component);
00453 }
00454 return ze != null ? new InputFlow(jar.getInputStream(ze), ze.getSize()) : null;
00455 } else {
00456 File f = findFile(file, component);
00457 return f.exists() ? new InputFlow(new FileInputStream(f), f.length()) : null;
00458 }
00459 } catch (IOException ignore) {
00460 return null;
00461 }
00462 }
00463
00464
00465
00466 Enumeration findResourcesPath(String path) {
00467 Vector answer = new Vector();
00468 if (jar != null) {
00469 ZipEntry entry;
00470
00471 path.replace('\\', '/');
00472 if (path.startsWith("/")){
00473 path = path.substring(1);
00474 }
00475 if (!path.endsWith("/")){
00476 if (path.length() > 1){
00477 path += "/";
00478 }
00479 }
00480
00481 Enumeration entries = jar.entries();
00482 while (entries.hasMoreElements()){
00483 entry = (ZipEntry) entries.nextElement();
00484 String name = entry.getName();
00485 if (name.startsWith(path)){
00486 int idx = name.lastIndexOf('/');
00487 if (entry.isDirectory()){
00488 idx = name.substring(0, idx).lastIndexOf('/');
00489 }
00490 if (idx > 0){
00491 if (name.substring(0, idx + 1).equals(path)){
00492 answer.add(name);
00493 }
00494 } else if (path.equals("")){
00495 answer.add(name);
00496 }
00497 }
00498 }
00499 } else {
00500 File f = findFile(file, path);
00501 if (!f.exists()) {
00502 return null;
00503 }
00504 if (!f.isDirectory()) {
00505 return null;
00506 }
00507 File[] files = f.listFiles();
00508 int length = files.length;
00509 for(int i = 0; i < length; i++){
00510 String filePath = files[i].getPath();
00511 filePath = filePath.substring(file.getPath().length() + 1);
00512 filePath.replace(File.separatorChar, '/');
00513 if(files[i].isDirectory()){
00514 filePath += "/";
00515 }
00516 answer.add(filePath);
00517 }
00518 }
00519
00520 if(answer.size() == 0){
00521 return null;
00522 }
00523 return answer.elements();
00524 }
00525
00526
00535 Archive getSubArchive(String path) throws IOException {
00536 if(bClosed) {
00537 return null;
00538 }
00539 return new Archive(this, path);
00540 }
00541
00542
00549 String getNativeLibrary(String path) throws IOException {
00550 if(bClosed) {
00551 throw new IOException("Archive is closed");
00552 }
00553 if (path.startsWith("/")) {
00554 path = path.substring(1);
00555 }
00556 File lib;
00557 if (jar != null) {
00558 lib = getSubFile(this, path);
00559 if (!lib.exists()) {
00560 (new File(lib.getParent())).mkdirs();
00561 ZipEntry ze = jar.getEntry(path);
00562 if (ze != null) {
00563 InputStream is = jar.getInputStream(ze);
00564 try {
00565 loadFile(lib, is, false);
00566 } finally {
00567 is.close();
00568 }
00569 } else {
00570 throw new FileNotFoundException("No such sub-archive: " + path);
00571 }
00572 }
00573 } else {
00574 lib = findFile(file, path);
00575
00576 if (!lib.exists() && (lib.getParent() != null)) {
00577 final String libname = lib.getName();
00578 File[] list = lib.getParentFile().listFiles(new FilenameFilter() {
00579 public boolean accept(File dir, String name) {
00580 int pos = name.lastIndexOf(libname);
00581 return ((pos > 1) && (name.charAt(pos - 1) == '_'));
00582 }
00583 });
00584 if (list.length > 0) {
00585 list[0].renameTo(lib);
00586 }
00587 }
00588
00589 }
00590 return lib.getAbsolutePath();
00591 }
00592
00593
00597 void purge() {
00598 close();
00599
00600 file.delete();
00601
00602 getSubFileTree(this).delete();
00603 }
00604
00605 boolean bClosed = false;
00606
00611 void close() {
00612 bClosed = true;
00613 if (subJar == null && jar != null) {
00614 try {
00615 jar.close();
00616 } catch (IOException ignore) {}
00617 }
00618 }
00619
00620
00621
00622
00623
00624
00625
00634 private boolean checkManifest() {
00635 Attributes a = manifest.getMainAttributes();
00636 Util.parseEntries(Constants.EXPORT_PACKAGE, a.getValue(Constants.EXPORT_PACKAGE),
00637 false, true, false);
00638 Util.parseEntries(Constants.IMPORT_PACKAGE, a.getValue(Constants.IMPORT_PACKAGE),
00639 false, true, false);
00640 Iterator nc = Util.parseEntries(Constants.BUNDLE_NATIVECODE,
00641 a.getValue(Constants.BUNDLE_NATIVECODE),
00642 false, false, false);
00643 String bc = a.getValue(Constants.BUNDLE_CLASSPATH);
00644 return (bc != null && !bc.trim().equals(".")) || nc.hasNext();
00645 }
00646
00647
00657 private FileTree findFile(File root, String path) {
00658 return new FileTree(root, path.replace('/', File.separatorChar));
00659 }
00660
00666 private Manifest getManifest() throws IOException {
00667
00668 InputFlow mif = getInputFlow("META-INF/MANIFEST.MF");
00669 if (mif != null) {
00670 return new Manifest(mif.is);
00671 } else {
00672 throw new IOException("Manifest is missing");
00673 }
00674 }
00675
00676
00683 private FileTree getSubFileTree(Archive archive) {
00684 return new FileTree(archive.file.getParent(),
00685 SUBDIR + archive.file.getName().substring(ARCHIVE.length()));
00686 }
00687
00688
00696 private File getSubFile(Archive archive, String path) {
00697 return new File(getSubFileTree(archive), path.replace('/', '-'));
00698 }
00699
00700
00707 private void loadFile(File output, InputStream is, boolean verify) throws IOException {
00708 OutputStream os = null;
00709
00710 try {
00711 os = new FileOutputStream(output);
00712 byte[] buf = new byte[8192];
00713 int n;
00714 try {
00715 while ((n = is.read(buf)) > 0) {
00716 os.write(buf, 0, n);
00717 }
00718 } catch (EOFException ignore) {
00719
00720
00721 }
00722 } catch (IOException e) {
00723 output.delete();
00724 throw e;
00725 } finally {
00726 if (os != null) {
00727 os.close();
00728 }
00729 }
00730 }
00731
00735 String getPath() {
00736 return file.getAbsolutePath();
00737 }
00738
00742 class InputFlow {
00743 final InputStream is;
00744 final long length;
00745
00746 InputFlow(InputStream is, long length) {
00747 this.is = is;
00748 this.length = length;
00749 }
00750 }
00751
00752 }