Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 10d657fa9e64ec9b69ce9e3781a3c61ecc74794c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
/*******************************************************************************
 * Copyright (c) 2004, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.osgi.framework.adaptor.core;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.eclipse.osgi.framework.adaptor.BundleData;
import org.eclipse.osgi.framework.debug.Debug;
import org.eclipse.osgi.framework.internal.core.Constants;
import org.eclipse.osgi.framework.internal.protocol.bundleresource.Handler;
import org.osgi.framework.FrameworkEvent;

/**
 * The BundleFile API is used by Adaptors to read resources out of an 
 * installed Bundle in the Framework.
 */
abstract public class BundleFile {
	/**
	 * The File object for this BundleFile.
	 */
	protected File basefile;

	/**
	 * Default constructor
	 *
	 */
	public BundleFile() {
		// do nothing
	}

	/**
	 * BundleFile constructor
	 * @param basefile The File object where this BundleFile is 
	 * persistently stored.
	 */
	public BundleFile(File basefile) {
		this.basefile = basefile;
	}

	/**
	 * Returns a File for the bundle entry specified by the path.
	 * If required the content of the bundle entry is extracted into a file
	 * on the file system.
	 * @param path The path to the entry to locate a File for.
	 * @return A File object to access the contents of the bundle entry.
	 */
	abstract public File getFile(String path);

	/**
	 * Locates a file name in this bundle and returns a BundleEntry object
	 *
	 * @param path path of the entry to locate in the bundle
	 * @return BundleEntry object or null if the file name
	 *         does not exist in the bundle
	 */
	abstract public BundleEntry getEntry(String path);

	/** 
	 * Allows to access the entries of the bundle. 
	 * Since the bundle content is usually a jar, this 
	 * allows to access the jar contents.
	 * 
	 * GetEntryPaths allows to enumerate the content of "path".
	 * If path is a directory, it is equivalent to listing the directory
	 * contents. The returned names are either files or directories 
	 * themselves. If a returned name is a directory, it finishes with a 
	 * slash. If a returned name is a file, it does not finish with a slash.
	 * @param path path of the entry to locate in the bundle
	 * @return an Enumeration of Strings that indicate the paths found or
	 * null if the path does not exist. 
	 */
	abstract public Enumeration getEntryPaths(String path);

	/**
	 * Closes the BundleFile.
	 * @throws IOException if any error occurs.
	 */
	abstract public void close() throws IOException;

	/**
	 * Opens the BundleFiles.
	 * @throws IOException if any error occurs.
	 */
	abstract public void open() throws IOException;

	/**
	 * Determines if any BundleEntries exist in the given directory path.
	 * @param dir The directory path to check existence of.
	 * @return true if the BundleFile contains entries under the given directory path;
	 * false otherwise.
	 */
	abstract public boolean containsDir(String dir);

	/**
	 * Returns a URL to access the contents of the entry specified by the path
	 * @param path the path to the resource
	 * @param hostBundleID the host bundle ID
	 */
	public URL getResourceURL(String path, long hostBundleID) {
		return getResourceURL(path, hostBundleID, 0);
	}

	/**
	 * Returns a URL to access the contents of the entry specified by the path
	 * @param path the path to the resource
	 * @param hostBundleID the host bundle ID
	 * @param index the resource index
	 */
	public URL getResourceURL(String path, long hostBundleID, int index) {
		BundleEntry bundleEntry = getEntry(path);
		if (bundleEntry == null)
			return null;

		try {
			//use the constant string for the protocol to prevent duplication
			return new URL(Constants.OSGI_RESOURCE_URL_PROTOCOL, Long.toString(hostBundleID), index, path, new Handler(bundleEntry));
		} catch (MalformedURLException e) {
			return null;
		}
	}

	/**
	 * A BundleFile that uses a ZipFile as it base file.
	 */
	public static class ZipBundleFile extends BundleFile {
		protected BundleData bundledata;
		protected ZipFile zipFile;
		protected boolean closed = true;

		public ZipBundleFile(File basefile, BundleData bundledata) throws IOException {
			super(basefile);
			if (!basefile.exists())
				throw new IOException(AdaptorMsg.formatter.getString("ADAPTER_FILEEXIST_EXCEPTION", basefile)); //$NON-NLS-1$
			this.bundledata = bundledata;
			this.closed = true;
		}

		protected boolean checkedOpen() {
			try {
				return getZipFile() != null;
			} catch (IOException e) {
				AbstractBundleData abstractData = (AbstractBundleData) bundledata;
				abstractData.getAdaptor().getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, abstractData.getBundle(), e);
				return false;
			}
		}

		protected ZipFile basicOpen() throws IOException {
			return new ZipFile(this.basefile);
		}

		protected ZipFile getZipFile() throws IOException {
			if (closed) {
				zipFile = basicOpen();
				closed = false;
			}
			return zipFile;
		}

		private ZipEntry getZipEntry(String path) {
			if (path.length() > 0 && path.charAt(0) == '/')
				path = path.substring(1);
			ZipEntry entry = zipFile.getEntry(path);
			if (entry != null && entry.getSize() == 0 && !entry.isDirectory()) {
				// work around the directory bug see bug 83542
				ZipEntry dirEntry = zipFile.getEntry(path + '/');
				if (dirEntry != null)
					entry = dirEntry;
			}
			return entry;
		}

		protected File extractDirectory(String dirName) {
			if (!checkedOpen())
				return null;
			Enumeration entries = zipFile.entries();
			while (entries.hasMoreElements()) {
				String entryPath = ((ZipEntry) entries.nextElement()).getName();
				if (entryPath.startsWith(dirName) && !entryPath.endsWith("/")) //$NON-NLS-1$
					getFile(entryPath);
			}
			return getExtractFile(dirName);
		}

		private File getExtractFile(String entryName) {
			if (!(bundledata instanceof AbstractBundleData)) {
				return null;
			}
			File bundleGenerationDir = ((AbstractBundleData) bundledata).createGenerationDir();
			/* if the generation dir exists, then we have place to cache */
			if (bundleGenerationDir != null && bundleGenerationDir.exists()) {
				String path = ".cp"; /* put all these entries in this subdir *///$NON-NLS-1$
				String name = entryName.replace('/', File.separatorChar);
				if ((name.length() > 1) && (name.charAt(0) == File.separatorChar)) /* if name has a leading slash */{
					path = path.concat(name);
				} else {
					path = path + File.separator + name;
				}
				return new File(bundleGenerationDir, path);
			}
			return null;
		}

		public File getFile(String entry) {
			if (!checkedOpen())
				return null;
			ZipEntry zipEntry = getZipEntry(entry);
			if (zipEntry == null) {
				return null;
			}

			try {

				File nested = getExtractFile(zipEntry.getName());
				if (nested != null) {
					if (nested.exists()) {
						/* the entry is already cached */
						if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
							Debug.println("File already present: " + nested.getPath()); //$NON-NLS-1$
						}
					} else {
						if (zipEntry.getName().endsWith("/")) { //$NON-NLS-1$
							if (!nested.mkdirs()) {
								if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
									Debug.println("Unable to create directory: " + nested.getPath()); //$NON-NLS-1$
								}
								throw new IOException(AdaptorMsg.formatter.getString("ADAPTOR_DIRECTORY_CREATE_EXCEPTION", nested.getAbsolutePath())); //$NON-NLS-1$
							}
							extractDirectory(zipEntry.getName());
						} else {
							InputStream in = zipFile.getInputStream(zipEntry);
							if (in == null)
								return null;
							/* the entry has not been cached */
							if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
								Debug.println("Creating file: " + nested.getPath()); //$NON-NLS-1$
							}
							/* create the necessary directories */
							File dir = new File(nested.getParent());
							if (!dir.exists() && !dir.mkdirs()) {
								if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
									Debug.println("Unable to create directory: " + dir.getPath()); //$NON-NLS-1$
								}
								throw new IOException(AdaptorMsg.formatter.getString("ADAPTOR_DIRECTORY_CREATE_EXCEPTION", dir.getAbsolutePath())); //$NON-NLS-1$
							}
							/* copy the entry to the cache */
							AbstractFrameworkAdaptor.readFile(in, nested);
						}
					}

					return nested;
				}
			} catch (IOException e) {
				if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
					Debug.printStackTrace(e);
				}
			}
			return null;
		}

		public boolean containsDir(String dir) {
			if (!checkedOpen())
				return false;
			if (dir == null)
				return false;

			if (dir.length() == 0)
				return true;

			if (dir.charAt(0) == '/') {
				if (dir.length() == 1)
					return true;
				dir = dir.substring(1);
			}

			if (dir.length() > 0 && dir.charAt(dir.length() - 1) != '/')
				dir = dir + '/';

			Enumeration entries = zipFile.entries();
			ZipEntry zipEntry;
			String entryPath;
			while (entries.hasMoreElements()) {
				zipEntry = (ZipEntry) entries.nextElement();
				entryPath = zipEntry.getName();
				if (entryPath.startsWith(dir)) {
					return true;
				}
			}
			return false;
		}

		public BundleEntry getEntry(String path) {
			if (!checkedOpen())
				return null;
			ZipEntry zipEntry = getZipEntry(path);
			if (zipEntry == null) {
				if (path.length() == 0 || path.charAt(path.length() - 1) == '/') {
					// this is a directory request lets see if any entries exist in this directory
					if (containsDir(path))
						return new BundleEntry.DirZipBundleEntry(this, path);
				}
				return null;
			}

			return new BundleEntry.ZipBundleEntry(zipEntry, this);

		}

		public Enumeration getEntryPaths(String path) {
			if (!checkedOpen())
				return null;
			if (path == null) {
				throw new NullPointerException();
			}

			if (path.length() > 0 && path.charAt(0) == '/') {
				path = path.substring(1);
			}
			if (path.length() > 0 && path.charAt(path.length() - 1) != '/') {
				path = new StringBuffer(path).append("/").toString(); //$NON-NLS-1$
			}

			Vector vEntries = new Vector();
			Enumeration entries = zipFile.entries();
			while (entries.hasMoreElements()) {
				ZipEntry zipEntry = (ZipEntry) entries.nextElement();
				String entryPath = zipEntry.getName();
				if (entryPath.startsWith(path)) {
					if (path.length() < entryPath.length()) {
						if (entryPath.lastIndexOf('/') < path.length()) {
							vEntries.add(entryPath);
						} else {
							entryPath = entryPath.substring(path.length());
							int slash = entryPath.indexOf('/');
							entryPath = path + entryPath.substring(0, slash + 1);
							if (!vEntries.contains(entryPath)) {
								vEntries.add(entryPath);
							}
						}
					}
				}
			}
			return vEntries.elements();
		}

		public void close() throws IOException {
			if (!closed) {
				closed = true;
				zipFile.close();
			}
		}

		public void open() {
			//do nothing
		}

	}

	/**
	 * A BundleFile that uses a directory as its base file.
	 */
	public static class DirBundleFile extends BundleFile {

		public DirBundleFile(File basefile) throws IOException {
			super(basefile);
			if (!basefile.exists() || !basefile.isDirectory()) {
				throw new IOException(AdaptorMsg.formatter.getString("ADAPTOR_DIRECTORY_EXCEPTION", basefile)); //$NON-NLS-1$
			}
		}

		public File getFile(String path) {
			File filePath = new File(this.basefile, path);
			if (filePath.exists()) {
				return filePath;
			}
			return null;
		}

		public BundleEntry getEntry(String path) {
			File filePath = new File(this.basefile, path);
			if (!filePath.exists()) {
				return null;
			}
			return new BundleEntry.FileBundleEntry(filePath, path);
		}

		public boolean containsDir(String dir) {
			File dirPath = new File(this.basefile, dir);
			return dirPath.exists() && dirPath.isDirectory();
		}

		public Enumeration getEntryPaths(final String path) {
			final java.io.File pathFile = new java.io.File(basefile, path);
			if (!pathFile.exists())
				return new Enumeration() {
					public boolean hasMoreElements() {
						return false;
					}

					public Object nextElement() {
						throw new NoSuchElementException();
					}
				};
			if (pathFile.isDirectory()) {
				final String[] fileList = pathFile.list();
				final String dirPath = path.length() == 0 || path.charAt(path.length() - 1) == '/' ? path : path + '/';
				return new Enumeration() {
					int cur = 0;

					public boolean hasMoreElements() {
						return fileList != null && cur < fileList.length;
					}

					public Object nextElement() {
						if (!hasMoreElements()) {
							throw new NoSuchElementException();
						}
						java.io.File childFile = new java.io.File(pathFile, fileList[cur]);
						StringBuffer sb = new StringBuffer(dirPath).append(fileList[cur++]);
						if (childFile.isDirectory()) {
							sb.append("/"); //$NON-NLS-1$
						}
						return sb.toString();
					}

				};
			}
			return new Enumeration() {
				int cur = 0;

				public boolean hasMoreElements() {
					return cur < 1;
				}

				public Object nextElement() {
					if (cur == 0) {
						cur = 1;
						return path;
					}
					throw new NoSuchElementException();
				}
			};
		}

		public void close() {
			// nothing to do.
		}

		public void open() {
			// nothing to do.
		}
	}

	/**
	 * A NestedDirBundleFile uses another BundleFile as its source but
	 * accesses all of its resources relative to a nested directory within
	 * the other BundleFile object.  This is used to support zipped bundles
	 * that use a Bundle-ClassPath with an nested directory specified.
	 * <p>
	 * For Example:
	 * <pre>
	 * Bundle-ClassPath: nested.jar,nesteddir/
	 * </pre>
	 */
	public static class NestedDirBundleFile extends BundleFile {
		BundleFile baseBundleFile;
		String cp;

		public NestedDirBundleFile(BundleFile baseBundlefile, String cp) {
			super(baseBundlefile.basefile);
			this.baseBundleFile = baseBundlefile;
			this.cp = cp;
			if (cp.charAt(cp.length() - 1) != '/') {
				this.cp = this.cp + '/';
			}
		}

		public void close() {
			// do nothing.
		}

		public BundleEntry getEntry(String path) {
			if (path.length() > 0 && path.charAt(0) == '/')
				path = path.substring(1);
			String newpath = new StringBuffer(cp).append(path).toString();
			return baseBundleFile.getEntry(newpath);
		}

		public boolean containsDir(String dir) {
			if (dir == null)
				return false;

			if (dir.length() > 0 && dir.charAt(0) == '/')
				dir = dir.substring(1);
			String newdir = new StringBuffer(cp).append(dir).toString();
			return baseBundleFile.containsDir(newdir);
		}

		public Enumeration getEntryPaths(String path) {
			// getEntryPaths is only valid if this is a root bundle file.
			return null;
		}

		public File getFile(String entry) {
			// getFile is only valid if this is a root bundle file.
			return null;
		}

		public void open() {
			// do nothing
		}
	}

	public File getBaseFile() {
		return basefile;
	}
}

Back to the top