Package: eclipse / 3.8.1-10

bundle-info-helper.patch Patch series | download
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
From: Jakub Adam <jakub.adam@ktknet.cz>
Date: Sat, 10 Mar 2012 18:06:03 +0100
Subject: bundle-info-helper

In bundles.info there is a list of bundles load by Equinox simple configurator
on startup. This file is generated when Eclipse is built from sources and its
contents are fixed thereafter. This patch tries to solve a situation when some
of orbit dependencies are upgraded by operating system's package manager.

The bundle's record in bundles.info contains a version number it had at the
time of compilation. When this number becomes different from the version of
bundle that is actually installed in the system, Eclipse rejects to load the
bundle which leads to missing plugins or even inability to start the workbench.

When bundles.info is loaded by simpleconfigurator, the BundleInfoHelper class
inspects OSGi metadata of the installed bundles and replaces the version
number from bundles.info with the real one. It does this only to the runtime
data structures, bundles file is kept read only. After that, simpleconfigurator
is able to load the upgraded bundles.
---
 .../src/org/debian/BundleInfoHelper.java           |  261 ++++++++++++++++++++
 .../utils/SimpleConfiguratorUtils.java             |    9 +-
 2 files changed, 269 insertions(+), 1 deletions(-)
 create mode 100644 eclipse/plugins/org.eclipse.equinox.simpleconfigurator/src/org/debian/BundleInfoHelper.java

diff --git a/eclipse/plugins/org.eclipse.equinox.simpleconfigurator/src/org/debian/BundleInfoHelper.java b/eclipse/plugins/org.eclipse.equinox.simpleconfigurator/src/org/debian/BundleInfoHelper.java
new file mode 100644
index 0000000..0888047
--- /dev/null
+++ b/eclipse/plugins/org.eclipse.equinox.simpleconfigurator/src/org/debian/BundleInfoHelper.java
@@ -0,0 +1,261 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Jakub Adam.
+ * 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
+ * 
+ * The code is based on getOSGiManifest() method from
+ * org.eclipse.equinox.internal.frameworkadmin.utils.Utils and
+ * org.eclipse.osgi.util.ManifestElement classes. The original code copyright
+ * holder is IBM Corporation.
+ *******************************************************************************/
+
+package org.debian;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import org.eclipse.equinox.internal.simpleconfigurator.utils.BundleInfo;
+import org.eclipse.equinox.internal.simpleconfigurator.utils.URIUtil;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+/** 
+ * In bundles.info there is a list of bundles load by Equinox simple configurator
+ * on startup. This file is generated when Eclipse is built from sources and its
+ * contents are fixed thereafter. This class tries to solve a situation when some
+ * of orbit dependencies are upgraded by operating system's package manager.
+ * 
+ * The bundle's record in bundles.info contains a version number it had at the
+ * time of compilation. When this number becomes different from the version of
+ * that is actually installed in the system, Eclipse rejects to load the bundle
+ * which leads to missing plugins or even inability to start the workbench.
+ * 
+ * When bundles.info is loaded by simpleconfigurator, the BundleInfoHelper class
+ * inspects OSGi metadata of the installed bundles and replaces the version
+ * number from bundles.info with the real one. It does this only to the runtime
+ * data structures, bundles file is kept read only. After that, simpleconfigurator
+ * can load the upgraded bundles.
+ */
+public class BundleInfoHelper {
+
+	public static List useDebianBundleVersions(List bundles) {
+		List newBundles = new ArrayList();
+
+		ListIterator it = bundles.listIterator();
+		while (it.hasNext()) {
+			BundleInfo bundle = (BundleInfo) it.next();
+
+			try {
+				URI fullUri;
+				if (bundle.getBaseLocation() != null)
+					fullUri = new URI(bundle.getBaseLocation().toString() + bundle.getLocation().toString());
+				else
+					fullUri = bundle.getLocation();
+
+				Dictionary manifest = getOSGiManifest(fullUri);
+
+				if (manifest != null) {
+					String debianVersion = (String) manifest.get(Constants.BUNDLE_VERSION);
+					debianVersion = Version.parseVersion(debianVersion).toString();
+					BundleInfo newBundle = new BundleInfo(bundle.getSymbolicName(), debianVersion, bundle.getLocation(), bundle.getStartLevel(), bundle.isMarkedAsStarted());
+					newBundle.setBaseLocation(bundle.getBaseLocation());
+					bundle = newBundle;
+				}
+			} catch (URISyntaxException e) {
+				// Use original bundle
+			}
+
+			newBundles.add(bundle);
+		}
+
+		return newBundles;
+	}
+
+	/* Following code is copied from org.eclipse.equinox.internal.frameworkadmin.utils.Utils
+	 * with some minor changes. It is here because required bundle may not be 
+	 * loaded yet at the early time when useDebianBundleVersions() is run. */
+
+	private static final String FILE_SCHEME = "file"; //$NON-NLS-1$
+
+	private static Dictionary getOSGiManifest(URI location) {
+		if (location == null)
+			return null;
+		// if we have a file-based URL that doesn't end in ".jar" then...
+		if (FILE_SCHEME.equals(location.getScheme()))
+			return basicLoadManifest(URIUtil.toFile(location));
+
+		try {
+			URL url = new URL("jar:" + location.toString() + "!/"); //$NON-NLS-1$//$NON-NLS-2$
+			JarURLConnection jarConnection = (JarURLConnection) url.openConnection();
+			ZipFile jar = jarConnection.getJarFile();
+
+			try {
+				ZipEntry entry = jar.getEntry(JarFile.MANIFEST_NAME);
+				if (entry == null)
+					return null;
+
+				Map manifest = parseBundleManifest(jar.getInputStream(entry), null);
+				if (manifest.get(Constants.BUNDLE_SYMBOLICNAME) == null) {
+					return null;
+				}
+				return manifestToProperties(manifest);
+			} catch (Exception e) {
+				return null;
+			} finally {
+				jar.close();
+			}
+		} catch (IOException e) {
+			if (System.getProperty("osgi.debug") != null) {
+				System.err.println("location=" + location);
+				e.printStackTrace();
+			}
+		}
+		return null;
+	}
+
+	//Return a dictionary representing a manifest. The data may result from plugin.xml conversion  
+	private static Dictionary basicLoadManifest(File bundleLocation) {
+		InputStream manifestStream = null;
+		ZipFile jarFile = null;
+		try {
+			try {
+				// Handle a JAR'd bundle
+				if (bundleLocation.isFile()) {
+					jarFile = new ZipFile(bundleLocation, ZipFile.OPEN_READ);
+					ZipEntry manifestEntry = jarFile.getEntry(JarFile.MANIFEST_NAME);
+					if (manifestEntry != null) {
+						manifestStream = jarFile.getInputStream(manifestEntry);
+					}
+				} else {
+					// we have a directory-based bundle
+					File bundleManifestFile = new File(bundleLocation, JarFile.MANIFEST_NAME);
+					if (bundleManifestFile.exists())
+						manifestStream = new BufferedInputStream(new FileInputStream(new File(bundleLocation, JarFile.MANIFEST_NAME)));
+				}
+			} catch (IOException e) {
+				//ignore
+			}
+			try {
+				Map manifest = parseBundleManifest(manifestStream, null);
+				// add this check to handle the case were we read a non-OSGi manifest
+				if (manifest.get(Constants.BUNDLE_SYMBOLICNAME) == null)
+					return null;
+				return manifestToProperties(manifest);
+			} catch (IOException ioe) {
+				return null;
+			} catch (Exception e) {
+				return null;
+			}
+		} finally {
+			try {
+				if (manifestStream != null)
+					manifestStream.close();
+			} catch (IOException e1) {
+				//Ignore
+			}
+			try {
+				if (jarFile != null)
+					jarFile.close();
+			} catch (IOException e2) {
+				//Ignore
+			}
+		}
+	}
+
+	private static Properties manifestToProperties(Map d) {
+		Iterator iter = d.keySet().iterator();
+		Properties result = new Properties();
+		while (iter.hasNext()) {
+			String key = (String) iter.next();
+			result.put(key, d.get(key));
+		}
+		return result;
+	}
+
+	// Copied from org.eclipse.osgi.util.ManifestElement
+
+	/**
+	 * Parses a bundle manifest and puts the header/value pairs into the supplied Map.
+	 * Only the main section of the manifest is parsed (up to the first blank line).  All
+	 * other sections are ignored.  If a header is duplicated then only the last  
+	 * value is stored in the map.
+	 * <p>
+	 * The supplied input stream is consumed by this method and will be closed.
+	 * If the supplied Map is null then a Map is created to put the header/value pairs into.
+	 * </p>
+	 * @param manifest an input stream for a bundle manifest.
+	 * @param headers a map used to put the header/value pairs from the bundle manifest.  This value may be null.
+	 * @throws Exception if the manifest has an invalid syntax
+	 * @throws IOException if an error occurs while reading the manifest
+	 * @return the map with the header/value pairs from the bundle manifest
+	 */
+	public static Map parseBundleManifest(InputStream manifest, Map headers) throws IOException, Exception {
+		if (headers == null)
+			headers = new HashMap();
+		BufferedReader br;
+		try {
+			br = new BufferedReader(new InputStreamReader(manifest, "UTF8")); //$NON-NLS-1$
+		} catch (UnsupportedEncodingException e) {
+			br = new BufferedReader(new InputStreamReader(manifest));
+		}
+		try {
+			String header = null;
+			StringBuffer value = new StringBuffer(256);
+			boolean firstLine = true;
+
+			while (true) {
+				String line = br.readLine();
+				/* The java.util.jar classes in JDK 1.3 use the value of the last
+				 * encountered manifest header. So we do the same to emulate
+				 * this behavior. We no longer throw a BundleException
+				 * for duplicate manifest headers.
+				 */
+
+				if ((line == null) || (line.length() == 0)) /* EOF or empty line */
+				{
+					if (!firstLine) /* flush last line */
+					{
+						headers.put(header, value.toString().trim());
+					}
+					break; /* done processing main attributes */
+				}
+
+				if (line.charAt(0) == ' ') /* continuation */
+				{
+					if (firstLine) /* if no previous line */
+					{
+						throw new Exception("The manifest line has an invalid leading space");
+					}
+					value.append(line.substring(1));
+					continue;
+				}
+
+				if (!firstLine) {
+					headers.put(header, value.toString().trim());
+					value.setLength(0); /* clear StringBuffer */
+				}
+
+				int colon = line.indexOf(':');
+				if (colon == -1) /* no colon */
+				{
+					throw new Exception("The manifest line is invalid; it has no colon '':'' character after the header key");
+				}
+				header = line.substring(0, colon).trim();
+				value.append(line.substring(colon + 1));
+				firstLine = false;
+			}
+		} finally {
+			try {
+				br.close();
+			} catch (IOException ee) {
+				// do nothing
+			}
+		}
+		return headers;
+	}
+}
diff --git a/eclipse/plugins/org.eclipse.equinox.simpleconfigurator/src/org/eclipse/equinox/internal/simpleconfigurator/utils/SimpleConfiguratorUtils.java b/eclipse/plugins/org.eclipse.equinox.simpleconfigurator/src/org/eclipse/equinox/internal/simpleconfigurator/utils/SimpleConfiguratorUtils.java
index aa0235a..54060aa 100644
--- a/eclipse/plugins/org.eclipse.equinox.simpleconfigurator/src/org/eclipse/equinox/internal/simpleconfigurator/utils/SimpleConfiguratorUtils.java
+++ b/eclipse/plugins/org.eclipse.equinox.simpleconfigurator/src/org/eclipse/equinox/internal/simpleconfigurator/utils/SimpleConfiguratorUtils.java
@@ -11,6 +11,8 @@ package org.eclipse.equinox.internal.simpleconfigurator.utils;
 import java.io.*;
 import java.net.*;
 import java.util.*;
+import org.debian.BundleInfoHelper;
+import org.eclipse.osgi.service.resolver.VersionRange;
 import org.osgi.framework.Version;
 
 public class SimpleConfiguratorUtils {
@@ -38,7 +40,12 @@ public class SimpleConfiguratorUtils {
 		}
 
 		try {
-			return readConfiguration(stream, base);
+			List result = readConfiguration(stream, base);
+
+			if (url.toString().startsWith("file:/usr/lib/eclipse/configuration"))
+				result = BundleInfoHelper.useDebianBundleVersions(result);
+
+			return result;
 		} finally {
 			stream.close();
 		}