Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 336401 - Specifying a directory as a file:// URL in osgi.bundles causes ZipException
Summary: Specifying a directory as a file:// URL in osgi.bundles causes ZipException
Status: RESOLVED WONTFIX
Alias: None
Product: Equinox
Classification: Eclipse Project
Component: Framework (show other bugs)
Version: 3.6.1   Edit
Hardware: PC Windows 7
: P3 minor (vote)
Target Milestone: ---   Edit
Assignee: equinox.framework-inbox CLA
QA Contact:
URL:
Whiteboard: stalebug
Keywords:
Depends on:
Blocks:
 
Reported: 2011-02-04 16:50 EST by Chris Dolan CLA
Modified: 2019-09-16 13:42 EDT (History)
2 users (show)

See Also:


Attachments
Automatically converts a file URL pointing to a directory into a reference URL. (1.15 KB, patch)
2011-02-09 20:21 EST, John Ross CLA
no flags Details | Diff
Automatically converts a file URL pointing to a directory into a reference URL. (1.24 KB, patch)
2011-02-09 20:32 EST, John Ross CLA
no flags Details | Diff
Alternative: Detect directory and throw exception with a more meaningful error message. (1.16 KB, patch)
2011-02-10 08:49 EST, John Ross CLA
no flags Details | Diff
throw meaningful exception (4.91 KB, text/plain)
2011-03-21 10:57 EDT, Thomas Watson CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Chris Dolan CLA 2011-02-04 16:50:23 EST
Build Identifier: org.eclipse.osgi-3.6.1.R36x.jar

My project specifies the osgi.bundles property explicitly in an auto-generated config.ini with file:// URLs containing absolute paths to jars. This works fine. In development, we instead point at directories (the compile output folder for IntelliJ, specifically). This causes the exception below because "bundles\19\1\bundlefile" does not exist.

If I change the entry in config.ini to be just a normal path instead of a file:// path (e.g. "C:\foo\bar@start" instead of "file:/C:/foo/bar@start") then it works great.  

I think the root cause is that a bundle specified by an URL triggers URL.openStream() in EclipseStarter.installBundles() whereas a simple file path is somehow different.

java.util.zip.ZipException: Exception in opening zip file: C:\path\to\my\project\config\org.eclipse.osgi\bundles\19\1\bundlefile
	at org.eclipse.osgi.framework.util.SecureAction.getZipFile(SecureAction.java:264)
	at org.eclipse.osgi.baseadaptor.bundlefile.ZipBundleFile.basicOpen(ZipBundleFile.java:88)
	at org.eclipse.osgi.baseadaptor.bundlefile.ZipBundleFile.getZipFile(ZipBundleFile.java:101)
	at org.eclipse.osgi.baseadaptor.bundlefile.ZipBundleFile.checkedOpen(ZipBundleFile.java:74)
	at org.eclipse.osgi.baseadaptor.bundlefile.ZipBundleFile.getEntry(ZipBundleFile.java:246)
	at org.eclipse.osgi.baseadaptor.BaseData.getEntry0(BaseData.java:111)
	at org.eclipse.osgi.baseadaptor.BaseData$1.run(BaseData.java:105)
	at java.security.AccessController.doPrivileged(Native Method)
	at org.eclipse.osgi.baseadaptor.BaseData.getEntry(BaseData.java:103)
	at org.eclipse.osgi.internal.baseadaptor.AdaptorUtil.loadManifestFrom(AdaptorUtil.java:187)
	at org.eclipse.core.runtime.internal.adaptor.EclipseStorageHook.getGeneratedManifest0(EclipseStorageHook.java:406)
	at org.eclipse.core.runtime.internal.adaptor.EclipseStorageHook$1.run(EclipseStorageHook.java:397)
	at java.security.AccessController.doPrivileged(Native Method)
	at org.eclipse.core.runtime.internal.adaptor.EclipseStorageHook.getGeneratedManifest(EclipseStorageHook.java:395)
	at org.eclipse.core.runtime.internal.adaptor.EclipseStorageHook.createCachedManifest(EclipseStorageHook.java:388)
	at org.eclipse.core.runtime.internal.adaptor.EclipseStorageHook.getManifest(EclipseStorageHook.java:507)
	at org.eclipse.osgi.internal.baseadaptor.BaseStorage.loadManifest(BaseStorage.java:306)
	at org.eclipse.osgi.internal.baseadaptor.BundleInstall.begin(BundleInstall.java:82)
	at org.eclipse.osgi.framework.internal.core.Framework.installWorkerPrivileged(Framework.java:921)
	at org.eclipse.osgi.framework.internal.core.Framework$1.run(Framework.java:837)
	at java.security.AccessController.doPrivileged(Native Method)
	at org.eclipse.osgi.framework.internal.core.Framework.installWorker(Framework.java:888)
	at org.eclipse.osgi.framework.internal.core.Framework.installBundle(Framework.java:832)
	at org.eclipse.osgi.framework.internal.core.BundleContextImpl.installBundle(BundleContextImpl.java:167)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.installBundles(EclipseStarter.java:1050)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.loadBasicBundles(EclipseStarter.java:637)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.startup(EclipseStarter.java:301)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:175)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.main(EclipseStarter.java:150)
	at com.avid.osgi.launcher.Main.start(Main.java:50)
	at com.avid.osgi.launcher.Main.main(Main.java:29)
Caused by: java.util.zip.ZipException: error in opening zip file
	at java.util.zip.ZipFile.open(Native Method)
	at java.util.zip.ZipFile.<init>(ZipFile.java:114)
	at java.util.zip.ZipFile.<init>(ZipFile.java:131)
	at org.eclipse.osgi.framework.util.SecureAction$14.run(SecureAction.java:255)
	at java.security.AccessController.doPrivileged(Native Method)
	at org.eclipse.osgi.framework.util.SecureAction.getZipFile(SecureAction.java:253)
	... 30 more


Reproducible: Always

Steps to Reproduce:
1. create a config.ini that has an osgi.bundles entry with a file:// URL pointing at a directory rather than a jar
2. launch Equinox, specifying that config.ini's parent folder via -configuration
Comment 1 Thomas Watson CLA 2011-02-06 16:32:33 EST
Can you try a reference URL (e.g. reference:file:/C:/foo/bar@start).  That better work because that is how we run from projects within an eclipse workspace.  At any rate, we need to investigate why a normal file: URL to a directory is not working.
Comment 2 John Ross CLA 2011-02-06 16:39:39 EST
Using the reference URL works. I encountered the same issues while writing the ScopeAdmin tests. I just assumed I had been doing something wrong when it started working with the reference URL. I can take a look.
Comment 3 John Ross CLA 2011-02-08 19:27:26 EST
Specifying a file URL string within osgi.bundles results in a call to BundleContext.install(String, InputStream) from EclipseStarter.installBundles. The value of the first argument is the original file URL string with some customizations. The value of the second argument is created from new URL(file URL string).openStream(). When BundleInstall.begin() is reached, because an InputStream was specified that's not a ReferenceInputStream, the code copies the bundle data to a storage area (such as ...\org.eclipse.osgi\bundles\19\1\bundlefile, where bundlefile is a file not a directory). Because the InputStream points to a directory instead of a file, bundlefile ends up being nothing more than a plain-text file with some directory info as opposed to the expected archive file.

Specifying a non-URL file path string within osgi.bundles works because of the legacy code in EclipseStarter.searchForBundle where a malformed URL is assumed to represent a file path and converted into a reference URL.

Specifying a reference URL string within osgi.bundles works because the bundle data is never copied to a new location, and, consequently, a corrupt archive file is never created.
Comment 4 John Ross CLA 2011-02-09 08:51:28 EST
Note this condition can be easily reproduced with code similar to the following.

String location = "file:/C:/workspaces/aries/org.apache.aries.scopeadmin.test/bundles/tb4/";
URL url = new URL(location);
Bundle b = bundleContext.installBundle(location, url.openStream());
Comment 5 Thomas Watson CLA 2011-02-09 08:59:47 EST
I'm not sure what can be done here.  We are giving an input stream (FileInputStream?) to a directory.  Unfortunately at that level of abstraction there is no way to determine if the contents of the stream points to a directory (or even a file) and therefore no way to know that we should do a directory copy instead of simply reading the stream and assume it is a valid jar.

I think when you are running out of a directory you most likely want to use reference installs anyway to prevent the full copy.  This works rather well in the dev environment if you have projects that look like bundles that can be run directly from your output folders in IntelliJ.
Comment 6 John Ross CLA 2011-02-09 09:04:25 EST
(In reply to comment #5)
> We are giving an input stream (FileInputStream?) to a directory.

The underlying stream from new URL(directory).openStream() is a ByteArrayInputStream.
Comment 7 John Ross CLA 2011-02-09 09:40:18 EST
(In reply to comment #4)
> Note this condition can be easily reproduced with code similar to the
> following.
> String location =
> "file:/C:/workspaces/aries/org.apache.aries.scopeadmin.test/bundles/tb4/";
> URL url = new URL(location);
> Bundle b = bundleContext.installBundle(location, url.openStream());

The following code snippet works because, when the InputStream is null, the framework creates its own InputStream with new URL(location).openConnection().getInputStream() and, since it has access to the URL and can query the protocol, knows this represents a file directory and proceeds to do a directory copy instead of a file copy.

String location = "file:/C:/workspaces/aries/org.apache.aries.scopeadmin.test/bundles/tb4/";
Bundle b = bundleContext.installBundle(location);

This doesn't help the EclipseStarter, however, since it wants to customize the location strings which requires it to pass an InputStream.
Comment 8 Chris Dolan CLA 2011-02-09 13:58:15 EST
(In reply to comment #5)
> I think when you are running out of a directory you most likely want to use
> reference installs anyway to prevent the full copy.  This works rather well in
> the dev environment if you have projects that look like bundles that can be run
> directly from your output folders in IntelliJ.

That certainly makes sense, and I'll change my project to do exactly that. But it's not at all obvious from the ZipException that I need to add "reference:" in front of my URLs.

Perhaps the best solution for this defect should be simply a better failure behavior.  That would have prevented me from filing this defect in the first place. Something like this:

if ("file".equals(url.getProtocol()) && new File(url.getPath()).isDirectory()) {
   throw new IllegalArgumentException("use reference:file: instead of file: for  URLs pointing at directories");
}
Comment 9 John Ross CLA 2011-02-09 20:21:56 EST
Created attachment 188640 [details]
Automatically converts a file URL pointing to a directory into a reference URL.

I'm wondering why we wouldn't instead automatically convert a file URL string pointing to a directory into a reference URL. This patch makes a simple modification to EclipseStarter.searchForBundle that does exactly that.
Comment 10 John Ross CLA 2011-02-09 20:32:20 EST
Created attachment 188641 [details]
Automatically converts a file URL pointing to a directory into a reference URL.

Added one additional check to the original patch to make sure the URL is using the file protocol.
Comment 11 Chris Dolan CLA 2011-02-09 21:39:47 EST
(In reply to comment #9)
> I'm wondering why we wouldn't instead automatically convert a file URL string
> pointing to a directory into a reference URL. This patch makes a simple
> modification to EclipseStarter.searchForBundle that does exactly that.

That sounds good to me, as long as users aren't surprised that their directory is not copied to the osgi folder before running.  If the file: URL is a remote filesystem and the network connection fails, that could be an unpleasant surprise. But I imagine that would be an extremely unlikely scenario.
Comment 12 John Ross CLA 2011-02-10 08:23:05 EST
(In reply to comment #11)
> as long as users aren't surprised that their directory
> is not copied to the osgi folder before running. 

Yes, it's a choice between being surprised the directory was not copied and being surprised a standard file URL does not work.
 
> If the file: URL is a remote
> filesystem and the network connection fails, that could be an unpleasant
> surprise.

There is no existing support for remote file URL connections with or without the modification.
Comment 13 John Ross CLA 2011-02-10 08:49:42 EST
Created attachment 188682 [details]
Alternative: Detect directory and throw exception with a more meaningful error message.

This alternate patch takes the approach suggested in comment 8. A file URL pointing to a directory is detected and an exception is thrown with a message suggesting to use a reference URL instead. I'll externalize the message if we decide to go this route.
Comment 14 Thomas Watson CLA 2011-03-21 10:57:23 EDT
Created attachment 191617 [details]
throw meaningful exception

The latest patch will not work for cases where a:

reference:file:<path to directory>

I think the code needs to be refactored to make sense of what is going on.  Here is a patch that attempts to do that and adds a message for cases where file:<path to directory> is used incorrectly.  Unfortunately this is more change than I had anticipated.  I am attaching this patch to consider early next release.
Comment 15 Thomas Watson CLA 2011-03-21 11:11:08 EDT
I opened doc bug340574 to improve the osgi.bundles option documentation.  It is sorely lacking.
Comment 16 Eclipse Genie CLA 2019-09-16 12:36:03 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet.

If you have further information on the current state of the bug, please add it. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.
Comment 17 Thomas Watson CLA 2019-09-16 13:42:30 EDT
At this point there is no plans to address this issue.